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