• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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