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