1 /* 2 * Copyright (C) 2017 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 18 package com.android.server.companion; 19 20 import static android.Manifest.permission.MANAGE_COMPANION_DEVICES; 21 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; 22 import static android.content.pm.PackageManager.CERT_INPUT_SHA256; 23 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 24 import static android.os.Process.SYSTEM_UID; 25 import static android.os.UserHandle.getCallingUserId; 26 27 import static com.android.internal.util.CollectionUtils.any; 28 import static com.android.internal.util.Preconditions.checkState; 29 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; 30 import static com.android.server.companion.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED; 31 import static com.android.server.companion.MetricUtils.logCreateAssociation; 32 import static com.android.server.companion.MetricUtils.logRemoveAssociation; 33 import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature; 34 import static com.android.server.companion.PackageUtils.getPackageInfo; 35 import static com.android.server.companion.PermissionsUtils.checkCallerCanManageCompanionDevice; 36 import static com.android.server.companion.PermissionsUtils.enforceCallerCanManageAssociationsForPackage; 37 import static com.android.server.companion.PermissionsUtils.enforceCallerCanManageCompanionDevice; 38 import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOr; 39 import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId; 40 import static com.android.server.companion.PermissionsUtils.sanitizeWithCallerChecks; 41 import static com.android.server.companion.RolesUtils.addRoleHolderForAssociation; 42 import static com.android.server.companion.RolesUtils.removeRoleHolderForAssociation; 43 44 import static java.util.Objects.requireNonNull; 45 import static java.util.concurrent.TimeUnit.DAYS; 46 import static java.util.concurrent.TimeUnit.MINUTES; 47 48 import android.annotation.NonNull; 49 import android.annotation.Nullable; 50 import android.annotation.SuppressLint; 51 import android.annotation.UserIdInt; 52 import android.app.ActivityManager; 53 import android.app.ActivityManager.RunningAppProcessInfo; 54 import android.app.ActivityManagerInternal; 55 import android.app.AppOpsManager; 56 import android.app.NotificationManager; 57 import android.app.PendingIntent; 58 import android.companion.AssociationInfo; 59 import android.companion.AssociationRequest; 60 import android.companion.DeviceNotAssociatedException; 61 import android.companion.IAssociationRequestCallback; 62 import android.companion.ICompanionDeviceManager; 63 import android.companion.IOnAssociationsChangedListener; 64 import android.content.ComponentName; 65 import android.content.Context; 66 import android.content.SharedPreferences; 67 import android.content.pm.PackageInfo; 68 import android.content.pm.PackageManager; 69 import android.content.pm.PackageManagerInternal; 70 import android.content.pm.UserInfo; 71 import android.net.MacAddress; 72 import android.net.NetworkPolicyManager; 73 import android.os.Binder; 74 import android.os.Environment; 75 import android.os.Handler; 76 import android.os.Message; 77 import android.os.Parcel; 78 import android.os.PowerWhitelistManager; 79 import android.os.RemoteCallbackList; 80 import android.os.RemoteException; 81 import android.os.ResultReceiver; 82 import android.os.ServiceManager; 83 import android.os.ShellCallback; 84 import android.os.SystemProperties; 85 import android.os.UserHandle; 86 import android.os.UserManager; 87 import android.util.ArraySet; 88 import android.util.ExceptionUtils; 89 import android.util.Log; 90 import android.util.Slog; 91 import android.util.SparseArray; 92 import android.util.SparseBooleanArray; 93 94 import com.android.internal.annotations.GuardedBy; 95 import com.android.internal.app.IAppOpsService; 96 import com.android.internal.content.PackageMonitor; 97 import com.android.internal.infra.PerUser; 98 import com.android.internal.notification.NotificationAccessConfirmationActivityContract; 99 import com.android.internal.os.BackgroundThread; 100 import com.android.internal.util.ArrayUtils; 101 import com.android.internal.util.DumpUtils; 102 import com.android.server.FgThread; 103 import com.android.server.LocalServices; 104 import com.android.server.SystemService; 105 import com.android.server.companion.presence.CompanionDevicePresenceMonitor; 106 import com.android.server.pm.UserManagerInternal; 107 import com.android.server.wm.ActivityTaskManagerInternal; 108 109 import java.io.File; 110 import java.io.FileDescriptor; 111 import java.io.PrintWriter; 112 import java.util.ArrayList; 113 import java.util.Collections; 114 import java.util.HashMap; 115 import java.util.HashSet; 116 import java.util.List; 117 import java.util.Map; 118 import java.util.Set; 119 120 @SuppressLint("LongLogTag") 121 public class CompanionDeviceManagerService extends SystemService { 122 static final String TAG = "CompanionDeviceManagerService"; 123 static final boolean DEBUG = false; 124 125 /** Range of Association IDs allocated for a user.*/ 126 private static final int ASSOCIATIONS_IDS_PER_USER_RANGE = 100000; 127 private static final long PAIR_WITHOUT_PROMPT_WINDOW_MS = 10 * 60 * 1000; // 10 min 128 129 private static final String PREF_FILE_NAME = "companion_device_preferences.xml"; 130 private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done"; 131 private static final String SYS_PROP_DEBUG_REMOVAL_TIME_WINDOW = 132 "debug.cdm.cdmservice.removal_time_window"; 133 134 private static final long ASSOCIATION_REMOVAL_TIME_WINDOW_DEFAULT = DAYS.toMillis(90); 135 136 private final ActivityManager mActivityManager; 137 private final OnPackageVisibilityChangeListener mOnPackageVisibilityChangeListener; 138 139 private PersistentDataStore mPersistentStore; 140 private final PersistUserStateHandler mUserPersistenceHandler; 141 142 private final AssociationStoreImpl mAssociationStore; 143 private AssociationRequestsProcessor mAssociationRequestsProcessor; 144 private CompanionDevicePresenceMonitor mDevicePresenceMonitor; 145 private CompanionApplicationController mCompanionAppController; 146 147 private final ActivityTaskManagerInternal mAtmInternal; 148 private final ActivityManagerInternal mAmInternal; 149 private final IAppOpsService mAppOpsManager; 150 private final PowerWhitelistManager mPowerWhitelistManager; 151 private final UserManager mUserManager; 152 final PackageManagerInternal mPackageManagerInternal; 153 154 /** 155 * A structure that consists of two nested maps, and effectively maps (userId + packageName) to 156 * a list of IDs that have been previously assigned to associations for that package. 157 * We maintain this structure so that we never re-use association IDs for the same package 158 * (until it's uninstalled). 159 */ 160 @GuardedBy("mPreviouslyUsedIds") 161 private final SparseArray<Map<String, Set<Integer>>> mPreviouslyUsedIds = new SparseArray<>(); 162 163 /** 164 * A structure that consists of a set of revoked associations that pending for role holder 165 * removal per each user. 166 * 167 * @see #maybeRemoveRoleHolderForAssociation(AssociationInfo) 168 * @see #addToPendingRoleHolderRemoval(AssociationInfo) 169 * @see #removeFromPendingRoleHolderRemoval(AssociationInfo) 170 * @see #getPendingRoleHolderRemovalAssociationsForUser(int) 171 */ 172 @GuardedBy("mRevokedAssociationsPendingRoleHolderRemoval") 173 private final PerUserAssociationSet mRevokedAssociationsPendingRoleHolderRemoval = 174 new PerUserAssociationSet(); 175 /** 176 * Contains uid-s of packages pending to be removed from the role holder list (after 177 * revocation of an association), which will happen one the package is no longer visible to the 178 * user. 179 * For quicker uid -> (userId, packageName) look-up this is not a {@code Set<Integer>} but 180 * a {@code Map<Integer, String>} which maps uid-s to packageName-s (userId-s can be derived 181 * from uid-s using {@link UserHandle#getUserId(int)}). 182 * 183 * @see #maybeRemoveRoleHolderForAssociation(AssociationInfo) 184 * @see #addToPendingRoleHolderRemoval(AssociationInfo) 185 * @see #removeFromPendingRoleHolderRemoval(AssociationInfo) 186 */ 187 @GuardedBy("mRevokedAssociationsPendingRoleHolderRemoval") 188 private final Map<Integer, String> mUidsPendingRoleHolderRemoval = new HashMap<>(); 189 190 private final RemoteCallbackList<IOnAssociationsChangedListener> mListeners = 191 new RemoteCallbackList<>(); 192 CompanionDeviceManagerService(Context context)193 public CompanionDeviceManagerService(Context context) { 194 super(context); 195 196 mActivityManager = context.getSystemService(ActivityManager.class); 197 mPowerWhitelistManager = context.getSystemService(PowerWhitelistManager.class); 198 mAppOpsManager = IAppOpsService.Stub.asInterface( 199 ServiceManager.getService(Context.APP_OPS_SERVICE)); 200 mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); 201 mAmInternal = LocalServices.getService(ActivityManagerInternal.class); 202 mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); 203 mUserManager = context.getSystemService(UserManager.class); 204 205 mUserPersistenceHandler = new PersistUserStateHandler(); 206 mAssociationStore = new AssociationStoreImpl(); 207 208 mOnPackageVisibilityChangeListener = 209 new OnPackageVisibilityChangeListener(mActivityManager); 210 } 211 212 @Override onStart()213 public void onStart() { 214 mPersistentStore = new PersistentDataStore(); 215 216 loadAssociationsFromDisk(); 217 mAssociationStore.registerListener(mAssociationStoreChangeListener); 218 219 mDevicePresenceMonitor = new CompanionDevicePresenceMonitor( 220 mAssociationStore, mDevicePresenceCallback); 221 222 mAssociationRequestsProcessor = new AssociationRequestsProcessor( 223 /* cdmService */this, mAssociationStore); 224 225 final Context context = getContext(); 226 mCompanionAppController = new CompanionApplicationController( 227 context, mApplicationControllerCallback); 228 229 // Publish "binder" service. 230 final CompanionDeviceManagerImpl impl = new CompanionDeviceManagerImpl(); 231 publishBinderService(Context.COMPANION_DEVICE_SERVICE, impl); 232 233 // Publish "local" service. 234 LocalServices.addService(CompanionDeviceManagerServiceInternal.class, new LocalService()); 235 } 236 loadAssociationsFromDisk()237 void loadAssociationsFromDisk() { 238 final Set<AssociationInfo> allAssociations = new ArraySet<>(); 239 synchronized (mPreviouslyUsedIds) { 240 // The data is stored in DE directories, so we can read the data for all users now 241 // (which would not be possible if the data was stored to CE directories). 242 mPersistentStore.readStateForUsers( 243 mUserManager.getAliveUsers(), allAssociations, mPreviouslyUsedIds); 244 } 245 246 final Set<AssociationInfo> activeAssociations = 247 new ArraySet<>(/* capacity */ allAssociations.size()); 248 // A set contains the userIds that need to persist state after remove the app 249 // from the list of role holders. 250 final Set<Integer> usersToPersistStateFor = new ArraySet<>(); 251 252 for (AssociationInfo association : allAssociations) { 253 if (!association.isRevoked()) { 254 activeAssociations.add(association); 255 } else if (maybeRemoveRoleHolderForAssociation(association)) { 256 // Nothing more to do here, but we'll need to persist all the associations to the 257 // disk afterwards. 258 usersToPersistStateFor.add(association.getUserId()); 259 } else { 260 addToPendingRoleHolderRemoval(association); 261 } 262 } 263 264 mAssociationStore.setAssociations(activeAssociations); 265 266 // IMPORTANT: only do this AFTER mAssociationStore.setAssociations(), because 267 // persistStateForUser() queries AssociationStore. 268 // (If persistStateForUser() is invoked before mAssociationStore.setAssociations() it 269 // would effectively just clear-out all the persisted associations). 270 for (int userId : usersToPersistStateFor) { 271 persistStateForUser(userId); 272 } 273 } 274 275 @Override onBootPhase(int phase)276 public void onBootPhase(int phase) { 277 final Context context = getContext(); 278 if (phase == PHASE_SYSTEM_SERVICES_READY) { 279 // WARNING: moving PackageMonitor to another thread (Looper) may introduce significant 280 // delays (even in case of the Main Thread). It may be fine overall, but would require 281 // updating the tests (adding a delay there). 282 mPackageMonitor.register(context, FgThread.get().getLooper(), UserHandle.ALL, true); 283 mDevicePresenceMonitor.init(context); 284 } else if (phase == PHASE_BOOT_COMPLETED) { 285 // Run the Inactive Association Removal job service daily. 286 InactiveAssociationsRemovalService.schedule(getContext()); 287 } 288 } 289 290 @Override onUserUnlocking(@onNull TargetUser user)291 public void onUserUnlocking(@NonNull TargetUser user) { 292 final int userId = user.getUserIdentifier(); 293 final List<AssociationInfo> associations = mAssociationStore.getAssociationsForUser(userId); 294 295 if (associations.isEmpty()) return; 296 297 updateAtm(userId, associations); 298 299 BackgroundThread.getHandler().sendMessageDelayed( 300 obtainMessage(CompanionDeviceManagerService::maybeGrantAutoRevokeExemptions, this), 301 MINUTES.toMillis(10)); 302 } 303 304 @Nullable getAssociationWithCallerChecks( @serIdInt int userId, @NonNull String packageName, @NonNull String macAddress)305 AssociationInfo getAssociationWithCallerChecks( 306 @UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress) { 307 final AssociationInfo association = mAssociationStore.getAssociationsForPackageWithAddress( 308 userId, packageName, macAddress); 309 return sanitizeWithCallerChecks(getContext(), association); 310 } 311 312 @Nullable getAssociationWithCallerChecks(int associationId)313 AssociationInfo getAssociationWithCallerChecks(int associationId) { 314 final AssociationInfo association = mAssociationStore.getAssociationById(associationId); 315 return sanitizeWithCallerChecks(getContext(), association); 316 } 317 onDeviceAppearedInternal(int associationId)318 private void onDeviceAppearedInternal(int associationId) { 319 if (DEBUG) Log.i(TAG, "onDevice_Appeared_Internal() id=" + associationId); 320 321 final AssociationInfo association = mAssociationStore.getAssociationById(associationId); 322 if (DEBUG) Log.d(TAG, " association=" + associationId); 323 324 if (!association.shouldBindWhenPresent()) return; 325 326 final int userId = association.getUserId(); 327 final String packageName = association.getPackageName(); 328 // Set bindImportant to true when the association is self-managed to avoid the target 329 // service being killed. 330 final boolean bindImportant = association.isSelfManaged(); 331 332 if (!mCompanionAppController.isCompanionApplicationBound(userId, packageName)) { 333 mCompanionAppController.bindCompanionApplication(userId, packageName, bindImportant); 334 } else if (DEBUG) { 335 Log.i(TAG, "u" + userId + "\\" + packageName + " is already bound"); 336 } 337 mCompanionAppController.notifyCompanionApplicationDeviceAppeared(association); 338 } 339 onDeviceDisappearedInternal(int associationId)340 private void onDeviceDisappearedInternal(int associationId) { 341 if (DEBUG) Log.i(TAG, "onDevice_Disappeared_Internal() id=" + associationId); 342 343 final AssociationInfo association = mAssociationStore.getAssociationById(associationId); 344 if (DEBUG) Log.d(TAG, " association=" + associationId); 345 346 final int userId = association.getUserId(); 347 final String packageName = association.getPackageName(); 348 349 if (!mCompanionAppController.isCompanionApplicationBound(userId, packageName)) { 350 if (DEBUG) Log.w(TAG, "u" + userId + "\\" + packageName + " is NOT bound"); 351 return; 352 } 353 354 if (association.shouldBindWhenPresent()) { 355 mCompanionAppController.notifyCompanionApplicationDeviceDisappeared(association); 356 } 357 358 // Check if there are other devices associated to the app that are present. 359 if (shouldBindPackage(userId, packageName)) return; 360 361 mCompanionAppController.unbindCompanionApplication(userId, packageName); 362 } 363 onCompanionApplicationBindingDiedInternal( @serIdInt int userId, @NonNull String packageName)364 private boolean onCompanionApplicationBindingDiedInternal( 365 @UserIdInt int userId, @NonNull String packageName) { 366 for (AssociationInfo ai : 367 mAssociationStore.getAssociationsForPackage(userId, packageName)) { 368 final int associationId = ai.getId(); 369 if (ai.isSelfManaged() 370 && mDevicePresenceMonitor.isDevicePresent(associationId)) { 371 mDevicePresenceMonitor.onSelfManagedDeviceReporterBinderDied(associationId); 372 } 373 } 374 // TODO(b/218613015): implement. 375 return false; 376 } 377 onRebindCompanionApplicationTimeoutInternal( @serIdInt int userId, @NonNull String packageName)378 private void onRebindCompanionApplicationTimeoutInternal( 379 @UserIdInt int userId, @NonNull String packageName) { 380 // TODO(b/218613015): implement. 381 } 382 383 /** 384 * @return whether the package should be bound (i.e. at least one of the devices associated with 385 * the package is currently present). 386 */ shouldBindPackage(@serIdInt int userId, @NonNull String packageName)387 private boolean shouldBindPackage(@UserIdInt int userId, @NonNull String packageName) { 388 final List<AssociationInfo> packageAssociations = 389 mAssociationStore.getAssociationsForPackage(userId, packageName); 390 for (AssociationInfo association : packageAssociations) { 391 if (!association.shouldBindWhenPresent()) continue; 392 if (mDevicePresenceMonitor.isDevicePresent(association.getId())) return true; 393 } 394 return false; 395 } 396 onAssociationChangedInternal( @ssociationStore.ChangeType int changeType, AssociationInfo association)397 private void onAssociationChangedInternal( 398 @AssociationStore.ChangeType int changeType, AssociationInfo association) { 399 final int id = association.getId(); 400 final int userId = association.getUserId(); 401 final String packageName = association.getPackageName(); 402 403 if (changeType == AssociationStore.CHANGE_TYPE_REMOVED) { 404 markIdAsPreviouslyUsedForPackage(id, userId, packageName); 405 } 406 407 final List<AssociationInfo> updatedAssociations = 408 mAssociationStore.getAssociationsForUser(userId); 409 410 mUserPersistenceHandler.postPersistUserState(userId); 411 412 // Notify listeners if ADDED, REMOVED or UPDATED_ADDRESS_CHANGED. 413 // Do NOT notify when UPDATED_ADDRESS_UNCHANGED, which means a minor tweak in association's 414 // configs, which "listeners" won't (and shouldn't) be able to see. 415 if (changeType != CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED) { 416 notifyListeners(userId, updatedAssociations); 417 } 418 updateAtm(userId, updatedAssociations); 419 } 420 persistStateForUser(@serIdInt int userId)421 private void persistStateForUser(@UserIdInt int userId) { 422 // We want to store both active associations and the revoked (removed) association that we 423 // are keeping around for the final clean-up (delayed role holder removal). 424 final List<AssociationInfo> allAssociations; 425 // Start with the active associations - these we can get from the AssociationStore. 426 allAssociations = new ArrayList<>( 427 mAssociationStore.getAssociationsForUser(userId)); 428 // ... and add the revoked (removed) association, that are yet to be permanently removed. 429 allAssociations.addAll(getPendingRoleHolderRemovalAssociationsForUser(userId)); 430 431 final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUser(userId); 432 433 mPersistentStore.persistStateForUser(userId, allAssociations, usedIdsForUser); 434 } 435 notifyListeners( @serIdInt int userId, @NonNull List<AssociationInfo> associations)436 private void notifyListeners( 437 @UserIdInt int userId, @NonNull List<AssociationInfo> associations) { 438 mListeners.broadcast((listener, callbackUserId) -> { 439 if ((int) callbackUserId == userId) { 440 try { 441 listener.onAssociationsChanged(associations); 442 } catch (RemoteException ignored) { 443 } 444 } 445 }); 446 } 447 markIdAsPreviouslyUsedForPackage( int associationId, @UserIdInt int userId, @NonNull String packageName)448 private void markIdAsPreviouslyUsedForPackage( 449 int associationId, @UserIdInt int userId, @NonNull String packageName) { 450 synchronized (mPreviouslyUsedIds) { 451 Map<String, Set<Integer>> usedIdsForUser = mPreviouslyUsedIds.get(userId); 452 if (usedIdsForUser == null) { 453 usedIdsForUser = new HashMap<>(); 454 mPreviouslyUsedIds.put(userId, usedIdsForUser); 455 } 456 457 final Set<Integer> usedIdsForPackage = 458 usedIdsForUser.computeIfAbsent(packageName, it -> new HashSet<>()); 459 usedIdsForPackage.add(associationId); 460 } 461 } 462 onPackageRemoveOrDataClearedInternal( @serIdInt int userId, @NonNull String packageName)463 private void onPackageRemoveOrDataClearedInternal( 464 @UserIdInt int userId, @NonNull String packageName) { 465 if (DEBUG) { 466 Log.i(TAG, "onPackageRemove_Or_DataCleared() u" + userId + "/" 467 + packageName); 468 } 469 470 // Clear associations. 471 final List<AssociationInfo> associationsForPackage = 472 mAssociationStore.getAssociationsForPackage(userId, packageName); 473 for (AssociationInfo association : associationsForPackage) { 474 mAssociationStore.removeAssociation(association.getId()); 475 } 476 477 mCompanionAppController.onPackagesChanged(userId); 478 } 479 onPackageModifiedInternal(@serIdInt int userId, @NonNull String packageName)480 private void onPackageModifiedInternal(@UserIdInt int userId, @NonNull String packageName) { 481 if (DEBUG) Log.i(TAG, "onPackageModified() u" + userId + "/" + packageName); 482 483 final List<AssociationInfo> associationsForPackage = 484 mAssociationStore.getAssociationsForPackage(userId, packageName); 485 for (AssociationInfo association : associationsForPackage) { 486 updateSpecialAccessPermissionForAssociatedPackage(association); 487 } 488 489 mCompanionAppController.onPackagesChanged(userId); 490 } 491 492 // Revoke associations if the selfManaged companion device does not connect for 3 months. removeInactiveSelfManagedAssociations()493 void removeInactiveSelfManagedAssociations() { 494 final long currentTime = System.currentTimeMillis(); 495 long removalWindow = SystemProperties.getLong(SYS_PROP_DEBUG_REMOVAL_TIME_WINDOW, -1); 496 if (removalWindow <= 0) { 497 // 0 or negative values indicate that the sysprop was never set or should be ignored. 498 removalWindow = ASSOCIATION_REMOVAL_TIME_WINDOW_DEFAULT; 499 } 500 501 for (AssociationInfo association : mAssociationStore.getAssociations()) { 502 if (!association.isSelfManaged()) continue; 503 504 final boolean isInactive = 505 currentTime - association.getLastTimeConnectedMs() >= removalWindow; 506 if (!isInactive) continue; 507 508 final int id = association.getId(); 509 510 Slog.i(TAG, "Removing inactive self-managed association id=" + id); 511 disassociateInternal(id); 512 } 513 } 514 515 class CompanionDeviceManagerImpl extends ICompanionDeviceManager.Stub { 516 @Override onTransact(int code, Parcel data, Parcel reply, int flags)517 public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 518 throws RemoteException { 519 try { 520 return super.onTransact(code, data, reply, flags); 521 } catch (Throwable e) { 522 Slog.e(TAG, "Error during IPC", e); 523 throw ExceptionUtils.propagate(e, RemoteException.class); 524 } 525 } 526 527 @Override associate(AssociationRequest request, IAssociationRequestCallback callback, String packageName, int userId)528 public void associate(AssociationRequest request, IAssociationRequestCallback callback, 529 String packageName, int userId) throws RemoteException { 530 Slog.i(TAG, "associate() " 531 + "request=" + request + ", " 532 + "package=u" + userId + "/" + packageName); 533 enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName, 534 "create associations"); 535 536 mAssociationRequestsProcessor.processNewAssociationRequest( 537 request, packageName, userId, callback); 538 } 539 540 @Override getAssociations(String packageName, int userId)541 public List<AssociationInfo> getAssociations(String packageName, int userId) { 542 enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName, 543 "get associations"); 544 545 if (!checkCallerCanManageCompanionDevice(getContext())) { 546 // If the caller neither is system nor holds MANAGE_COMPANION_DEVICES: it needs to 547 // request the feature (also: the caller is the app itself). 548 enforceUsesCompanionDeviceFeature(getContext(), userId, packageName); 549 } 550 551 return mAssociationStore.getAssociationsForPackage(userId, packageName); 552 } 553 554 @Override getAllAssociationsForUser(int userId)555 public List<AssociationInfo> getAllAssociationsForUser(int userId) throws RemoteException { 556 enforceCallerIsSystemOrCanInteractWithUserId(getContext(), userId); 557 enforceCallerCanManageCompanionDevice(getContext(), "getAllAssociationsForUser"); 558 559 return mAssociationStore.getAssociationsForUser(userId); 560 } 561 562 @Override addOnAssociationsChangedListener(IOnAssociationsChangedListener listener, int userId)563 public void addOnAssociationsChangedListener(IOnAssociationsChangedListener listener, 564 int userId) { 565 enforceCallerIsSystemOrCanInteractWithUserId(getContext(), userId); 566 enforceCallerCanManageCompanionDevice(getContext(), 567 "addOnAssociationsChangedListener"); 568 569 mListeners.register(listener, userId); 570 } 571 572 @Override removeOnAssociationsChangedListener(IOnAssociationsChangedListener listener, int userId)573 public void removeOnAssociationsChangedListener(IOnAssociationsChangedListener listener, 574 int userId) { 575 enforceCallerIsSystemOrCanInteractWithUserId(getContext(), userId); 576 enforceCallerCanManageCompanionDevice( 577 getContext(), "removeOnAssociationsChangedListener"); 578 579 mListeners.unregister(listener); 580 } 581 582 @Override legacyDisassociate(String deviceMacAddress, String packageName, int userId)583 public void legacyDisassociate(String deviceMacAddress, String packageName, int userId) { 584 if (DEBUG) { 585 Log.i(TAG, "legacyDisassociate() pkg=u" + userId + "/" + packageName 586 + ", macAddress=" + deviceMacAddress); 587 } 588 589 requireNonNull(deviceMacAddress); 590 requireNonNull(packageName); 591 592 final AssociationInfo association = 593 getAssociationWithCallerChecks(userId, packageName, deviceMacAddress); 594 if (association == null) { 595 throw new IllegalArgumentException("Association does not exist " 596 + "or the caller does not have permissions to manage it " 597 + "(ie. it belongs to a different package or a different user)."); 598 } 599 600 disassociateInternal(association.getId()); 601 } 602 603 @Override disassociate(int associationId)604 public void disassociate(int associationId) { 605 if (DEBUG) Log.i(TAG, "disassociate() associationId=" + associationId); 606 607 final AssociationInfo association = getAssociationWithCallerChecks(associationId); 608 if (association == null) { 609 throw new IllegalArgumentException("Association with ID " + associationId + " " 610 + "does not exist " 611 + "or belongs to a different package " 612 + "or belongs to a different user"); 613 } 614 615 disassociateInternal(associationId); 616 } 617 618 @Override requestNotificationAccess(ComponentName component, int userId)619 public PendingIntent requestNotificationAccess(ComponentName component, int userId) 620 throws RemoteException { 621 String callingPackage = component.getPackageName(); 622 checkCanCallNotificationApi(callingPackage); 623 // TODO: check userId. 624 final long identity = Binder.clearCallingIdentity(); 625 try { 626 return PendingIntent.getActivityAsUser(getContext(), 627 0 /* request code */, 628 NotificationAccessConfirmationActivityContract.launcherIntent( 629 getContext(), userId, component), 630 PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT 631 | PendingIntent.FLAG_CANCEL_CURRENT, 632 null /* options */, 633 new UserHandle(userId)); 634 } finally { 635 Binder.restoreCallingIdentity(identity); 636 } 637 } 638 639 /** 640 * @deprecated Use 641 * {@link NotificationManager#isNotificationListenerAccessGranted(ComponentName)} instead. 642 */ 643 @Deprecated 644 @Override hasNotificationAccess(ComponentName component)645 public boolean hasNotificationAccess(ComponentName component) throws RemoteException { 646 checkCanCallNotificationApi(component.getPackageName()); 647 NotificationManager nm = getContext().getSystemService(NotificationManager.class); 648 return nm.isNotificationListenerAccessGranted(component); 649 } 650 651 @Override isDeviceAssociatedForWifiConnection(String packageName, String macAddress, int userId)652 public boolean isDeviceAssociatedForWifiConnection(String packageName, String macAddress, 653 int userId) { 654 getContext().enforceCallingOrSelfPermission( 655 MANAGE_COMPANION_DEVICES, "isDeviceAssociated"); 656 657 boolean bypassMacPermission = getContext().getPackageManager().checkPermission( 658 android.Manifest.permission.COMPANION_APPROVE_WIFI_CONNECTIONS, packageName) 659 == PERMISSION_GRANTED; 660 if (bypassMacPermission) { 661 return true; 662 } 663 664 return any(mAssociationStore.getAssociationsForPackage(userId, packageName), 665 a -> a.isLinkedTo(macAddress)); 666 } 667 668 @Override registerDevicePresenceListenerService(String deviceAddress, String callingPackage, int userId)669 public void registerDevicePresenceListenerService(String deviceAddress, 670 String callingPackage, int userId) throws RemoteException { 671 // TODO: take the userId into account. 672 registerDevicePresenceListenerActive(callingPackage, deviceAddress, true); 673 } 674 675 @Override unregisterDevicePresenceListenerService(String deviceAddress, String callingPackage, int userId)676 public void unregisterDevicePresenceListenerService(String deviceAddress, 677 String callingPackage, int userId) throws RemoteException { 678 // TODO: take the userId into account. 679 registerDevicePresenceListenerActive(callingPackage, deviceAddress, false); 680 } 681 682 @Override dispatchMessage(int messageId, int associationId, byte[] message)683 public void dispatchMessage(int messageId, int associationId, byte[] message) 684 throws RemoteException { 685 // TODO(b/199427116): implement. 686 } 687 688 @Override notifyDeviceAppeared(int associationId)689 public void notifyDeviceAppeared(int associationId) { 690 if (DEBUG) Log.i(TAG, "notifyDevice_Appeared() id=" + associationId); 691 692 AssociationInfo association = getAssociationWithCallerChecks(associationId); 693 if (association == null) { 694 throw new IllegalArgumentException("Association with ID " + associationId + " " 695 + "does not exist " 696 + "or belongs to a different package " 697 + "or belongs to a different user"); 698 } 699 700 if (!association.isSelfManaged()) { 701 throw new IllegalArgumentException("Association with ID " + associationId 702 + " is not self-managed. notifyDeviceAppeared(int) can only be called for" 703 + " self-managed associations."); 704 } 705 // AssociationInfo class is immutable: create a new AssociationInfo object with updated 706 // timestamp. 707 association = AssociationInfo.builder(association) 708 .setLastTimeConnected(System.currentTimeMillis()) 709 .build(); 710 mAssociationStore.updateAssociation(association); 711 712 mDevicePresenceMonitor.onSelfManagedDeviceConnected(associationId); 713 } 714 715 @Override notifyDeviceDisappeared(int associationId)716 public void notifyDeviceDisappeared(int associationId) { 717 if (DEBUG) Log.i(TAG, "notifyDevice_Disappeared() id=" + associationId); 718 719 final AssociationInfo association = getAssociationWithCallerChecks(associationId); 720 if (association == null) { 721 throw new IllegalArgumentException("Association with ID " + associationId + " " 722 + "does not exist " 723 + "or belongs to a different package " 724 + "or belongs to a different user"); 725 } 726 727 if (!association.isSelfManaged()) { 728 throw new IllegalArgumentException("Association with ID " + associationId 729 + " is not self-managed. notifyDeviceAppeared(int) can only be called for" 730 + " self-managed associations."); 731 } 732 733 mDevicePresenceMonitor.onSelfManagedDeviceDisconnected(associationId); 734 } 735 registerDevicePresenceListenerActive(String packageName, String deviceAddress, boolean active)736 private void registerDevicePresenceListenerActive(String packageName, String deviceAddress, 737 boolean active) throws RemoteException { 738 if (DEBUG) { 739 Log.i(TAG, "registerDevicePresenceListenerActive()" 740 + " active=" + active 741 + " deviceAddress=" + deviceAddress); 742 } 743 744 getContext().enforceCallingOrSelfPermission( 745 android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE, 746 "[un]registerDevicePresenceListenerService"); 747 final int userId = getCallingUserId(); 748 enforceCallerIsSystemOr(userId, packageName); 749 750 AssociationInfo association = mAssociationStore.getAssociationsForPackageWithAddress( 751 userId, packageName, deviceAddress); 752 753 if (association == null) { 754 throw new RemoteException(new DeviceNotAssociatedException("App " + packageName 755 + " is not associated with device " + deviceAddress 756 + " for user " + userId)); 757 } 758 759 // If already at specified state, then no-op. 760 if (active == association.isNotifyOnDeviceNearby()) { 761 if (DEBUG) Log.d(TAG, "Device presence listener is already at desired state."); 762 return; 763 } 764 765 // AssociationInfo class is immutable: create a new AssociationInfo object with updated 766 // flag. 767 association = AssociationInfo.builder(association) 768 .setNotifyOnDeviceNearby(active) 769 .build(); 770 // Do not need to call {@link BleCompanionDeviceScanner#restartScan()} since it will 771 // trigger {@link BleCompanionDeviceScanner#restartScan(int, AssociationInfo)} when 772 // an application sets/unsets the mNotifyOnDeviceNearby flag. 773 mAssociationStore.updateAssociation(association); 774 775 // If device is already present, then trigger callback. 776 if (active && mDevicePresenceMonitor.isDevicePresent(association.getId())) { 777 if (DEBUG) Log.d(TAG, "Device is already present. Triggering callback."); 778 onDeviceAppearedInternal(association.getId()); 779 } 780 781 // If last listener is unregistered, then unbind application. 782 if (!active && !shouldBindPackage(userId, packageName)) { 783 if (DEBUG) Log.d(TAG, "Last listener unregistered. Unbinding application."); 784 mCompanionAppController.unbindCompanionApplication(userId, packageName); 785 } 786 } 787 788 @Override createAssociation(String packageName, String macAddress, int userId, byte[] certificate)789 public void createAssociation(String packageName, String macAddress, int userId, 790 byte[] certificate) { 791 if (!getContext().getPackageManager().hasSigningCertificate( 792 packageName, certificate, CERT_INPUT_SHA256)) { 793 Slog.e(TAG, "Given certificate doesn't match the package certificate."); 794 return; 795 } 796 797 getContext().enforceCallingOrSelfPermission( 798 android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES, "createAssociation"); 799 800 legacyCreateAssociation(userId, macAddress, packageName, null); 801 } 802 checkCanCallNotificationApi(String callingPackage)803 private void checkCanCallNotificationApi(String callingPackage) { 804 final int userId = getCallingUserId(); 805 enforceCallerIsSystemOr(userId, callingPackage); 806 807 if (getCallingUid() == SYSTEM_UID) return; 808 809 enforceUsesCompanionDeviceFeature(getContext(), userId, callingPackage); 810 checkState(!ArrayUtils.isEmpty( 811 mAssociationStore.getAssociationsForPackage(userId, callingPackage)), 812 "App must have an association before calling this API"); 813 } 814 815 @Override canPairWithoutPrompt(String packageName, String macAddress, int userId)816 public boolean canPairWithoutPrompt(String packageName, String macAddress, int userId) { 817 final AssociationInfo association = 818 mAssociationStore.getAssociationsForPackageWithAddress( 819 userId, packageName, macAddress); 820 if (association == null) { 821 return false; 822 } 823 return System.currentTimeMillis() - association.getTimeApprovedMs() 824 < PAIR_WITHOUT_PROMPT_WINDOW_MS; 825 } 826 827 @Override onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)828 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 829 String[] args, ShellCallback callback, ResultReceiver resultReceiver) 830 throws RemoteException { 831 enforceCallerCanManageCompanionDevice(getContext(), "onShellCommand"); 832 833 final CompanionDeviceShellCommand cmd = new CompanionDeviceShellCommand( 834 CompanionDeviceManagerService.this, 835 mAssociationStore, 836 mDevicePresenceMonitor); 837 cmd.exec(this, in, out, err, args, callback, resultReceiver); 838 } 839 840 @Override dump(@onNull FileDescriptor fd, @NonNull PrintWriter out, @Nullable String[] args)841 public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter out, 842 @Nullable String[] args) { 843 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, out)) { 844 return; 845 } 846 847 mAssociationStore.dump(out); 848 mDevicePresenceMonitor.dump(out); 849 mCompanionAppController.dump(out); 850 } 851 } 852 853 /** 854 * @deprecated use 855 * {@link #createAssociation(int, String, MacAddress, CharSequence, String, boolean)} 856 */ 857 @Deprecated legacyCreateAssociation(@serIdInt int userId, @NonNull String deviceMacAddress, @NonNull String packageName, @Nullable String deviceProfile)858 void legacyCreateAssociation(@UserIdInt int userId, @NonNull String deviceMacAddress, 859 @NonNull String packageName, @Nullable String deviceProfile) { 860 final MacAddress macAddress = MacAddress.fromString(deviceMacAddress); 861 createAssociation(userId, packageName, macAddress, null, deviceProfile, false); 862 } 863 createAssociation(@serIdInt int userId, @NonNull String packageName, @Nullable MacAddress macAddress, @Nullable CharSequence displayName, @Nullable String deviceProfile, boolean selfManaged)864 AssociationInfo createAssociation(@UserIdInt int userId, @NonNull String packageName, 865 @Nullable MacAddress macAddress, @Nullable CharSequence displayName, 866 @Nullable String deviceProfile, boolean selfManaged) { 867 final int id = getNewAssociationIdForPackage(userId, packageName); 868 final long timestamp = System.currentTimeMillis(); 869 870 final AssociationInfo association = new AssociationInfo(id, userId, packageName, 871 macAddress, displayName, deviceProfile, selfManaged, 872 /* notifyOnDeviceNearby */ false, /* revoked */ false, timestamp, Long.MAX_VALUE); 873 Slog.i(TAG, "New CDM association created=" + association); 874 mAssociationStore.addAssociation(association); 875 876 // If the "Device Profile" is specified, make the companion application a holder of the 877 // corresponding role. 878 if (deviceProfile != null) { 879 addRoleHolderForAssociation(getContext(), association); 880 } 881 882 updateSpecialAccessPermissionForAssociatedPackage(association); 883 logCreateAssociation(deviceProfile); 884 885 // Don't need to update the mRevokedAssociationsPendingRoleHolderRemoval since 886 // maybeRemoveRoleHolderForAssociation in PackageInactivityListener will handle the case 887 // that there are other devices with the same profile, so the role holder won't be removed. 888 889 return association; 890 } 891 892 @NonNull getPreviouslyUsedIdsForUser(@serIdInt int userId)893 private Map<String, Set<Integer>> getPreviouslyUsedIdsForUser(@UserIdInt int userId) { 894 synchronized (mPreviouslyUsedIds) { 895 return getPreviouslyUsedIdsForUserLocked(userId); 896 } 897 } 898 899 @GuardedBy("mPreviouslyUsedIds") 900 @NonNull getPreviouslyUsedIdsForUserLocked(@serIdInt int userId)901 private Map<String, Set<Integer>> getPreviouslyUsedIdsForUserLocked(@UserIdInt int userId) { 902 final Map<String, Set<Integer>> usedIdsForUser = mPreviouslyUsedIds.get(userId); 903 if (usedIdsForUser == null) { 904 return Collections.emptyMap(); 905 } 906 return deepUnmodifiableCopy(usedIdsForUser); 907 } 908 909 @GuardedBy("mPreviouslyUsedIds") 910 @NonNull getPreviouslyUsedIdsForPackageLocked( @serIdInt int userId, @NonNull String packageName)911 private Set<Integer> getPreviouslyUsedIdsForPackageLocked( 912 @UserIdInt int userId, @NonNull String packageName) { 913 // "Deeply unmodifiable" map: the map itself and the Set<Integer> values it contains are all 914 // unmodifiable. 915 final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUserLocked(userId); 916 final Set<Integer> usedIdsForPackage = usedIdsForUser.get(packageName); 917 918 if (usedIdsForPackage == null) { 919 return Collections.emptySet(); 920 } 921 922 //The set is already unmodifiable. 923 return usedIdsForPackage; 924 } 925 getNewAssociationIdForPackage(@serIdInt int userId, @NonNull String packageName)926 private int getNewAssociationIdForPackage(@UserIdInt int userId, @NonNull String packageName) { 927 synchronized (mPreviouslyUsedIds) { 928 // First: collect all IDs currently in use for this user's Associations. 929 final SparseBooleanArray usedIds = new SparseBooleanArray(); 930 931 // We should really only be checking associations for the given user (i.e.: 932 // mAssociationStore.getAssociationsForUser(userId)), BUT in the past we've got in a 933 // state where association IDs were not assigned correctly in regard to 934 // user-to-association-ids-range (e.g. associations with IDs from 1 to 100,000 should 935 // always belong to u0), so let's check all the associations. 936 for (AssociationInfo it : mAssociationStore.getAssociations()) { 937 usedIds.put(it.getId(), true); 938 } 939 940 // Second: collect all IDs that have been previously used for this package (and user). 941 final Set<Integer> previouslyUsedIds = 942 getPreviouslyUsedIdsForPackageLocked(userId, packageName); 943 944 int id = getFirstAssociationIdForUser(userId); 945 final int lastAvailableIdForUser = getLastAssociationIdForUser(userId); 946 947 // Find first ID that isn't used now AND has never been used for the given package. 948 while (usedIds.get(id) || previouslyUsedIds.contains(id)) { 949 // Increment and try again 950 id++; 951 // ... but first check if the ID is valid (within the range allocated to the user). 952 if (id > lastAvailableIdForUser) { 953 throw new RuntimeException("Cannot create a new Association ID for " 954 + packageName + " for user " + userId); 955 } 956 } 957 958 return id; 959 } 960 } 961 962 // TODO: also revoke notification access disassociateInternal(int associationId)963 void disassociateInternal(int associationId) { 964 final AssociationInfo association = mAssociationStore.getAssociationById(associationId); 965 final int userId = association.getUserId(); 966 final String packageName = association.getPackageName(); 967 final String deviceProfile = association.getDeviceProfile(); 968 969 if (!maybeRemoveRoleHolderForAssociation(association)) { 970 // Need to remove the app from list of the role holders, but will have to do it later 971 // (the app is in foreground at the moment). 972 addToPendingRoleHolderRemoval(association); 973 } 974 975 // Need to check if device still present now because CompanionDevicePresenceMonitor will 976 // remove current connected device after mAssociationStore.removeAssociation 977 final boolean wasPresent = mDevicePresenceMonitor.isDevicePresent(associationId); 978 979 // Removing the association. 980 mAssociationStore.removeAssociation(associationId); 981 // Do not need to persistUserState since CompanionDeviceManagerService will get callback 982 // from #onAssociationChanged, and it will handle the persistUserState which including 983 // active and revoked association. 984 logRemoveAssociation(deviceProfile); 985 986 if (!wasPresent || !association.isNotifyOnDeviceNearby()) return; 987 // The device was connected and the app was notified: check if we need to unbind the app 988 // now. 989 final boolean shouldStayBound = any( 990 mAssociationStore.getAssociationsForPackage(userId, packageName), 991 it -> it.isNotifyOnDeviceNearby() 992 && mDevicePresenceMonitor.isDevicePresent(it.getId())); 993 if (shouldStayBound) return; 994 mCompanionAppController.unbindCompanionApplication(userId, packageName); 995 } 996 997 /** 998 * First, checks if the companion application should be removed from the list role holders when 999 * upon association's removal, i.e.: association's profile (matches the role) is not null, 1000 * the application does not have other associations with the same profile, etc. 1001 * 1002 * <p> 1003 * Then, if establishes that the application indeed has to be removed from the list of the role 1004 * holders, checks if it could be done right now - 1005 * {@link android.app.role.RoleManager#removeRoleHolderAsUser(String, String, int, UserHandle, java.util.concurrent.Executor, java.util.function.Consumer) RoleManager#removeRoleHolderAsUser()} 1006 * will kill the application's process, which leads poor user experience if the application was 1007 * in foreground when this happened, to avoid this CDMS delays invoking 1008 * {@code RoleManager.removeRoleHolderAsUser()} until the app is no longer in foreground. 1009 * 1010 * @return {@code true} if the application does NOT need be removed from the list of the role 1011 * holders OR if the application was successfully removed from the list of role holders. 1012 * I.e.: from the role-management perspective the association is done with. 1013 * {@code false} if the application needs to be removed from the list of role the role 1014 * holders, BUT it CDMS would prefer to do it later. 1015 * I.e.: application is in the foreground at the moment, but invoking 1016 * {@code RoleManager.removeRoleHolderAsUser()} will kill the application's process, 1017 * which would lead to the poor UX, hence need to try later. 1018 */ 1019 maybeRemoveRoleHolderForAssociation(@onNull AssociationInfo association)1020 private boolean maybeRemoveRoleHolderForAssociation(@NonNull AssociationInfo association) { 1021 if (DEBUG) Log.d(TAG, "maybeRemoveRoleHolderForAssociation() association=" + association); 1022 1023 final String deviceProfile = association.getDeviceProfile(); 1024 if (deviceProfile == null) { 1025 // No role was granted to for this association, there is nothing else we need to here. 1026 return true; 1027 } 1028 1029 // Check if the applications is associated with another devices with the profile. If so, 1030 // it should remain the role holder. 1031 final int id = association.getId(); 1032 final int userId = association.getUserId(); 1033 final String packageName = association.getPackageName(); 1034 final boolean roleStillInUse = any( 1035 mAssociationStore.getAssociationsForPackage(userId, packageName), 1036 it -> deviceProfile.equals(it.getDeviceProfile()) && id != it.getId()); 1037 if (roleStillInUse) { 1038 // Application should remain a role holder, there is nothing else we need to here. 1039 return true; 1040 } 1041 1042 final int packageProcessImportance = getPackageProcessImportance(userId, packageName); 1043 if (packageProcessImportance <= IMPORTANCE_VISIBLE) { 1044 // Need to remove the app from the list of role holders, but the process is visible to 1045 // the user at the moment, so we'll need to it later: log and return false. 1046 Slog.i(TAG, "Cannot remove role holder for the removed association id=" + id 1047 + " now - process is visible."); 1048 return false; 1049 } 1050 1051 removeRoleHolderForAssociation(getContext(), association); 1052 return true; 1053 } 1054 getPackageProcessImportance(@serIdInt int userId, @NonNull String packageName)1055 private int getPackageProcessImportance(@UserIdInt int userId, @NonNull String packageName) { 1056 return Binder.withCleanCallingIdentity(() -> { 1057 final int uid = 1058 mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId); 1059 return mActivityManager.getUidImportance(uid); 1060 }); 1061 } 1062 1063 /** 1064 * Set revoked flag for active association and add the revoked association and the uid into 1065 * the caches. 1066 * 1067 * @see #mRevokedAssociationsPendingRoleHolderRemoval 1068 * @see #mUidsPendingRoleHolderRemoval 1069 * @see OnPackageVisibilityChangeListener 1070 */ addToPendingRoleHolderRemoval(@onNull AssociationInfo association)1071 private void addToPendingRoleHolderRemoval(@NonNull AssociationInfo association) { 1072 // First: set revoked flag. 1073 association = AssociationInfo.builder(association) 1074 .setRevoked(true) 1075 .build(); 1076 1077 final String packageName = association.getPackageName(); 1078 final int userId = association.getUserId(); 1079 final int uid = mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId); 1080 1081 // Second: add to the set. 1082 synchronized (mRevokedAssociationsPendingRoleHolderRemoval) { 1083 mRevokedAssociationsPendingRoleHolderRemoval.forUser(association.getUserId()) 1084 .add(association); 1085 if (!mUidsPendingRoleHolderRemoval.containsKey(uid)) { 1086 mUidsPendingRoleHolderRemoval.put(uid, packageName); 1087 1088 if (mUidsPendingRoleHolderRemoval.size() == 1) { 1089 // Just added first uid: start the listener 1090 mOnPackageVisibilityChangeListener.startListening(); 1091 } 1092 } 1093 } 1094 } 1095 1096 /** 1097 * Remove the revoked association form the cache and also remove the uid form the map if 1098 * there are other associations with the same package still pending for role holder removal. 1099 * 1100 * @see #mRevokedAssociationsPendingRoleHolderRemoval 1101 * @see #mUidsPendingRoleHolderRemoval 1102 * @see OnPackageVisibilityChangeListener 1103 */ removeFromPendingRoleHolderRemoval(@onNull AssociationInfo association)1104 private void removeFromPendingRoleHolderRemoval(@NonNull AssociationInfo association) { 1105 final String packageName = association.getPackageName(); 1106 final int userId = association.getUserId(); 1107 final int uid = mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId); 1108 1109 synchronized (mRevokedAssociationsPendingRoleHolderRemoval) { 1110 mRevokedAssociationsPendingRoleHolderRemoval.forUser(userId) 1111 .remove(association); 1112 1113 final boolean shouldKeepUidForRemoval = any( 1114 getPendingRoleHolderRemovalAssociationsForUser(userId), 1115 ai -> packageName.equals(ai.getPackageName())); 1116 // Do not remove the uid form the map since other associations with 1117 // the same packageName still pending for role holder removal. 1118 if (!shouldKeepUidForRemoval) { 1119 mUidsPendingRoleHolderRemoval.remove(uid); 1120 } 1121 1122 if (mUidsPendingRoleHolderRemoval.isEmpty()) { 1123 // The set is empty now - can "turn off" the listener. 1124 mOnPackageVisibilityChangeListener.stopListening(); 1125 } 1126 } 1127 } 1128 1129 /** 1130 * @return a copy of the revoked associations set (safeguarding against 1131 * {@code ConcurrentModificationException}-s). 1132 */ getPendingRoleHolderRemovalAssociationsForUser( @serIdInt int userId)1133 private @NonNull Set<AssociationInfo> getPendingRoleHolderRemovalAssociationsForUser( 1134 @UserIdInt int userId) { 1135 synchronized (mRevokedAssociationsPendingRoleHolderRemoval) { 1136 // Return a copy. 1137 return new ArraySet<>(mRevokedAssociationsPendingRoleHolderRemoval.forUser(userId)); 1138 } 1139 } 1140 getPackageNameByUid(int uid)1141 private String getPackageNameByUid(int uid) { 1142 synchronized (mRevokedAssociationsPendingRoleHolderRemoval) { 1143 return mUidsPendingRoleHolderRemoval.get(uid); 1144 } 1145 } 1146 updateSpecialAccessPermissionForAssociatedPackage(AssociationInfo association)1147 private void updateSpecialAccessPermissionForAssociatedPackage(AssociationInfo association) { 1148 final PackageInfo packageInfo = 1149 getPackageInfo(getContext(), association.getUserId(), association.getPackageName()); 1150 1151 Binder.withCleanCallingIdentity(() -> updateSpecialAccessPermissionAsSystem(packageInfo)); 1152 } 1153 updateSpecialAccessPermissionAsSystem(PackageInfo packageInfo)1154 private void updateSpecialAccessPermissionAsSystem(PackageInfo packageInfo) { 1155 if (packageInfo == null) { 1156 return; 1157 } 1158 if (containsEither(packageInfo.requestedPermissions, 1159 android.Manifest.permission.RUN_IN_BACKGROUND, 1160 android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) { 1161 mPowerWhitelistManager.addToWhitelist(packageInfo.packageName); 1162 } else { 1163 try { 1164 mPowerWhitelistManager.removeFromWhitelist(packageInfo.packageName); 1165 } catch (UnsupportedOperationException e) { 1166 Slog.w(TAG, packageInfo.packageName + " can't be removed from power save" 1167 + " whitelist. It might due to the package is whitelisted by the system."); 1168 } 1169 } 1170 1171 NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext()); 1172 if (containsEither(packageInfo.requestedPermissions, 1173 android.Manifest.permission.USE_DATA_IN_BACKGROUND, 1174 android.Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)) { 1175 networkPolicyManager.addUidPolicy( 1176 packageInfo.applicationInfo.uid, 1177 NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND); 1178 } else { 1179 networkPolicyManager.removeUidPolicy( 1180 packageInfo.applicationInfo.uid, 1181 NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND); 1182 } 1183 1184 exemptFromAutoRevoke(packageInfo.packageName, packageInfo.applicationInfo.uid); 1185 } 1186 exemptFromAutoRevoke(String packageName, int uid)1187 private void exemptFromAutoRevoke(String packageName, int uid) { 1188 try { 1189 mAppOpsManager.setMode( 1190 AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, 1191 uid, 1192 packageName, 1193 AppOpsManager.MODE_IGNORED); 1194 } catch (RemoteException e) { 1195 Slog.w(TAG, "Error while granting auto revoke exemption for " + packageName, e); 1196 } 1197 } 1198 updateAtm(int userId, List<AssociationInfo> associations)1199 private void updateAtm(int userId, List<AssociationInfo> associations) { 1200 final Set<Integer> companionAppUids = new ArraySet<>(); 1201 for (AssociationInfo association : associations) { 1202 final int uid = mPackageManagerInternal.getPackageUid(association.getPackageName(), 1203 0, userId); 1204 if (uid >= 0) { 1205 companionAppUids.add(uid); 1206 } 1207 } 1208 if (mAtmInternal != null) { 1209 mAtmInternal.setCompanionAppUids(userId, companionAppUids); 1210 } 1211 if (mAmInternal != null) { 1212 // Make a copy of the set and send it to ActivityManager. 1213 mAmInternal.setCompanionAppUids(userId, new ArraySet<>(companionAppUids)); 1214 } 1215 } 1216 maybeGrantAutoRevokeExemptions()1217 private void maybeGrantAutoRevokeExemptions() { 1218 Slog.d(TAG, "maybeGrantAutoRevokeExemptions()"); 1219 1220 PackageManager pm = getContext().getPackageManager(); 1221 for (int userId : LocalServices.getService(UserManagerInternal.class).getUserIds()) { 1222 SharedPreferences pref = getContext().getSharedPreferences( 1223 new File(Environment.getUserSystemDirectory(userId), PREF_FILE_NAME), 1224 Context.MODE_PRIVATE); 1225 if (pref.getBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, false)) { 1226 continue; 1227 } 1228 1229 try { 1230 final List<AssociationInfo> associations = 1231 mAssociationStore.getAssociationsForUser(userId); 1232 for (AssociationInfo a : associations) { 1233 try { 1234 int uid = pm.getPackageUidAsUser(a.getPackageName(), userId); 1235 exemptFromAutoRevoke(a.getPackageName(), uid); 1236 } catch (PackageManager.NameNotFoundException e) { 1237 Slog.w(TAG, "Unknown companion package: " + a.getPackageName(), e); 1238 } 1239 } 1240 } finally { 1241 pref.edit().putBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, true).apply(); 1242 } 1243 } 1244 } 1245 1246 private final AssociationStore.OnChangeListener mAssociationStoreChangeListener = 1247 new AssociationStore.OnChangeListener() { 1248 @Override 1249 public void onAssociationChanged(int changeType, AssociationInfo association) { 1250 onAssociationChangedInternal(changeType, association); 1251 } 1252 }; 1253 1254 private final CompanionDevicePresenceMonitor.Callback mDevicePresenceCallback = 1255 new CompanionDevicePresenceMonitor.Callback() { 1256 @Override 1257 public void onDeviceAppeared(int associationId) { 1258 onDeviceAppearedInternal(associationId); 1259 } 1260 1261 @Override 1262 public void onDeviceDisappeared(int associationId) { 1263 onDeviceDisappearedInternal(associationId); 1264 } 1265 }; 1266 1267 private final CompanionApplicationController.Callback mApplicationControllerCallback = 1268 new CompanionApplicationController.Callback() { 1269 @Override 1270 public boolean onCompanionApplicationBindingDied(int userId, @NonNull String packageName) { 1271 return onCompanionApplicationBindingDiedInternal(userId, packageName); 1272 } 1273 1274 @Override 1275 public void onRebindCompanionApplicationTimeout(int userId, @NonNull String packageName) { 1276 onRebindCompanionApplicationTimeoutInternal(userId, packageName); 1277 } 1278 }; 1279 1280 private final PackageMonitor mPackageMonitor = new PackageMonitor() { 1281 @Override 1282 public void onPackageRemoved(String packageName, int uid) { 1283 onPackageRemoveOrDataClearedInternal(getChangingUserId(), packageName); 1284 } 1285 1286 @Override 1287 public void onPackageDataCleared(String packageName, int uid) { 1288 onPackageRemoveOrDataClearedInternal(getChangingUserId(), packageName); 1289 } 1290 1291 @Override 1292 public void onPackageModified(String packageName) { 1293 onPackageModifiedInternal(getChangingUserId(), packageName); 1294 } 1295 }; 1296 getFirstAssociationIdForUser(@serIdInt int userId)1297 static int getFirstAssociationIdForUser(@UserIdInt int userId) { 1298 // We want the IDs to start from 1, not 0. 1299 return userId * ASSOCIATIONS_IDS_PER_USER_RANGE + 1; 1300 } 1301 getLastAssociationIdForUser(@serIdInt int userId)1302 static int getLastAssociationIdForUser(@UserIdInt int userId) { 1303 return (userId + 1) * ASSOCIATIONS_IDS_PER_USER_RANGE; 1304 } 1305 deepUnmodifiableCopy(Map<String, Set<Integer>> orig)1306 private static Map<String, Set<Integer>> deepUnmodifiableCopy(Map<String, Set<Integer>> orig) { 1307 final Map<String, Set<Integer>> copy = new HashMap<>(); 1308 1309 for (Map.Entry<String, Set<Integer>> entry : orig.entrySet()) { 1310 final Set<Integer> valueCopy = new HashSet<>(entry.getValue()); 1311 copy.put(entry.getKey(), Collections.unmodifiableSet(valueCopy)); 1312 } 1313 1314 return Collections.unmodifiableMap(copy); 1315 } 1316 containsEither(T[] array, T a, T b)1317 private static <T> boolean containsEither(T[] array, T a, T b) { 1318 return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b); 1319 } 1320 1321 private class LocalService implements CompanionDeviceManagerServiceInternal { 1322 @Override removeInactiveSelfManagedAssociations()1323 public void removeInactiveSelfManagedAssociations() { 1324 CompanionDeviceManagerService.this.removeInactiveSelfManagedAssociations(); 1325 } 1326 } 1327 1328 /** 1329 * This method must only be called from {@link CompanionDeviceShellCommand} for testing 1330 * purposes only! 1331 */ persistState()1332 void persistState() { 1333 mUserPersistenceHandler.clearMessages(); 1334 for (UserInfo user : mUserManager.getAliveUsers()) { 1335 persistStateForUser(user.id); 1336 } 1337 } 1338 1339 /** 1340 * This class is dedicated to handling requests to persist user state. 1341 */ 1342 @SuppressLint("HandlerLeak") 1343 private class PersistUserStateHandler extends Handler { PersistUserStateHandler()1344 PersistUserStateHandler() { 1345 super(BackgroundThread.get().getLooper()); 1346 } 1347 1348 /** 1349 * Persists user state unless there is already an outstanding request for the given user. 1350 */ postPersistUserState(@serIdInt int userId)1351 synchronized void postPersistUserState(@UserIdInt int userId) { 1352 if (!hasMessages(userId)) { 1353 sendMessage(obtainMessage(userId)); 1354 } 1355 } 1356 1357 /** 1358 * Clears *ALL* outstanding persist requests for *ALL* users. 1359 */ clearMessages()1360 synchronized void clearMessages() { 1361 removeCallbacksAndMessages(null); 1362 } 1363 1364 @Override handleMessage(@onNull Message msg)1365 public void handleMessage(@NonNull Message msg) { 1366 final int userId = msg.what; 1367 persistStateForUser(userId); 1368 } 1369 } 1370 1371 /** 1372 * An OnUidImportanceListener class which watches the importance of the packages. 1373 * In this class, we ONLY interested in the importance of the running process is greater than 1374 * {@link RunningAppProcessInfo.IMPORTANCE_VISIBLE} for the uids have been added into the 1375 * {@link mUidsPendingRoleHolderRemoval}. Lastly remove the role holder for the revoked 1376 * associations for the same packages. 1377 * 1378 * @see #maybeRemoveRoleHolderForAssociation(AssociationInfo) 1379 * @see #removeFromPendingRoleHolderRemoval(AssociationInfo) 1380 * @see #getPendingRoleHolderRemovalAssociationsForUser(int) 1381 */ 1382 private class OnPackageVisibilityChangeListener implements 1383 ActivityManager.OnUidImportanceListener { 1384 final @NonNull ActivityManager mAm; 1385 OnPackageVisibilityChangeListener(@onNull ActivityManager am)1386 OnPackageVisibilityChangeListener(@NonNull ActivityManager am) { 1387 this.mAm = am; 1388 } 1389 startListening()1390 void startListening() { 1391 Binder.withCleanCallingIdentity( 1392 () -> mAm.addOnUidImportanceListener( 1393 /* listener */ OnPackageVisibilityChangeListener.this, 1394 RunningAppProcessInfo.IMPORTANCE_VISIBLE)); 1395 } 1396 stopListening()1397 void stopListening() { 1398 Binder.withCleanCallingIdentity( 1399 () -> mAm.removeOnUidImportanceListener( 1400 /* listener */ OnPackageVisibilityChangeListener.this)); 1401 } 1402 1403 @Override onUidImportance(int uid, int importance)1404 public void onUidImportance(int uid, int importance) { 1405 if (importance <= RunningAppProcessInfo.IMPORTANCE_VISIBLE) { 1406 // The lower the importance value the more "important" the process is. 1407 // We are only interested when the process ceases to be visible. 1408 return; 1409 } 1410 1411 final String packageName = getPackageNameByUid(uid); 1412 if (packageName == null) { 1413 // Not interested in this uid. 1414 return; 1415 } 1416 1417 final int userId = UserHandle.getUserId(uid); 1418 1419 boolean needToPersistStateForUser = false; 1420 1421 for (AssociationInfo association : 1422 getPendingRoleHolderRemovalAssociationsForUser(userId)) { 1423 if (!packageName.equals(association.getPackageName())) continue; 1424 1425 if (!maybeRemoveRoleHolderForAssociation(association)) { 1426 // Did not remove the role holder, will have to try again later. 1427 continue; 1428 } 1429 1430 removeFromPendingRoleHolderRemoval(association); 1431 needToPersistStateForUser = true; 1432 } 1433 1434 if (needToPersistStateForUser) { 1435 mUserPersistenceHandler.postPersistUserState(userId); 1436 } 1437 } 1438 } 1439 1440 private static class PerUserAssociationSet extends PerUser<Set<AssociationInfo>> { 1441 @Override create(int userId)1442 protected @NonNull Set<AssociationInfo> create(int userId) { 1443 return new ArraySet<>(); 1444 } 1445 } 1446 } 1447