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 overrideUser)317 private boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName, 318 boolean overrideUser) { 319 return addRoleHolderInternal(role, packageName, false, overrideUser, false); 320 } 321 322 @WorkerThread addRoleHolderInternal(@onNull Role role, @NonNull String packageName, boolean dontKillApp, boolean overrideUser, boolean added)323 private boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName, 324 boolean dontKillApp, boolean overrideUser, boolean added) { 325 role.grant(packageName, dontKillApp, overrideUser, this); 326 327 String roleName = role.getName(); 328 if (!added) { 329 added = mRoleManager.addRoleHolderFromController(roleName, packageName); 330 } 331 if (!added) { 332 Log.e(LOG_TAG, "Failed to add role holder in RoleManager, package: " + packageName 333 + ", role: " + roleName); 334 } 335 return added; 336 } 337 338 @WorkerThread removeRoleHolderInternal(@onNull Role role, @NonNull String packageName, boolean dontKillApp)339 private boolean removeRoleHolderInternal(@NonNull Role role, @NonNull String packageName, 340 boolean dontKillApp) { 341 ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, this); 342 if (applicationInfo == null) { 343 Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName); 344 } 345 346 if (applicationInfo != null) { 347 role.revoke(packageName, dontKillApp, false, this); 348 } 349 350 String roleName = role.getName(); 351 boolean removed = mRoleManager.removeRoleHolderFromController(roleName, packageName); 352 if (!removed) { 353 Log.e(LOG_TAG, "Failed to remove role holder in RoleManager," + " package: " 354 + packageName + ", role: " + roleName); 355 } 356 return removed; 357 } 358 359 @WorkerThread clearRoleHoldersInternal(@onNull Role role, boolean dontKillApp)360 private boolean clearRoleHoldersInternal(@NonNull Role role, boolean dontKillApp) { 361 String roleName = role.getName(); 362 List<String> packageNames = mRoleManager.getRoleHolders(roleName); 363 boolean cleared = true; 364 365 int packageNamesSize = packageNames.size(); 366 for (int i = 0; i < packageNamesSize; i++) { 367 String packageName = packageNames.get(i); 368 boolean removed = removeRoleHolderInternal(role, packageName, dontKillApp); 369 if (!removed) { 370 cleared = false; 371 } 372 } 373 374 if (!cleared) { 375 Log.e(LOG_TAG, "Failed to clear role holders, role: " + roleName); 376 } 377 return cleared; 378 } 379 380 @WorkerThread addFallbackRoleHolderMaybe(@onNull Role role)381 private boolean addFallbackRoleHolderMaybe(@NonNull Role role) { 382 String roleName = role.getName(); 383 List<String> currentPackageNames = mRoleManager.getRoleHolders(roleName); 384 if (!currentPackageNames.isEmpty()) { 385 return true; 386 } 387 388 String fallbackPackageName = role.getFallbackHolder(this); 389 if (fallbackPackageName == null) { 390 return true; 391 } 392 393 if (!role.isPackageQualified(fallbackPackageName, this)) { 394 Log.e(LOG_TAG, "Fallback role holder package doesn't qualify for the role, package: " 395 + fallbackPackageName + ", role: " + roleName); 396 return false; 397 } 398 399 Log.i(LOG_TAG, "Adding package as fallback role holder, package: " + fallbackPackageName 400 + ", role: " + roleName); 401 // TODO: If we don't override user here, user might end up missing incoming 402 // phone calls or SMS, so we just keep the old behavior. But overriding user 403 // choice about permission without explicit user action is bad, so maybe we 404 // should at least show a notification? 405 return addRoleHolderInternal(role, fallbackPackageName, 406 role.shouldOverrideUserWhenGranting()); 407 } 408 409 @Override onIsApplicationQualifiedForRole(@onNull String roleName, @NonNull String packageName)410 public boolean onIsApplicationQualifiedForRole(@NonNull String roleName, 411 @NonNull String packageName) { 412 // This API has been deprecated and Settings has been using onIsApplicationVisibleForRole() 413 // instead. 414 return false; 415 } 416 417 @Override onIsApplicationVisibleForRole(@onNull String roleName, @NonNull String packageName)418 public boolean onIsApplicationVisibleForRole(@NonNull String roleName, 419 @NonNull String packageName) { 420 Role role = Roles.get(this).get(roleName); 421 if (role == null) { 422 return false; 423 } 424 if (!role.isAvailable(this)) { 425 return false; 426 } 427 if (!role.isPackageQualified(packageName, this)) { 428 return false; 429 } 430 ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, this); 431 if (applicationInfo == null || !role.isApplicationVisibleAsUser(applicationInfo, 432 Process.myUserHandle(), this)) { 433 return false; 434 } 435 return true; 436 } 437 438 @Override onIsRoleVisible(@onNull String roleName)439 public boolean onIsRoleVisible(@NonNull String roleName) { 440 Role role = Roles.get(this).get(roleName); 441 if (role == null) { 442 return false; 443 } 444 if (!role.isAvailable(this)) { 445 return false; 446 } 447 return role.isVisibleAsUser(Process.myUserHandle(), this); 448 } 449 checkFlags(int flags, int allowedFlags)450 private static boolean checkFlags(int flags, int allowedFlags) { 451 if ((flags & allowedFlags) != flags) { 452 Log.e(LOG_TAG, "flags is invalid, flags: 0x" + Integer.toHexString(flags) 453 + ", allowed flags: 0x" + Integer.toHexString(allowedFlags)); 454 return false; 455 } 456 return true; 457 } 458 hasFlag(int flags, int flag)459 private static boolean hasFlag(int flags, int flag) { 460 return (flags & flag) == flag; 461 } 462 } 463