diff options
17 files changed, 353 insertions, 379 deletions
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ba9751fe1d12..52bad21e156d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -8679,7 +8679,7 @@ android:permission="android.permission.BIND_JOB_SERVICE"> </service> - <service android:name="com.android.server.companion.InactiveAssociationsRemovalService" + <service android:name="com.android.server.companion.association.InactiveAssociationsRemovalService" android:permission="android.permission.BIND_JOB_SERVICE"> </service> diff --git a/services/companion/java/com/android/server/companion/AssociationStore.java b/services/companion/java/com/android/server/companion/AssociationStore.java deleted file mode 100644 index 01905bb2297f..000000000000 --- a/services/companion/java/com/android/server/companion/AssociationStore.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.companion; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UserIdInt; -import android.companion.AssociationInfo; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Collection; -import java.util.List; - -/** - * Interface for a store of {@link AssociationInfo}-s. - */ -public interface AssociationStore { - - @IntDef(prefix = { "CHANGE_TYPE_" }, value = { - CHANGE_TYPE_ADDED, - CHANGE_TYPE_REMOVED, - CHANGE_TYPE_UPDATED_ADDRESS_CHANGED, - CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED, - }) - @Retention(RetentionPolicy.SOURCE) - @interface ChangeType {} - - int CHANGE_TYPE_ADDED = 0; - int CHANGE_TYPE_REMOVED = 1; - int CHANGE_TYPE_UPDATED_ADDRESS_CHANGED = 2; - int CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED = 3; - - /** Listener for any changes to {@link AssociationInfo}-s. */ - interface OnChangeListener { - default void onAssociationChanged( - @ChangeType int changeType, AssociationInfo association) { - switch (changeType) { - case CHANGE_TYPE_ADDED: - onAssociationAdded(association); - break; - - case CHANGE_TYPE_REMOVED: - onAssociationRemoved(association); - break; - - case CHANGE_TYPE_UPDATED_ADDRESS_CHANGED: - onAssociationUpdated(association, true); - break; - - case CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED: - onAssociationUpdated(association, false); - break; - } - } - - default void onAssociationAdded(AssociationInfo association) {} - - default void onAssociationRemoved(AssociationInfo association) {} - - default void onAssociationUpdated(AssociationInfo association, boolean addressChanged) {} - } - - /** - * @return all CDM associations. - */ - @NonNull - Collection<AssociationInfo> getAssociations(); - - /** - * @return a {@link List} of associations that belong to the user. - */ - @NonNull - List<AssociationInfo> getAssociationsForUser(@UserIdInt int userId); - - /** - * @return a {@link List} of association that belong to the package. - */ - @NonNull - List<AssociationInfo> getAssociationsForPackage( - @UserIdInt int userId, @NonNull String packageName); - - /** - * @return an association with the given address that belong to the given package if such an - * association exists, otherwise {@code null}. - */ - @Nullable - AssociationInfo getAssociationsForPackageWithAddress( - @UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress); - - /** - * @return an association with the given id if such an association exists, otherwise - * {@code null}. - */ - @Nullable - AssociationInfo getAssociationById(int id); - - /** - * @return all associations with the given MAc address. - */ - @NonNull - List<AssociationInfo> getAssociationsByAddress(@NonNull String macAddress); - - /** Register a {@link OnChangeListener} */ - void registerListener(@NonNull OnChangeListener listener); - - /** Un-register a previously registered {@link OnChangeListener} */ - void unregisterListener(@NonNull OnChangeListener listener); - - /** @hide */ - static String changeTypeToString(@ChangeType int changeType) { - switch (changeType) { - case CHANGE_TYPE_ADDED: - return "ASSOCIATION_ADDED"; - - case CHANGE_TYPE_REMOVED: - return "ASSOCIATION_REMOVED"; - - case CHANGE_TYPE_UPDATED_ADDRESS_CHANGED: - return "ASSOCIATION_UPDATED"; - - case CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED: - return "ASSOCIATION_UPDATED_ADDRESS_UNCHANGED"; - - default: - return "Unknown (" + changeType + ")"; - } - } -} diff --git a/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java index e4cc1f8949b5..f2409fb8843e 100644 --- a/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java +++ b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java @@ -34,6 +34,9 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.CollectionUtils; +import com.android.server.companion.association.AssociationDiskStore; +import com.android.server.companion.association.AssociationRequestsProcessor; +import com.android.server.companion.association.AssociationStore; import com.android.server.companion.datatransfer.SystemDataTransferRequestStore; import java.nio.ByteBuffer; @@ -54,9 +57,9 @@ class BackupRestoreProcessor { @NonNull private final PackageManagerInternal mPackageManager; @NonNull - private final AssociationStoreImpl mAssociationStore; + private final AssociationStore mAssociationStore; @NonNull - private final PersistentDataStore mPersistentStore; + private final AssociationDiskStore mPersistentStore; @NonNull private final SystemDataTransferRequestStore mSystemDataTransferRequestStore; @NonNull @@ -71,8 +74,8 @@ class BackupRestoreProcessor { new PerUserAssociationSet(); BackupRestoreProcessor(@NonNull CompanionDeviceManagerService service, - @NonNull AssociationStoreImpl associationStore, - @NonNull PersistentDataStore persistentStore, + @NonNull AssociationStore associationStore, + @NonNull AssociationDiskStore persistentStore, @NonNull SystemDataTransferRequestStore systemDataTransferRequestStore, @NonNull AssociationRequestsProcessor associationRequestsProcessor) { mService = service; diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java index 559ebbc290f6..c801489ce963 100644 --- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java +++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java @@ -37,6 +37,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.PerUser; +import com.android.server.companion.association.AssociationStore; import com.android.server.companion.presence.CompanionDevicePresenceMonitor; import com.android.server.companion.presence.ObservableUuid; import com.android.server.companion.presence.ObservableUuidStore; diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 17ba0730c8e4..e4a1048e9faa 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -37,7 +37,9 @@ import static android.os.UserHandle.getCallingUserId; import static com.android.internal.util.CollectionUtils.any; import static com.android.internal.util.Preconditions.checkState; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; -import static com.android.server.companion.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED; +import static com.android.server.companion.association.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED; +import static com.android.server.companion.utils.AssociationUtils.getFirstAssociationIdForUser; +import static com.android.server.companion.utils.AssociationUtils.getLastAssociationIdForUser; import static com.android.server.companion.utils.PackageUtils.isRestrictedSettingsAllowed; import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature; import static com.android.server.companion.utils.PackageUtils.getPackageInfo; @@ -117,6 +119,11 @@ import com.android.internal.util.DumpUtils; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.companion.association.AssociationDiskStore; +import com.android.server.companion.association.AssociationRequestsProcessor; +import com.android.server.companion.association.AssociationRevokeProcessor; +import com.android.server.companion.association.AssociationStore; +import com.android.server.companion.association.InactiveAssociationsRemovalService; import com.android.server.companion.datatransfer.SystemDataTransferProcessor; import com.android.server.companion.datatransfer.SystemDataTransferRequestStore; import com.android.server.companion.datatransfer.contextsync.CrossDeviceCall; @@ -147,8 +154,6 @@ public class CompanionDeviceManagerService extends SystemService { static final String TAG = "CDM_CompanionDeviceManagerService"; static final boolean DEBUG = false; - /** Range of Association IDs allocated for a user. */ - private static final int ASSOCIATIONS_IDS_PER_USER_RANGE = 100000; private static final long PAIR_WITHOUT_PROMPT_WINDOW_MS = 10 * 60 * 1000; // 10 min private static final String PREF_FILE_NAME = "companion_device_preferences.xml"; @@ -160,10 +165,10 @@ public class CompanionDeviceManagerService extends SystemService { private static final int MAX_CN_LENGTH = 500; private final ActivityManager mActivityManager; - private PersistentDataStore mPersistentStore; + private AssociationDiskStore mAssociationDiskStore; private final PersistUserStateHandler mUserPersistenceHandler; - private final AssociationStoreImpl mAssociationStore; + private final AssociationStore mAssociationStore; private final SystemDataTransferRequestStore mSystemDataTransferRequestStore; private AssociationRequestsProcessor mAssociationRequestsProcessor; private SystemDataTransferProcessor mSystemDataTransferProcessor; @@ -178,7 +183,7 @@ public class CompanionDeviceManagerService extends SystemService { private final IAppOpsService mAppOpsManager; private final PowerWhitelistManager mPowerWhitelistManager; private final UserManager mUserManager; - final PackageManagerInternal mPackageManagerInternal; + public final PackageManagerInternal mPackageManagerInternal; private final PowerManagerInternal mPowerManagerInternal; /** @@ -210,7 +215,7 @@ public class CompanionDeviceManagerService extends SystemService { mUserManager = context.getSystemService(UserManager.class); mUserPersistenceHandler = new PersistUserStateHandler(); - mAssociationStore = new AssociationStoreImpl(); + mAssociationStore = new AssociationStore(); mSystemDataTransferRequestStore = new SystemDataTransferRequestStore(); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); @@ -221,11 +226,11 @@ public class CompanionDeviceManagerService extends SystemService { public void onStart() { final Context context = getContext(); - mPersistentStore = new PersistentDataStore(); + mAssociationDiskStore = new AssociationDiskStore(); mAssociationRequestsProcessor = new AssociationRequestsProcessor( /* cdmService */ this, mAssociationStore); mBackupRestoreProcessor = new BackupRestoreProcessor( - /* cdmService */ this, mAssociationStore, mPersistentStore, + /* cdmService */ this, mAssociationStore, mAssociationDiskStore, mSystemDataTransferRequestStore, mAssociationRequestsProcessor); mObservableUuidStore.getObservableUuidsForUser(getContext().getUserId()); @@ -264,10 +269,13 @@ public class CompanionDeviceManagerService extends SystemService { void loadAssociationsFromDisk() { final Set<AssociationInfo> allAssociations = new ArraySet<>(); synchronized (mPreviouslyUsedIds) { + List<Integer> userIds = new ArrayList<>(); + for (UserInfo user : mUserManager.getAliveUsers()) { + userIds.add(user.id); + } // The data is stored in DE directories, so we can read the data for all users now // (which would not be possible if the data was stored to CE directories). - mPersistentStore.readStateForUsers( - mUserManager.getAliveUsers(), allAssociations, mPreviouslyUsedIds); + mAssociationDiskStore.readStateForUsers(userIds, allAssociations, mPreviouslyUsedIds); } final Set<AssociationInfo> activeAssociations = @@ -291,7 +299,7 @@ public class CompanionDeviceManagerService extends SystemService { } } - mAssociationStore.setAssociations(activeAssociations); + mAssociationStore.setAssociationsToCache(activeAssociations); // IMPORTANT: only do this AFTER mAssociationStore.setAssociations(), because // persistStateForUser() queries AssociationStore. @@ -582,7 +590,7 @@ public class CompanionDeviceManagerService extends SystemService { final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUser(userId); - mPersistentStore.persistStateForUser(userId, allAssociations, usedIdsForUser); + mAssociationDiskStore.persistStateForUser(userId, allAssociations, usedIdsForUser); } private void notifyListeners( @@ -646,7 +654,8 @@ public class CompanionDeviceManagerService extends SystemService { final List<AssociationInfo> associationsForPackage = mAssociationStore.getAssociationsForPackage(userId, packageName); for (AssociationInfo association : associationsForPackage) { - updateSpecialAccessPermissionForAssociatedPackage(association); + updateSpecialAccessPermissionForAssociatedPackage(association.getUserId(), + association.getPackageName()); } mCompanionAppController.onPackagesChanged(userId); @@ -692,7 +701,7 @@ public class CompanionDeviceManagerService extends SystemService { } } - class CompanionDeviceManagerImpl extends ICompanionDeviceManager.Stub { + public class CompanionDeviceManagerImpl extends ICompanionDeviceManager.Stub { @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { @@ -1338,7 +1347,10 @@ public class CompanionDeviceManagerService extends SystemService { return usedIdsForPackage; } - int getNewAssociationIdForPackage(@UserIdInt int userId, @NonNull String packageName) { + /** + * Get a new association id for the package. + */ + public int getNewAssociationIdForPackage(@UserIdInt int userId, @NonNull String packageName) { synchronized (mPreviouslyUsedIds) { // First: collect all IDs currently in use for this user's Associations. final SparseBooleanArray usedIds = new SparseBooleanArray(); @@ -1383,9 +1395,12 @@ public class CompanionDeviceManagerService extends SystemService { } } - void updateSpecialAccessPermissionForAssociatedPackage(AssociationInfo association) { + /** + * Update special access for the association's package + */ + public void updateSpecialAccessPermissionForAssociatedPackage(int userId, String packageName) { final PackageInfo packageInfo = - getPackageInfo(getContext(), association.getUserId(), association.getPackageName()); + getPackageInfo(getContext(), userId, packageName); Binder.withCleanCallingIdentity(() -> updateSpecialAccessPermissionAsSystem(packageInfo)); } @@ -1539,15 +1554,6 @@ public class CompanionDeviceManagerService extends SystemService { } }; - static int getFirstAssociationIdForUser(@UserIdInt int userId) { - // We want the IDs to start from 1, not 0. - return userId * ASSOCIATIONS_IDS_PER_USER_RANGE + 1; - } - - static int getLastAssociationIdForUser(@UserIdInt int userId) { - return (userId + 1) * ASSOCIATIONS_IDS_PER_USER_RANGE; - } - private static Map<String, Set<Integer>> deepUnmodifiableCopy(Map<String, Set<Integer>> orig) { final Map<String, Set<Integer>> copy = new HashMap<>(); @@ -1671,11 +1677,17 @@ public class CompanionDeviceManagerService extends SystemService { } } - void postPersistUserState(@UserIdInt int userId) { + /** + * Persist associations + */ + public void postPersistUserState(@UserIdInt int userId) { mUserPersistenceHandler.postPersistUserState(userId); } - static class PerUserAssociationSet extends PerUser<Set<AssociationInfo>> { + /** + * Set to store associations + */ + public static class PerUserAssociationSet extends PerUser<Set<AssociationInfo>> { @Override protected @NonNull Set<AssociationInfo> create(int userId) { return new ArraySet<>(); diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java index 74b4cabbab67..16877dcaf183 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java @@ -32,6 +32,9 @@ import android.os.ShellCommand; import android.util.Base64; import android.util.proto.ProtoOutputStream; +import com.android.server.companion.association.AssociationRequestsProcessor; +import com.android.server.companion.association.AssociationRevokeProcessor; +import com.android.server.companion.association.AssociationStore; import com.android.server.companion.datatransfer.SystemDataTransferProcessor; import com.android.server.companion.datatransfer.contextsync.BitmapUtils; import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncController; @@ -47,7 +50,7 @@ class CompanionDeviceShellCommand extends ShellCommand { private final CompanionDeviceManagerService mService; private final AssociationRevokeProcessor mRevokeProcessor; - private final AssociationStoreImpl mAssociationStore; + private final AssociationStore mAssociationStore; private final CompanionDevicePresenceMonitor mDevicePresenceMonitor; private final CompanionTransportManager mTransportManager; @@ -56,7 +59,7 @@ class CompanionDeviceShellCommand extends ShellCommand { private final BackupRestoreProcessor mBackupRestoreProcessor; CompanionDeviceShellCommand(CompanionDeviceManagerService service, - AssociationStoreImpl associationStore, + AssociationStore associationStore, CompanionDevicePresenceMonitor devicePresenceMonitor, CompanionTransportManager transportManager, SystemDataTransferProcessor systemDataTransferProcessor, diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java index 7527efb7b19a..75cb12058247 100644 --- a/services/companion/java/com/android/server/companion/PersistentDataStore.java +++ b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.companion; +package com.android.server.companion.association; import static com.android.internal.util.CollectionUtils.forEach; import static com.android.internal.util.XmlUtils.readBooleanAttribute; @@ -25,8 +25,8 @@ import static com.android.internal.util.XmlUtils.writeBooleanAttribute; import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.internal.util.XmlUtils.writeStringAttribute; -import static com.android.server.companion.CompanionDeviceManagerService.getFirstAssociationIdForUser; -import static com.android.server.companion.CompanionDeviceManagerService.getLastAssociationIdForUser; +import static com.android.server.companion.utils.AssociationUtils.getFirstAssociationIdForUser; +import static com.android.server.companion.utils.AssociationUtils.getLastAssociationIdForUser; import static com.android.server.companion.utils.DataStoreUtils.createStorageFileForUser; import static com.android.server.companion.utils.DataStoreUtils.fileToByteArray; import static com.android.server.companion.utils.DataStoreUtils.isEndOfTag; @@ -38,12 +38,10 @@ import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.companion.AssociationInfo; -import android.content.pm.UserInfo; import android.net.MacAddress; import android.os.Environment; import android.util.ArrayMap; import android.util.AtomicFile; -import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.Xml; @@ -51,7 +49,6 @@ import android.util.Xml; import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; -import com.android.server.companion.utils.DataStoreUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -71,6 +68,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** + * IMPORTANT: This class should NOT be directly used except {@link AssociationStore} + * * The class responsible for persisting Association records and other related information (such as * previously used IDs) to a disk, and reading the data back from the disk. * @@ -107,8 +106,6 @@ import java.util.concurrent.ConcurrentMap; * Since Android T the data is stored to "companion_device_manager.xml" file in * {@link Environment#getDataSystemDeDirectory(int) /data/system_de/}. * - * See {@link DataStoreUtils#getBaseStorageFileForUser(int, String)} - * * <p> * Since Android T the data is stored using the v1 schema. * @@ -161,9 +158,8 @@ import java.util.concurrent.ConcurrentMap; * }</pre> */ @SuppressLint("LongLogTag") -final class PersistentDataStore { - private static final String TAG = "CompanionDevice_PersistentDataStore"; - private static final boolean DEBUG = CompanionDeviceManagerService.DEBUG; +public final class AssociationDiskStore { + private static final String TAG = "CompanionDevice_AssociationDiskStore"; private static final int CURRENT_PERSISTENCE_VERSION = 1; @@ -200,11 +196,13 @@ final class PersistentDataStore { private final @NonNull ConcurrentMap<Integer, AtomicFile> mUserIdToStorageFile = new ConcurrentHashMap<>(); - void readStateForUsers(@NonNull List<UserInfo> users, + /** + * Read all associations for given users + */ + public void readStateForUsers(@NonNull List<Integer> userIds, @NonNull Set<AssociationInfo> allAssociationsOut, @NonNull SparseArray<Map<String, Set<Integer>>> previouslyUsedIdsPerUserOut) { - for (UserInfo user : users) { - final int userId = user.id; + for (int userId : userIds) { // Previously used IDs are stored in the "out" collection per-user. final Map<String, Set<Integer>> previouslyUsedIds = new ArrayMap<>(); @@ -247,12 +245,11 @@ final class PersistentDataStore { * @param associationsOut a container to read the {@link AssociationInfo}s "into". * @param previouslyUsedIdsPerPackageOut a container to read the used IDs "into". */ - void readStateForUser(@UserIdInt int userId, + private void readStateForUser(@UserIdInt int userId, @NonNull Collection<AssociationInfo> associationsOut, @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) { Slog.i(TAG, "Reading associations for user " + userId + " from disk"); final AtomicFile file = getStorageFileForUser(userId); - if (DEBUG) Log.d(TAG, " > File=" + file.getBaseFile().getPath()); // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize // accesses to the file on the file system using this AtomicFile object. @@ -261,12 +258,8 @@ final class PersistentDataStore { final AtomicFile readFrom; final String rootTag; if (!file.getBaseFile().exists()) { - if (DEBUG) Log.d(TAG, " > File does not exist -> Try to read legacy file"); - legacyBaseFile = getBaseLegacyStorageFileForUser(userId); - if (DEBUG) Log.d(TAG, " > Legacy file=" + legacyBaseFile.getPath()); if (!legacyBaseFile.exists()) { - if (DEBUG) Log.d(TAG, " > Legacy file does not exist -> Abort"); return; } @@ -277,27 +270,16 @@ final class PersistentDataStore { rootTag = XML_TAG_STATE; } - if (DEBUG) Log.d(TAG, " > Reading associations..."); final int version = readStateFromFileLocked(userId, readFrom, rootTag, associationsOut, previouslyUsedIdsPerPackageOut); - if (DEBUG) { - Log.d(TAG, " > Done reading: " + associationsOut); - if (version < CURRENT_PERSISTENCE_VERSION) { - Log.d(TAG, " > File used old format: v." + version + " -> Re-write"); - } - } if (legacyBaseFile != null || version < CURRENT_PERSISTENCE_VERSION) { // The data is either in the legacy file or in the legacy format, or both. // Save the data to right file in using the current format. - if (DEBUG) { - Log.d(TAG, " > Writing the data to " + file.getBaseFile().getPath()); - } persistStateToFileLocked(file, associationsOut, previouslyUsedIdsPerPackageOut); if (legacyBaseFile != null) { // We saved the data to the right file, can delete the old file now. - if (DEBUG) Log.d(TAG, " > Deleting legacy file"); legacyBaseFile.delete(); } } @@ -314,14 +296,12 @@ final class PersistentDataStore { * @param associations a set of user's associations. * @param previouslyUsedIdsPerPackage a set previously used Association IDs for the user. */ - void persistStateForUser(@UserIdInt int userId, + public void persistStateForUser(@UserIdInt int userId, @NonNull Collection<AssociationInfo> associations, @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) { Slog.i(TAG, "Writing associations for user " + userId + " to disk"); - if (DEBUG) Slog.d(TAG, " > " + associations); final AtomicFile file = getStorageFileForUser(userId); - if (DEBUG) Log.d(TAG, " > File=" + file.getBaseFile().getPath()); // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize // accesses to the file on the file system using this AtomicFile object. synchronized (file) { @@ -404,7 +384,10 @@ final class PersistentDataStore { u -> createStorageFileForUser(userId, FILE_NAME)); } - byte[] getBackupPayload(@UserIdInt int userId) { + /** + * Get associations backup payload from disk + */ + public byte[] getBackupPayload(@UserIdInt int userId) { Slog.i(TAG, "Fetching stored state data for user " + userId + " from disk"); final AtomicFile file = getStorageFileForUser(userId); @@ -413,7 +396,10 @@ final class PersistentDataStore { } } - void readStateFromPayload(byte[] payload, @UserIdInt int userId, + /** + * Convert payload to a set of associations + */ + public void readStateFromPayload(byte[] payload, @UserIdInt int userId, @NonNull Set<AssociationInfo> associationsOut, @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) { try (ByteArrayInputStream in = new ByteArrayInputStream(payload)) { @@ -615,7 +601,7 @@ final class PersistentDataStore { macAddress, displayName, profile, null, selfManaged, notify, revoked, pending, timeApproved, lastTimeConnected, systemDataSyncFlags); } catch (Exception e) { - if (DEBUG) Log.w(TAG, "Could not create AssociationInfo", e); + Slog.e(TAG, "Could not create AssociationInfo", e); } return associationInfo; } diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java index 1dab40ea5876..29ec7c2c9743 100644 --- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java +++ b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.companion; +package com.android.server.companion.association; import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; import static android.app.PendingIntent.FLAG_IMMUTABLE; @@ -24,7 +24,6 @@ import static android.companion.CompanionDeviceManager.RESULT_INTERNAL_ERROR; import static android.content.ComponentName.createRelative; import static android.content.pm.PackageManager.FEATURE_WATCH; -import static com.android.server.companion.CompanionDeviceManagerService.DEBUG; import static com.android.server.companion.utils.MetricUtils.logCreateAssociation; import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature; import static com.android.server.companion.utils.PermissionsUtils.enforcePermissionForCreatingAssociation; @@ -59,6 +58,7 @@ import android.os.UserHandle; import android.util.Slog; import com.android.internal.R; +import com.android.server.companion.CompanionDeviceManagerService; import com.android.server.companion.utils.PackageUtils; import java.util.List; @@ -107,7 +107,7 @@ import java.util.List; * ResultReceiver, MacAddress) */ @SuppressLint("LongLogTag") -class AssociationRequestsProcessor { +public class AssociationRequestsProcessor { private static final String TAG = "CDM_AssociationRequestsProcessor"; // AssociationRequestsProcessor <-> UI @@ -130,12 +130,12 @@ class AssociationRequestsProcessor { private final @NonNull Context mContext; private final @NonNull CompanionDeviceManagerService mService; private final @NonNull PackageManagerInternal mPackageManager; - private final @NonNull AssociationStoreImpl mAssociationStore; + private final @NonNull AssociationStore mAssociationStore; @NonNull private final ComponentName mCompanionDeviceActivity; - AssociationRequestsProcessor(@NonNull CompanionDeviceManagerService service, - @NonNull AssociationStoreImpl associationStore) { + public AssociationRequestsProcessor(@NonNull CompanionDeviceManagerService service, + @NonNull AssociationStore associationStore) { mContext = service.getContext(); mService = service; mPackageManager = service.mPackageManagerInternal; @@ -149,7 +149,7 @@ class AssociationRequestsProcessor { * Handle incoming {@link AssociationRequest}s, sent via * {@link android.companion.ICompanionDeviceManager#associate(AssociationRequest, IAssociationRequestCallback, String, int)} */ - void processNewAssociationRequest(@NonNull AssociationRequest request, + public void processNewAssociationRequest(@NonNull AssociationRequest request, @NonNull String packageName, @UserIdInt int userId, @NonNull IAssociationRequestCallback callback) { requireNonNull(request, "Request MUST NOT be null"); @@ -161,11 +161,8 @@ class AssociationRequestsProcessor { requireNonNull(callback, "Callback MUST NOT be null"); final int packageUid = mPackageManager.getPackageUid(packageName, 0, userId); - if (DEBUG) { - Slog.d(TAG, "processNewAssociationRequest() " - + "request=" + request + ", " - + "package=u" + userId + "/" + packageName + " (uid=" + packageUid + ")"); - } + Slog.d(TAG, "processNewAssociationRequest() " + "request=" + request + ", " + "package=u" + + userId + "/" + packageName + " (uid=" + packageUid + ")"); // 1. Enforce permissions and other requirements. enforcePermissionForCreatingAssociation(mContext, request, packageUid); @@ -223,7 +220,7 @@ class AssociationRequestsProcessor { /** * Process another AssociationRequest in CompanionDeviceActivity to cancel current dialog. */ - PendingIntent buildAssociationCancellationIntent(@NonNull String packageName, + public PendingIntent buildAssociationCancellationIntent(@NonNull String packageName, @UserIdInt int userId) { requireNonNull(packageName, "Package name MUST NOT be null"); @@ -248,13 +245,6 @@ class AssociationRequestsProcessor { final int userId = request.getUserId(); final int packageUid = mPackageManager.getPackageUid(packageName, 0, userId); - if (DEBUG) { - Slog.d(TAG, "processAssociationRequestApproval()\n" - + " package=u" + userId + "/" + packageName + " (uid=" + packageUid + ")\n" - + " request=" + request + "\n" - + " macAddress=" + macAddress + "\n"); - } - // 1. Need to check permissions again in case something changed, since we first received // this request. try { @@ -288,6 +278,9 @@ class AssociationRequestsProcessor { } } + /** + * Create an association. + */ public void createAssociation(@UserIdInt int userId, @NonNull String packageName, @Nullable MacAddress macAddress, @Nullable CharSequence displayName, @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice, @@ -309,6 +302,9 @@ class AssociationRequestsProcessor { // that there are other devices with the same profile, so the role holder won't be removed. } + /** + * Grant a role if specified and add an association to store. + */ public void maybeGrantRoleAndStoreAssociation(@NonNull AssociationInfo association, @Nullable IAssociationRequestCallback callback, @Nullable ResultReceiver resultReceiver) { @@ -331,6 +327,9 @@ class AssociationRequestsProcessor { }); } + /** + * Enable system data sync. + */ public void enableSystemDataSync(int associationId, int flags) { AssociationInfo association = mAssociationStore.getAssociationById(associationId); AssociationInfo updated = (new AssociationInfo.Builder(association)) @@ -338,6 +337,9 @@ class AssociationRequestsProcessor { mAssociationStore.updateAssociation(updated); } + /** + * Disable system data sync. + */ public void disableSystemDataSync(int associationId, int flags) { AssociationInfo association = mAssociationStore.getAssociationById(associationId); AssociationInfo updated = (new AssociationInfo.Builder(association)) @@ -350,7 +352,8 @@ class AssociationRequestsProcessor { mAssociationStore.addAssociation(association); - mService.updateSpecialAccessPermissionForAssociatedPackage(association); + mService.updateSpecialAccessPermissionForAssociatedPackage(association.getUserId(), + association.getPackageName()); logCreateAssociation(association.getDeviceProfile()); } @@ -431,38 +434,37 @@ class AssociationRequestsProcessor { private final ResultReceiver mOnRequestConfirmationReceiver = new ResultReceiver(Handler.getMain()) { - @Override - protected void onReceiveResult(int resultCode, Bundle data) { - if (DEBUG) { - Slog.d(TAG, "mOnRequestConfirmationReceiver.onReceiveResult() " - + "code=" + resultCode + ", " + "data=" + data); - } - - if (resultCode != RESULT_CODE_ASSOCIATION_APPROVED) { - Slog.w(TAG, "Unknown result code:" + resultCode); - return; - } - - final AssociationRequest request = data.getParcelable(EXTRA_ASSOCIATION_REQUEST, android.companion.AssociationRequest.class); - final IAssociationRequestCallback callback = IAssociationRequestCallback.Stub - .asInterface(data.getBinder(EXTRA_APPLICATION_CALLBACK)); - final ResultReceiver resultReceiver = data.getParcelable(EXTRA_RESULT_RECEIVER, android.os.ResultReceiver.class); - - requireNonNull(request); - requireNonNull(callback); - requireNonNull(resultReceiver); - - final MacAddress macAddress; - if (request.isSelfManaged()) { - macAddress = null; - } else { - macAddress = data.getParcelable(EXTRA_MAC_ADDRESS, android.net.MacAddress.class); - requireNonNull(macAddress); - } - - processAssociationRequestApproval(request, callback, resultReceiver, macAddress); - } - }; + @Override + protected void onReceiveResult(int resultCode, Bundle data) { + if (resultCode != RESULT_CODE_ASSOCIATION_APPROVED) { + Slog.w(TAG, "Unknown result code:" + resultCode); + return; + } + + final AssociationRequest request = data.getParcelable(EXTRA_ASSOCIATION_REQUEST, + android.companion.AssociationRequest.class); + final IAssociationRequestCallback callback = IAssociationRequestCallback.Stub + .asInterface(data.getBinder(EXTRA_APPLICATION_CALLBACK)); + final ResultReceiver resultReceiver = data.getParcelable(EXTRA_RESULT_RECEIVER, + android.os.ResultReceiver.class); + + requireNonNull(request); + requireNonNull(callback); + requireNonNull(resultReceiver); + + final MacAddress macAddress; + if (request.isSelfManaged()) { + macAddress = null; + } else { + macAddress = data.getParcelable(EXTRA_MAC_ADDRESS, + android.net.MacAddress.class); + requireNonNull(macAddress); + } + + processAssociationRequestApproval(request, callback, resultReceiver, + macAddress); + } + }; private boolean mayAssociateWithoutPrompt(@NonNull String packageName, @UserIdInt int userId) { // Throttle frequent associations diff --git a/services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java b/services/companion/java/com/android/server/companion/association/AssociationRevokeProcessor.java index 10963ea37e8e..490be0da593b 100644 --- a/services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java +++ b/services/companion/java/com/android/server/companion/association/AssociationRevokeProcessor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.companion; +package com.android.server.companion.association; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION; @@ -38,6 +38,8 @@ import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.server.companion.CompanionApplicationController; +import com.android.server.companion.CompanionDeviceManagerService; import com.android.server.companion.datatransfer.SystemDataTransferRequestStore; import com.android.server.companion.presence.CompanionDevicePresenceMonitor; @@ -55,7 +57,7 @@ public class AssociationRevokeProcessor { private static final boolean DEBUG = false; private final @NonNull Context mContext; private final @NonNull CompanionDeviceManagerService mService; - private final @NonNull AssociationStoreImpl mAssociationStore; + private final @NonNull AssociationStore mAssociationStore; private final @NonNull PackageManagerInternal mPackageManagerInternal; private final @NonNull CompanionDevicePresenceMonitor mDevicePresenceMonitor; private final @NonNull SystemDataTransferRequestStore mSystemDataTransferRequestStore; @@ -90,8 +92,8 @@ public class AssociationRevokeProcessor { @GuardedBy("mRevokedAssociationsPendingRoleHolderRemoval") private final Map<Integer, String> mUidsPendingRoleHolderRemoval = new HashMap<>(); - AssociationRevokeProcessor(@NonNull CompanionDeviceManagerService service, - @NonNull AssociationStoreImpl associationStore, + public AssociationRevokeProcessor(@NonNull CompanionDeviceManagerService service, + @NonNull AssociationStore associationStore, @NonNull PackageManagerInternal packageManager, @NonNull CompanionDevicePresenceMonitor devicePresenceMonitor, @NonNull CompanionApplicationController applicationController, @@ -108,8 +110,11 @@ public class AssociationRevokeProcessor { mSystemDataTransferRequestStore = systemDataTransferRequestStore; } + /** + * Disassociate an association + */ // TODO: also revoke notification access - void disassociateInternal(int associationId) { + public void disassociateInternal(int associationId) { final AssociationInfo association = mAssociationStore.getAssociationById(associationId); final int userId = association.getUserId(); final String packageName = association.getPackageName(); @@ -168,7 +173,7 @@ public class AssociationRevokeProcessor { * {@code RoleManager.removeRoleHolderAsUser()} will kill the application's process, * which would lead to the poor UX, hence need to try later. */ - boolean maybeRemoveRoleHolderForAssociation(@NonNull AssociationInfo association) { + public boolean maybeRemoveRoleHolderForAssociation(@NonNull AssociationInfo association) { if (DEBUG) Log.d(TAG, "maybeRemoveRoleHolderForAssociation() association=" + association); final String deviceProfile = association.getDeviceProfile(); @@ -208,15 +213,6 @@ public class AssociationRevokeProcessor { return true; } - @SuppressLint("MissingPermission") - private int getPackageProcessImportance(@UserIdInt int userId, @NonNull String packageName) { - return Binder.withCleanCallingIdentity(() -> { - final int uid = - mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId); - return mActivityManager.getUidImportance(uid); - }); - } - /** * Set revoked flag for active association and add the revoked association and the uid into * the caches. @@ -225,7 +221,7 @@ public class AssociationRevokeProcessor { * @see #mUidsPendingRoleHolderRemoval * @see OnPackageVisibilityChangeListener */ - void addToPendingRoleHolderRemoval(@NonNull AssociationInfo association) { + public void addToPendingRoleHolderRemoval(@NonNull AssociationInfo association) { // First: set revoked flag association = (new AssociationInfo.Builder(association)).setRevoked(true).build(); final String packageName = association.getPackageName(); @@ -247,6 +243,28 @@ public class AssociationRevokeProcessor { } /** + * @return a copy of the revoked associations set (safeguarding against + * {@code ConcurrentModificationException}-s). + */ + @NonNull + public Set<AssociationInfo> getPendingRoleHolderRemovalAssociationsForUser( + @UserIdInt int userId) { + synchronized (mRevokedAssociationsPendingRoleHolderRemoval) { + // Return a copy. + return new ArraySet<>(mRevokedAssociationsPendingRoleHolderRemoval.forUser(userId)); + } + } + + @SuppressLint("MissingPermission") + private int getPackageProcessImportance(@UserIdInt int userId, @NonNull String packageName) { + return Binder.withCleanCallingIdentity(() -> { + final int uid = + mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId); + return mActivityManager.getUidImportance(uid); + }); + } + + /** * Remove the revoked association from the cache and also remove the uid from the map if * there are other associations with the same package still pending for role holder removal. * @@ -279,18 +297,6 @@ public class AssociationRevokeProcessor { } } - /** - * @return a copy of the revoked associations set (safeguarding against - * {@code ConcurrentModificationException}-s). - */ - @NonNull Set<AssociationInfo> getPendingRoleHolderRemovalAssociationsForUser( - @UserIdInt int userId) { - synchronized (mRevokedAssociationsPendingRoleHolderRemoval) { - // Return a copy. - return new ArraySet<>(mRevokedAssociationsPendingRoleHolderRemoval.forUser(userId)); - } - } - private String getPackageNameByUid(int uid) { synchronized (mRevokedAssociationsPendingRoleHolderRemoval) { return mUidsPendingRoleHolderRemoval.get(uid); diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/association/AssociationStore.java index 8c6ad3bad857..2f94bdebb988 100644 --- a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java +++ b/services/companion/java/com/android/server/companion/association/AssociationStore.java @@ -14,15 +14,15 @@ * limitations under the License. */ -package com.android.server.companion; +package com.android.server.companion.association; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.companion.AssociationInfo; import android.net.MacAddress; -import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -30,6 +30,8 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.CollectionUtils; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -40,24 +42,69 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.StringJoiner; /** - * Implementation of the {@link AssociationStore}, with addition of the methods for modification. - * <ul> - * <li> {@link #addAssociation(AssociationInfo)} - * <li> {@link #removeAssociation(int)} - * <li> {@link #updateAssociation(AssociationInfo)} - * </ul> - * - * The class has package-private access level, and instances of the class should only be created by - * the {@link CompanionDeviceManagerService}. - * Other system component (both inside and outside if the com.android.server.companion package) - * should use public {@link AssociationStore} interface. + * Association store for CRUD. */ @SuppressLint("LongLogTag") -class AssociationStoreImpl implements AssociationStore { - private static final boolean DEBUG = false; +public class AssociationStore { + + @IntDef(prefix = { "CHANGE_TYPE_" }, value = { + CHANGE_TYPE_ADDED, + CHANGE_TYPE_REMOVED, + CHANGE_TYPE_UPDATED_ADDRESS_CHANGED, + CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ChangeType {} + + public static final int CHANGE_TYPE_ADDED = 0; + public static final int CHANGE_TYPE_REMOVED = 1; + public static final int CHANGE_TYPE_UPDATED_ADDRESS_CHANGED = 2; + public static final int CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED = 3; + + /** Listener for any changes to associations. */ + public interface OnChangeListener { + /** + * Called when there are association changes. + */ + default void onAssociationChanged( + @AssociationStore.ChangeType int changeType, AssociationInfo association) { + switch (changeType) { + case CHANGE_TYPE_ADDED: + onAssociationAdded(association); + break; + + case CHANGE_TYPE_REMOVED: + onAssociationRemoved(association); + break; + + case CHANGE_TYPE_UPDATED_ADDRESS_CHANGED: + onAssociationUpdated(association, true); + break; + + case CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED: + onAssociationUpdated(association, false); + break; + } + } + + /** + * Called when an association is added. + */ + default void onAssociationAdded(AssociationInfo association) {} + + /** + * Called when an association is removed. + */ + default void onAssociationRemoved(AssociationInfo association) {} + + /** + * Called when an association is updated. + */ + default void onAssociationUpdated(AssociationInfo association, boolean addressChanged) {} + } + private static final String TAG = "CDM_AssociationStore"; private final Object mLock = new Object(); @@ -72,17 +119,17 @@ class AssociationStoreImpl implements AssociationStore { @GuardedBy("mListeners") private final Set<OnChangeListener> mListeners = new LinkedHashSet<>(); - void addAssociation(@NonNull AssociationInfo association) { + /** + * Add an association. + */ + public void addAssociation(@NonNull AssociationInfo association) { + Slog.i(TAG, "Adding new association=" + association); + // Validity check first. checkNotRevoked(association); final int id = association.getId(); - if (DEBUG) { - Log.i(TAG, "addAssociation() " + association.toShortString()); - Log.d(TAG, " association=" + association); - } - synchronized (mLock) { if (mIdMap.containsKey(id)) { Slog.e(TAG, "Association with id " + id + " already exists."); @@ -96,34 +143,34 @@ class AssociationStoreImpl implements AssociationStore { } invalidateCacheForUserLocked(association.getUserId()); + + Slog.i(TAG, "Done adding new association."); } broadcastChange(CHANGE_TYPE_ADDED, association); } - void updateAssociation(@NonNull AssociationInfo updated) { + /** + * Update an association. + */ + public void updateAssociation(@NonNull AssociationInfo updated) { + Slog.i(TAG, "Updating new association=" + updated); // Validity check first. checkNotRevoked(updated); final int id = updated.getId(); - if (DEBUG) { - Log.i(TAG, "updateAssociation() " + updated.toShortString()); - Log.d(TAG, " updated=" + updated); - } - final AssociationInfo current; final boolean macAddressChanged; synchronized (mLock) { current = mIdMap.get(id); if (current == null) { - if (DEBUG) Log.w(TAG, "Association with id " + id + " does not exist."); + Slog.w(TAG, "Can't update association. It does not exist."); return; } - if (DEBUG) Log.d(TAG, " current=" + current); if (current.equals(updated)) { - if (DEBUG) Log.w(TAG, " No changes."); + Slog.w(TAG, "Association is the same."); return; } @@ -144,6 +191,7 @@ class AssociationStoreImpl implements AssociationStore { mAddressMap.computeIfAbsent(updatedAddress, it -> new HashSet<>()).add(id); } } + Slog.i(TAG, "Done updating association."); } final int changeType = macAddressChanged ? CHANGE_TYPE_UPDATED_ADDRESS_CHANGED @@ -151,21 +199,19 @@ class AssociationStoreImpl implements AssociationStore { broadcastChange(changeType, updated); } - void removeAssociation(int id) { - if (DEBUG) Log.i(TAG, "removeAssociation() id=" + id); + /** + * Remove an association + */ + public void removeAssociation(int id) { + Slog.i(TAG, "Removing association id=" + id); final AssociationInfo association; synchronized (mLock) { association = mIdMap.remove(id); if (association == null) { - if (DEBUG) Log.w(TAG, "Association with id " + id + " is not stored."); + Slog.w(TAG, "Can't remove association. It does not exist."); return; - } else { - if (DEBUG) { - Log.i(TAG, "removed " + association.toShortString()); - Log.d(TAG, " association=" + association); - } } final MacAddress macAddress = association.getDeviceMacAddress(); @@ -174,6 +220,8 @@ class AssociationStoreImpl implements AssociationStore { } invalidateCacheForUserLocked(association.getUserId()); + + Slog.i(TAG, "Done removing association."); } broadcastChange(CHANGE_TYPE_REMOVED, association); @@ -195,12 +243,18 @@ class AssociationStoreImpl implements AssociationStore { } } + /** + * Get associations for the user. + */ public @NonNull List<AssociationInfo> getAssociationsForUser(@UserIdInt int userId) { synchronized (mLock) { return getAssociationsForUserLocked(userId); } } + /** + * Get associations for the package + */ public @NonNull List<AssociationInfo> getAssociationsForPackage( @UserIdInt int userId, @NonNull String packageName) { final List<AssociationInfo> associationsForUser = getAssociationsForUser(userId); @@ -210,6 +264,9 @@ class AssociationStoreImpl implements AssociationStore { return Collections.unmodifiableList(associationsForPackage); } + /** + * Get associations by mac address for the package. + */ public @Nullable AssociationInfo getAssociationsForPackageWithAddress( @UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress) { final List<AssociationInfo> associations = getAssociationsByAddress(macAddress); @@ -217,13 +274,20 @@ class AssociationStoreImpl implements AssociationStore { it -> it.belongsToPackage(userId, packageName)); } + /** + * Get association by id. + */ public @Nullable AssociationInfo getAssociationById(int id) { synchronized (mLock) { return mIdMap.get(id); } } - public @NonNull List<AssociationInfo> getAssociationsByAddress(@NonNull String macAddress) { + /** + * Get associations by mac address. + */ + @NonNull + public List<AssociationInfo> getAssociationsByAddress(@NonNull String macAddress) { final MacAddress address = MacAddress.fromString(macAddress); synchronized (mLock) { @@ -240,7 +304,8 @@ class AssociationStoreImpl implements AssociationStore { } @GuardedBy("mLock") - private @NonNull List<AssociationInfo> getAssociationsForUserLocked(@UserIdInt int userId) { + @NonNull + private List<AssociationInfo> getAssociationsForUserLocked(@UserIdInt int userId) { final List<AssociationInfo> cached = mCachedPerUser.get(userId); if (cached != null) { return cached; @@ -262,12 +327,18 @@ class AssociationStoreImpl implements AssociationStore { mCachedPerUser.delete(userId); } + /** + * Register a listener for association changes. + */ public void registerListener(@NonNull OnChangeListener listener) { synchronized (mListeners) { mListeners.add(listener); } } + /** + * Unregister a listener previously registered for association changes. + */ public void unregisterListener(@NonNull OnChangeListener listener) { synchronized (mListeners) { mListeners.remove(listener); @@ -297,43 +368,30 @@ class AssociationStoreImpl implements AssociationStore { } } - void setAssociations(Collection<AssociationInfo> allAssociations) { + /** + * Set associations to cache. It will clear the existing cache. + */ + public void setAssociationsToCache(Collection<AssociationInfo> associations) { // Validity check first. - allAssociations.forEach(AssociationStoreImpl::checkNotRevoked); + associations.forEach(AssociationStore::checkNotRevoked); - if (DEBUG) { - Log.i(TAG, "setAssociations() n=" + allAssociations.size()); - final StringJoiner stringJoiner = new StringJoiner(", "); - allAssociations.forEach(assoc -> stringJoiner.add(assoc.toShortString())); - Log.v(TAG, " associations=" + stringJoiner); - } synchronized (mLock) { - setAssociationsLocked(allAssociations); - } - } - - @GuardedBy("mLock") - private void setAssociationsLocked(Collection<AssociationInfo> associations) { - clearLocked(); + mIdMap.clear(); + mAddressMap.clear(); + mCachedPerUser.clear(); - for (AssociationInfo association : associations) { - final int id = association.getId(); - mIdMap.put(id, association); + for (AssociationInfo association : associations) { + final int id = association.getId(); + mIdMap.put(id, association); - final MacAddress address = association.getDeviceMacAddress(); - if (address != null) { - mAddressMap.computeIfAbsent(address, it -> new HashSet<>()).add(id); + final MacAddress address = association.getDeviceMacAddress(); + if (address != null) { + mAddressMap.computeIfAbsent(address, it -> new HashSet<>()).add(id); + } } } } - @GuardedBy("mLock") - private void clearLocked() { - mIdMap.clear(); - mAddressMap.clear(); - mCachedPerUser.clear(); - } - private static void checkNotRevoked(@NonNull AssociationInfo association) { if (association.isRevoked()) { throw new IllegalArgumentException( diff --git a/services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java b/services/companion/java/com/android/server/companion/association/InactiveAssociationsRemovalService.java index aac628cab403..894c49a2b5cf 100644 --- a/services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java +++ b/services/companion/java/com/android/server/companion/association/InactiveAssociationsRemovalService.java @@ -14,9 +14,7 @@ * limitations under the License. */ -package com.android.server.companion; - -import static com.android.server.companion.CompanionDeviceManagerService.TAG; +package com.android.server.companion.association; import static java.util.concurrent.TimeUnit.DAYS; @@ -29,13 +27,17 @@ import android.content.Context; import android.util.Slog; import com.android.server.LocalServices; +import com.android.server.companion.CompanionDeviceManagerServiceInternal; /** - * A Job Service responsible for clean up the Association. + * A Job Service responsible for clean up idle self-managed associations. + * * The job will be executed only if the device is charging and in idle mode due to the application - * will be killed if association/role are revoked. + * will be killed if association/role are revoked. See {@link AssociationRevokeProcessor} */ public class InactiveAssociationsRemovalService extends JobService { + + private static final String TAG = "CDM_InactiveAssociationsRemovalService"; private static final String JOB_NAMESPACE = "companion"; private static final int JOB_ID = 1; private static final long ONE_DAY_INTERVAL = DAYS.toMillis(1); @@ -60,7 +62,10 @@ public class InactiveAssociationsRemovalService extends JobService { return false; } - static void schedule(Context context) { + /** + * Schedule this job. + */ + public static void schedule(Context context) { Slog.i(TAG, "Scheduling the Association Removal job"); final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class).forNamespace(JOB_NAMESPACE); diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java index 74236a402244..a08e0da90d49 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java +++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java @@ -52,8 +52,8 @@ import android.permission.PermissionControllerManager; import android.util.Slog; import com.android.internal.R; -import com.android.server.companion.AssociationStore; import com.android.server.companion.CompanionDeviceManagerService; +import com.android.server.companion.association.AssociationStore; import com.android.server.companion.transport.CompanionTransportManager; import com.android.server.companion.utils.PackageUtils; import com.android.server.companion.utils.PermissionsUtils; diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java index 2899c055afbd..99466a966647 100644 --- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java +++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java @@ -59,8 +59,8 @@ import android.os.Looper; import android.util.Log; import android.util.Slog; -import com.android.server.companion.AssociationStore; -import com.android.server.companion.AssociationStore.ChangeType; +import com.android.server.companion.association.AssociationStore; +import com.android.server.companion.association.AssociationStore.ChangeType; import java.util.ArrayList; import java.util.Arrays; diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java index 0287f6258c06..4da3f9bead4e 100644 --- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java +++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java @@ -39,7 +39,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; -import com.android.server.companion.AssociationStore; +import com.android.server.companion.association.AssociationStore; import java.util.Arrays; import java.util.Collections; diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java index 3da9693b75c9..37bbb937d1b5 100644 --- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java +++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java @@ -44,7 +44,7 @@ import android.util.SparseArray; import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; -import com.android.server.companion.AssociationStore; +import com.android.server.companion.association.AssociationStore; import java.io.PrintWriter; import java.util.HashSet; diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java index 3861f99eb03c..6dd14ac91a34 100644 --- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java +++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java @@ -32,7 +32,7 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; -import com.android.server.companion.AssociationStore; +import com.android.server.companion.association.AssociationStore; import java.io.FileDescriptor; import java.io.IOException; diff --git a/services/companion/java/com/android/server/companion/utils/AssociationUtils.java b/services/companion/java/com/android/server/companion/utils/AssociationUtils.java new file mode 100644 index 000000000000..e4d96413cf8b --- /dev/null +++ b/services/companion/java/com/android/server/companion/utils/AssociationUtils.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.companion.utils; + +import android.annotation.UserIdInt; + +public final class AssociationUtils { + + /** Range of Association IDs allocated for a user. */ + private static final int ASSOCIATIONS_IDS_PER_USER_RANGE = 100000; + + /** + * Get the left boundary of the association id range for the user. + */ + public static int getFirstAssociationIdForUser(@UserIdInt int userId) { + // We want the IDs to start from 1, not 0. + return userId * ASSOCIATIONS_IDS_PER_USER_RANGE + 1; + } + + /** + * Get the right boundary of the association id range for the user. + */ + public static int getLastAssociationIdForUser(@UserIdInt int userId) { + return (userId + 1) * ASSOCIATIONS_IDS_PER_USER_RANGE; + } + + private AssociationUtils() {} +} |