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