Loading core/api/test-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -954,6 +954,7 @@ package android.companion { method @NonNull public android.companion.AssociationInfo.Builder setDeviceProfile(@Nullable String); method @NonNull public android.companion.AssociationInfo.Builder setDisplayName(@Nullable CharSequence); method @NonNull public android.companion.AssociationInfo.Builder setLastTimeConnected(long); method @FlaggedApi("android.companion.enable_data_sync") @NonNull public android.companion.AssociationInfo.Builder setMetadata(@NonNull android.os.PersistableBundle); method @NonNull public android.companion.AssociationInfo.Builder setNotifyOnDeviceNearby(boolean); method @FlaggedApi("android.companion.association_verification") @NonNull public android.companion.AssociationInfo.Builder setPackagesToNotify(@Nullable java.util.List<java.lang.String>); method @NonNull public android.companion.AssociationInfo.Builder setRevoked(boolean); Loading core/java/android/companion/AssociationInfo.java +80 −4 Original line number Diff line number Diff line Loading @@ -22,10 +22,13 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.companion.CompanionDeviceManager.FeatureName; import android.graphics.drawable.Icon; import android.net.MacAddress; import android.os.BaseBundle; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; import java.util.Date; import java.util.List; Loading @@ -44,6 +47,11 @@ public final class AssociationInfo implements Parcelable { * A String indicates the selfManaged device is not connected. */ private static final String LAST_TIME_CONNECTED_NONE = "None"; /** * Key for the reception timestamp of the metadata. */ static final String METADATA_TIMESTAMP = "_timestamp_"; /** * A unique ID of this Association record. * Disclosed to the clients (i.e. companion applications) for referring to this record (e.g. in Loading Loading @@ -89,6 +97,11 @@ public final class AssociationInfo implements Parcelable { private final DeviceId mDeviceId; @Nullable private final List<String> mPackagesToNotify; /** * A map of metadata describing the device's data sync policies for each feature client. */ @NonNull private final PersistableBundle mMetadata; /** * A device icon displayed on a selfManaged association dialog. Loading @@ -106,7 +119,7 @@ public final class AssociationInfo implements Parcelable { boolean selfManaged, boolean notifyOnDeviceNearby, boolean revoked, boolean pending, long timeApprovedMs, long lastTimeConnectedMs, int systemDataSyncFlags, int transportFlags, @Nullable Icon deviceIcon, @Nullable DeviceId deviceId, @Nullable List<String> packagesToNotify) { @Nullable List<String> packagesToNotify, @Nullable PersistableBundle metadata) { if (id <= 0) { throw new IllegalArgumentException("Association ID should be greater than 0"); } Loading @@ -133,6 +146,7 @@ public final class AssociationInfo implements Parcelable { mDeviceIcon = deviceIcon; mDeviceId = deviceId; mPackagesToNotify = packagesToNotify; mMetadata = metadata; } /** Loading Loading @@ -322,6 +336,47 @@ public final class AssociationInfo implements Parcelable { return mPackagesToNotify; } /** * @return the metadata of the association. * @hide */ @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) @NonNull public PersistableBundle getMetadata() { return mMetadata; } /** * @return the metadata of the association for a given feature name. * If the metadata is not available, it returns a new empty bundle. * * @hide */ @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) @NonNull public PersistableBundle getMetadata(@NonNull @FeatureName String feature) { if (METADATA_TIMESTAMP.equals(feature)) { throw new IllegalArgumentException("Cannot get metadata for timestamp. " + "Use getMetadataTimestamp() instead to get the timestamp."); } PersistableBundle bundle = mMetadata.getPersistableBundle(feature); if (bundle == null) { return new PersistableBundle(); } return bundle; } /** * @return the timestamp at which the metadata was last received from the remote device. * If the metadata was never set, then it returns 0. * @hide */ @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) public long getMetadataTimestamp() { return mMetadata.getLong(METADATA_TIMESTAMP, 0L); } /** * Utility method for checking if the association represents a device with the given MAC * address. Loading Loading @@ -395,6 +450,7 @@ public final class AssociationInfo implements Parcelable { + ", mTransportFlags=" + mTransportFlags + ", mDeviceId=" + mDeviceId + ", mPackagesToNotify=" + mPackagesToNotify + ", mMetadata=" + mMetadata + '}'; } Loading @@ -421,7 +477,8 @@ public final class AssociationInfo implements Parcelable { && mTransportFlags == that.mTransportFlags && isSameIcon(mDeviceIcon, that.mDeviceIcon) && Objects.equals(mDeviceId, that.mDeviceId) && Objects.equals(mPackagesToNotify, that.mPackagesToNotify); && Objects.equals(mPackagesToNotify, that.mPackagesToNotify) && BaseBundle.kindofEquals(mMetadata, that.mMetadata); } private boolean isSameIcon(Icon iconA, Icon iconB) { Loading @@ -436,7 +493,7 @@ public final class AssociationInfo implements Parcelable { return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName, mDeviceProfile, mAssociatedDevice, mSelfManaged, mNotifyOnDeviceNearby, mRevoked, mPending, mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags, mTransportFlags, mDeviceIcon, mDeviceId, mPackagesToNotify); mTransportFlags, mDeviceIcon, mDeviceId, mPackagesToNotify, mMetadata); } @Override Loading Loading @@ -476,6 +533,7 @@ public final class AssociationInfo implements Parcelable { } dest.writeStringList(mPackagesToNotify); dest.writePersistableBundle(mMetadata); } private AssociationInfo(@NonNull Parcel in) { Loading Loading @@ -507,6 +565,7 @@ public final class AssociationInfo implements Parcelable { mDeviceId = null; } mPackagesToNotify = in.createStringArrayList(); mMetadata = in.readPersistableBundle(); } @NonNull Loading Loading @@ -548,6 +607,7 @@ public final class AssociationInfo implements Parcelable { private Icon mDeviceIcon; private DeviceId mDeviceId; private List<String> mPackagesToNotify; private PersistableBundle mMetadata = new PersistableBundle(); // Empty bundle by default. /** @hide */ @TestApi Loading Loading @@ -578,6 +638,7 @@ public final class AssociationInfo implements Parcelable { mDeviceIcon = info.mDeviceIcon; mDeviceId = info.mDeviceId; mPackagesToNotify = info.mPackagesToNotify; mMetadata = info.mMetadata; } /** Loading Loading @@ -605,6 +666,7 @@ public final class AssociationInfo implements Parcelable { mDeviceIcon = info.mDeviceIcon; mDeviceId = info.mDeviceId; mPackagesToNotify = info.mPackagesToNotify; mMetadata = info.mMetadata; } /** @hide */ Loading Loading @@ -746,6 +808,16 @@ public final class AssociationInfo implements Parcelable { return this; } /** @hide */ @TestApi @NonNull @SuppressLint("MissingGetterMatchingBuilder") @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) public Builder setMetadata(@NonNull PersistableBundle metadata) { mMetadata = metadata; return this; } /** @hide */ @TestApi @NonNull Loading @@ -757,6 +829,9 @@ public final class AssociationInfo implements Parcelable { throw new IllegalArgumentException("MAC address and the display name must NOT be " + "null at the same time"); } if (mMetadata == null) { throw new IllegalArgumentException("Association metadata cannot be null"); } return new AssociationInfo( mId, mUserId, Loading @@ -775,7 +850,8 @@ public final class AssociationInfo implements Parcelable { mTransportFlags, mDeviceIcon, mDeviceId, mPackagesToNotify mPackagesToNotify, mMetadata ); } } Loading core/java/android/companion/CompanionDeviceManager.java +65 −2 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMIN import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION; import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER; import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH; import static android.companion.AssociationInfo.METADATA_TIMESTAMP; import static android.graphics.drawable.Icon.TYPE_URI; import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP; Loading @@ -31,6 +32,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.StringDef; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; Loading Loading @@ -61,6 +63,7 @@ import android.os.Binder; import android.os.Handler; import android.os.OutcomeReceiver; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.UserHandle; import android.service.notification.NotificationListenerService; Loading Loading @@ -219,6 +222,36 @@ public final class CompanionDeviceManager { @FlaggedApi(Flags.FLAG_ENABLE_TASK_CONTINUITY) public static final int FLAG_TASK_CONTINUITY = 2; /** * The feature name for task continuity manager. * @hide */ @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) public static final String FEATURE_TASK_CONTINUITY = "task_continuity_manager"; /** * The feature name for the mode Sync. * @hide */ @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) public static final String FEATURE_MODE_SYNC = "mode_sync"; /** * The feature name for airplane mode sync. * @hide */ @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) public static final String FEATURE_AIRPLANE_MODE_SYNC = "airplane_mode_sync"; /** @hide */ @StringDef(prefix = { "FEATURE_" }, value = { FEATURE_TASK_CONTINUITY, FEATURE_MODE_SYNC, FEATURE_AIRPLANE_MODE_SYNC, }) @Retention(RetentionPolicy.SOURCE) public @interface FeatureName {} /** * A device, returned in the activity result of the {@link IntentSender} received in * {@link Callback#onDeviceFound} Loading Loading @@ -612,7 +645,7 @@ public final class CompanionDeviceManager { } /** * <p>Enable system data sync. * <p>Enable system data sync (it only supports call metadata sync for now). * By default all supported system data types are enabled.</p> * * <p>Calling this API requires a uses-feature Loading @@ -635,7 +668,7 @@ public final class CompanionDeviceManager { } /** * <p>Disable system data sync. * <p>Disable system data sync (it only supports call metadata sync for now). * By default all supported system data types are enabled.</p> * * <p>Calling this API requires a uses-feature Loading Loading @@ -1975,6 +2008,36 @@ public final class CompanionDeviceManager { } } /** * Sets the metadata for this device. If the metadata for the feature is already set, then * it will be overwritten. The client feature service can use this metadata to store * feature-relevant data to be shared with associated devices. * * @param userId The user id of the user for which this metadata is set. * @param feature The feature name for this metadata. * @param value The bundle containing feature-relevant metadata. * * @hide */ @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) public void setLocalMetadata(@UserIdInt int userId, @NonNull @FeatureName String feature, @Nullable PersistableBundle value) { if (mService == null) { Log.w(TAG, "CompanionDeviceManager service is not available."); return; } if (METADATA_TIMESTAMP.equals(feature)) { throw new IllegalArgumentException("Cannot set metadata timestamp"); } try { mService.setLocalMetadata(userId, feature, value); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } private static class AssociationRequestCallbackProxy extends IAssociationRequestCallback.Stub { private final Handler mHandler; private final Callback mCallback; Loading core/java/android/companion/ICompanionDeviceManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.companion.ObservingDevicePresenceRequest; import android.companion.datatransfer.PermissionSyncRequest; import android.content.ComponentName; import android.os.ParcelUuid; import android.os.PersistableBundle; import android.companion.DeviceId; Loading Loading @@ -153,4 +154,6 @@ interface ICompanionDeviceManager { AssociationInfo getAssociationByDeviceId(int userId, in DeviceId deviceId); DeviceId setDeviceId(int associationId, in DeviceId deviceId); void setLocalMetadata(int userId, String key, in PersistableBundle value); } services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +31 −15 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ import android.net.MacAddress; import android.os.Binder; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.PowerExemptionManager; import android.os.PowerManagerInternal; import android.os.RemoteException; Loading @@ -98,6 +99,8 @@ import com.android.server.companion.association.AssociationRequestsProcessor; import com.android.server.companion.association.AssociationStore; import com.android.server.companion.association.DisassociationProcessor; import com.android.server.companion.association.InactiveAssociationsRemovalService; import com.android.server.companion.datasync.DataSyncProcessor; import com.android.server.companion.datasync.LocalMetadataStore; import com.android.server.companion.datatransfer.SystemDataTransferProcessor; import com.android.server.companion.datatransfer.SystemDataTransferRequestStore; import com.android.server.companion.datatransfer.contextsync.CrossDeviceCall; Loading Loading @@ -137,6 +140,8 @@ public class CompanionDeviceManagerService extends SystemService { private final CompanionTransportManager mTransportManager; private final DisassociationProcessor mDisassociationProcessor; private final CrossDeviceSyncController mCrossDeviceSyncController; private final LocalMetadataStore mLocalMetadataStore; private final DataSyncProcessor mDataSyncProcessor; private final Object mPackageLock = new Object(); Loading @@ -163,6 +168,7 @@ public class CompanionDeviceManagerService extends SystemService { mAssociationStore = new AssociationStore(context, userManager, associationDiskStore); mSystemDataTransferRequestStore = new SystemDataTransferRequestStore(); mObservableUuidStore = new ObservableUuidStore(); mLocalMetadataStore = new LocalMetadataStore(); // Init processors mAssociationRequestsProcessor = new AssociationRequestsProcessor(context, Loading Loading @@ -192,6 +198,8 @@ public class CompanionDeviceManagerService extends SystemService { packageManagerInternal, mAssociationStore, mSystemDataTransferRequestStore, mTransportManager); mDataSyncProcessor = new DataSyncProcessor(mAssociationStore, mLocalMetadataStore); // TODO(b/279663946): move context sync to a dedicated system service mCrossDeviceSyncController = new CrossDeviceSyncController(getContext(), mTransportManager); } Loading Loading @@ -647,25 +655,22 @@ public class CompanionDeviceManagerService extends SystemService { @Override public void enablePermissionsSync(int associationId) { if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) { throw new SecurityException("Caller must be system UID"); } enforceCallerIsSystem(); mSystemDataTransferProcessor.enablePermissionsSync(associationId); } @Override public void disablePermissionsSync(int associationId) { if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) { throw new SecurityException("Caller must be system UID"); } enforceCallerIsSystem(); mSystemDataTransferProcessor.disablePermissionsSync(associationId); } @Override public PermissionSyncRequest getPermissionSyncRequest(int associationId) { if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) { throw new SecurityException("Caller must be system UID"); } enforceCallerIsSystem(); return mSystemDataTransferProcessor.getPermissionSyncRequest(associationId); } Loading Loading @@ -734,6 +739,12 @@ public class CompanionDeviceManagerService extends SystemService { } } private void enforceCallerIsSystem() { if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) { throw new SecurityException("Caller must be system UID"); } } @Override public boolean canPairWithoutPrompt(String packageName, String macAddress, int userId) { final AssociationInfo association = Loading @@ -760,18 +771,23 @@ public class CompanionDeviceManagerService extends SystemService { } @Override public byte[] getBackupPayload(int userId) { if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) { throw new SecurityException("Caller must be system"); public void setLocalMetadata(int userId, String key, PersistableBundle value) { enforceCallerIsSystem(); mDataSyncProcessor.setLocalMetadata(userId, key, value); } @Override public byte[] getBackupPayload(int userId) { enforceCallerIsSystem(); return mBackupRestoreProcessor.getBackupPayload(userId); } @Override public void applyRestoredPayload(byte[] payload, int userId) { if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) { throw new SecurityException("Caller must be system"); } enforceCallerIsSystem(); mBackupRestoreProcessor.applyRestoredPayload(payload, userId); } Loading Loading
core/api/test-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -954,6 +954,7 @@ package android.companion { method @NonNull public android.companion.AssociationInfo.Builder setDeviceProfile(@Nullable String); method @NonNull public android.companion.AssociationInfo.Builder setDisplayName(@Nullable CharSequence); method @NonNull public android.companion.AssociationInfo.Builder setLastTimeConnected(long); method @FlaggedApi("android.companion.enable_data_sync") @NonNull public android.companion.AssociationInfo.Builder setMetadata(@NonNull android.os.PersistableBundle); method @NonNull public android.companion.AssociationInfo.Builder setNotifyOnDeviceNearby(boolean); method @FlaggedApi("android.companion.association_verification") @NonNull public android.companion.AssociationInfo.Builder setPackagesToNotify(@Nullable java.util.List<java.lang.String>); method @NonNull public android.companion.AssociationInfo.Builder setRevoked(boolean); Loading
core/java/android/companion/AssociationInfo.java +80 −4 Original line number Diff line number Diff line Loading @@ -22,10 +22,13 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.companion.CompanionDeviceManager.FeatureName; import android.graphics.drawable.Icon; import android.net.MacAddress; import android.os.BaseBundle; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; import java.util.Date; import java.util.List; Loading @@ -44,6 +47,11 @@ public final class AssociationInfo implements Parcelable { * A String indicates the selfManaged device is not connected. */ private static final String LAST_TIME_CONNECTED_NONE = "None"; /** * Key for the reception timestamp of the metadata. */ static final String METADATA_TIMESTAMP = "_timestamp_"; /** * A unique ID of this Association record. * Disclosed to the clients (i.e. companion applications) for referring to this record (e.g. in Loading Loading @@ -89,6 +97,11 @@ public final class AssociationInfo implements Parcelable { private final DeviceId mDeviceId; @Nullable private final List<String> mPackagesToNotify; /** * A map of metadata describing the device's data sync policies for each feature client. */ @NonNull private final PersistableBundle mMetadata; /** * A device icon displayed on a selfManaged association dialog. Loading @@ -106,7 +119,7 @@ public final class AssociationInfo implements Parcelable { boolean selfManaged, boolean notifyOnDeviceNearby, boolean revoked, boolean pending, long timeApprovedMs, long lastTimeConnectedMs, int systemDataSyncFlags, int transportFlags, @Nullable Icon deviceIcon, @Nullable DeviceId deviceId, @Nullable List<String> packagesToNotify) { @Nullable List<String> packagesToNotify, @Nullable PersistableBundle metadata) { if (id <= 0) { throw new IllegalArgumentException("Association ID should be greater than 0"); } Loading @@ -133,6 +146,7 @@ public final class AssociationInfo implements Parcelable { mDeviceIcon = deviceIcon; mDeviceId = deviceId; mPackagesToNotify = packagesToNotify; mMetadata = metadata; } /** Loading Loading @@ -322,6 +336,47 @@ public final class AssociationInfo implements Parcelable { return mPackagesToNotify; } /** * @return the metadata of the association. * @hide */ @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) @NonNull public PersistableBundle getMetadata() { return mMetadata; } /** * @return the metadata of the association for a given feature name. * If the metadata is not available, it returns a new empty bundle. * * @hide */ @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) @NonNull public PersistableBundle getMetadata(@NonNull @FeatureName String feature) { if (METADATA_TIMESTAMP.equals(feature)) { throw new IllegalArgumentException("Cannot get metadata for timestamp. " + "Use getMetadataTimestamp() instead to get the timestamp."); } PersistableBundle bundle = mMetadata.getPersistableBundle(feature); if (bundle == null) { return new PersistableBundle(); } return bundle; } /** * @return the timestamp at which the metadata was last received from the remote device. * If the metadata was never set, then it returns 0. * @hide */ @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) public long getMetadataTimestamp() { return mMetadata.getLong(METADATA_TIMESTAMP, 0L); } /** * Utility method for checking if the association represents a device with the given MAC * address. Loading Loading @@ -395,6 +450,7 @@ public final class AssociationInfo implements Parcelable { + ", mTransportFlags=" + mTransportFlags + ", mDeviceId=" + mDeviceId + ", mPackagesToNotify=" + mPackagesToNotify + ", mMetadata=" + mMetadata + '}'; } Loading @@ -421,7 +477,8 @@ public final class AssociationInfo implements Parcelable { && mTransportFlags == that.mTransportFlags && isSameIcon(mDeviceIcon, that.mDeviceIcon) && Objects.equals(mDeviceId, that.mDeviceId) && Objects.equals(mPackagesToNotify, that.mPackagesToNotify); && Objects.equals(mPackagesToNotify, that.mPackagesToNotify) && BaseBundle.kindofEquals(mMetadata, that.mMetadata); } private boolean isSameIcon(Icon iconA, Icon iconB) { Loading @@ -436,7 +493,7 @@ public final class AssociationInfo implements Parcelable { return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName, mDeviceProfile, mAssociatedDevice, mSelfManaged, mNotifyOnDeviceNearby, mRevoked, mPending, mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags, mTransportFlags, mDeviceIcon, mDeviceId, mPackagesToNotify); mTransportFlags, mDeviceIcon, mDeviceId, mPackagesToNotify, mMetadata); } @Override Loading Loading @@ -476,6 +533,7 @@ public final class AssociationInfo implements Parcelable { } dest.writeStringList(mPackagesToNotify); dest.writePersistableBundle(mMetadata); } private AssociationInfo(@NonNull Parcel in) { Loading Loading @@ -507,6 +565,7 @@ public final class AssociationInfo implements Parcelable { mDeviceId = null; } mPackagesToNotify = in.createStringArrayList(); mMetadata = in.readPersistableBundle(); } @NonNull Loading Loading @@ -548,6 +607,7 @@ public final class AssociationInfo implements Parcelable { private Icon mDeviceIcon; private DeviceId mDeviceId; private List<String> mPackagesToNotify; private PersistableBundle mMetadata = new PersistableBundle(); // Empty bundle by default. /** @hide */ @TestApi Loading Loading @@ -578,6 +638,7 @@ public final class AssociationInfo implements Parcelable { mDeviceIcon = info.mDeviceIcon; mDeviceId = info.mDeviceId; mPackagesToNotify = info.mPackagesToNotify; mMetadata = info.mMetadata; } /** Loading Loading @@ -605,6 +666,7 @@ public final class AssociationInfo implements Parcelable { mDeviceIcon = info.mDeviceIcon; mDeviceId = info.mDeviceId; mPackagesToNotify = info.mPackagesToNotify; mMetadata = info.mMetadata; } /** @hide */ Loading Loading @@ -746,6 +808,16 @@ public final class AssociationInfo implements Parcelable { return this; } /** @hide */ @TestApi @NonNull @SuppressLint("MissingGetterMatchingBuilder") @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) public Builder setMetadata(@NonNull PersistableBundle metadata) { mMetadata = metadata; return this; } /** @hide */ @TestApi @NonNull Loading @@ -757,6 +829,9 @@ public final class AssociationInfo implements Parcelable { throw new IllegalArgumentException("MAC address and the display name must NOT be " + "null at the same time"); } if (mMetadata == null) { throw new IllegalArgumentException("Association metadata cannot be null"); } return new AssociationInfo( mId, mUserId, Loading @@ -775,7 +850,8 @@ public final class AssociationInfo implements Parcelable { mTransportFlags, mDeviceIcon, mDeviceId, mPackagesToNotify mPackagesToNotify, mMetadata ); } } Loading
core/java/android/companion/CompanionDeviceManager.java +65 −2 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMIN import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION; import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER; import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH; import static android.companion.AssociationInfo.METADATA_TIMESTAMP; import static android.graphics.drawable.Icon.TYPE_URI; import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP; Loading @@ -31,6 +32,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.StringDef; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; Loading Loading @@ -61,6 +63,7 @@ import android.os.Binder; import android.os.Handler; import android.os.OutcomeReceiver; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.UserHandle; import android.service.notification.NotificationListenerService; Loading Loading @@ -219,6 +222,36 @@ public final class CompanionDeviceManager { @FlaggedApi(Flags.FLAG_ENABLE_TASK_CONTINUITY) public static final int FLAG_TASK_CONTINUITY = 2; /** * The feature name for task continuity manager. * @hide */ @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) public static final String FEATURE_TASK_CONTINUITY = "task_continuity_manager"; /** * The feature name for the mode Sync. * @hide */ @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) public static final String FEATURE_MODE_SYNC = "mode_sync"; /** * The feature name for airplane mode sync. * @hide */ @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) public static final String FEATURE_AIRPLANE_MODE_SYNC = "airplane_mode_sync"; /** @hide */ @StringDef(prefix = { "FEATURE_" }, value = { FEATURE_TASK_CONTINUITY, FEATURE_MODE_SYNC, FEATURE_AIRPLANE_MODE_SYNC, }) @Retention(RetentionPolicy.SOURCE) public @interface FeatureName {} /** * A device, returned in the activity result of the {@link IntentSender} received in * {@link Callback#onDeviceFound} Loading Loading @@ -612,7 +645,7 @@ public final class CompanionDeviceManager { } /** * <p>Enable system data sync. * <p>Enable system data sync (it only supports call metadata sync for now). * By default all supported system data types are enabled.</p> * * <p>Calling this API requires a uses-feature Loading @@ -635,7 +668,7 @@ public final class CompanionDeviceManager { } /** * <p>Disable system data sync. * <p>Disable system data sync (it only supports call metadata sync for now). * By default all supported system data types are enabled.</p> * * <p>Calling this API requires a uses-feature Loading Loading @@ -1975,6 +2008,36 @@ public final class CompanionDeviceManager { } } /** * Sets the metadata for this device. If the metadata for the feature is already set, then * it will be overwritten. The client feature service can use this metadata to store * feature-relevant data to be shared with associated devices. * * @param userId The user id of the user for which this metadata is set. * @param feature The feature name for this metadata. * @param value The bundle containing feature-relevant metadata. * * @hide */ @FlaggedApi(Flags.FLAG_ENABLE_DATA_SYNC) public void setLocalMetadata(@UserIdInt int userId, @NonNull @FeatureName String feature, @Nullable PersistableBundle value) { if (mService == null) { Log.w(TAG, "CompanionDeviceManager service is not available."); return; } if (METADATA_TIMESTAMP.equals(feature)) { throw new IllegalArgumentException("Cannot set metadata timestamp"); } try { mService.setLocalMetadata(userId, feature, value); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } private static class AssociationRequestCallbackProxy extends IAssociationRequestCallback.Stub { private final Handler mHandler; private final Callback mCallback; Loading
core/java/android/companion/ICompanionDeviceManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.companion.ObservingDevicePresenceRequest; import android.companion.datatransfer.PermissionSyncRequest; import android.content.ComponentName; import android.os.ParcelUuid; import android.os.PersistableBundle; import android.companion.DeviceId; Loading Loading @@ -153,4 +154,6 @@ interface ICompanionDeviceManager { AssociationInfo getAssociationByDeviceId(int userId, in DeviceId deviceId); DeviceId setDeviceId(int associationId, in DeviceId deviceId); void setLocalMetadata(int userId, String key, in PersistableBundle value); }
services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +31 −15 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ import android.net.MacAddress; import android.os.Binder; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.PowerExemptionManager; import android.os.PowerManagerInternal; import android.os.RemoteException; Loading @@ -98,6 +99,8 @@ import com.android.server.companion.association.AssociationRequestsProcessor; import com.android.server.companion.association.AssociationStore; import com.android.server.companion.association.DisassociationProcessor; import com.android.server.companion.association.InactiveAssociationsRemovalService; import com.android.server.companion.datasync.DataSyncProcessor; import com.android.server.companion.datasync.LocalMetadataStore; import com.android.server.companion.datatransfer.SystemDataTransferProcessor; import com.android.server.companion.datatransfer.SystemDataTransferRequestStore; import com.android.server.companion.datatransfer.contextsync.CrossDeviceCall; Loading Loading @@ -137,6 +140,8 @@ public class CompanionDeviceManagerService extends SystemService { private final CompanionTransportManager mTransportManager; private final DisassociationProcessor mDisassociationProcessor; private final CrossDeviceSyncController mCrossDeviceSyncController; private final LocalMetadataStore mLocalMetadataStore; private final DataSyncProcessor mDataSyncProcessor; private final Object mPackageLock = new Object(); Loading @@ -163,6 +168,7 @@ public class CompanionDeviceManagerService extends SystemService { mAssociationStore = new AssociationStore(context, userManager, associationDiskStore); mSystemDataTransferRequestStore = new SystemDataTransferRequestStore(); mObservableUuidStore = new ObservableUuidStore(); mLocalMetadataStore = new LocalMetadataStore(); // Init processors mAssociationRequestsProcessor = new AssociationRequestsProcessor(context, Loading Loading @@ -192,6 +198,8 @@ public class CompanionDeviceManagerService extends SystemService { packageManagerInternal, mAssociationStore, mSystemDataTransferRequestStore, mTransportManager); mDataSyncProcessor = new DataSyncProcessor(mAssociationStore, mLocalMetadataStore); // TODO(b/279663946): move context sync to a dedicated system service mCrossDeviceSyncController = new CrossDeviceSyncController(getContext(), mTransportManager); } Loading Loading @@ -647,25 +655,22 @@ public class CompanionDeviceManagerService extends SystemService { @Override public void enablePermissionsSync(int associationId) { if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) { throw new SecurityException("Caller must be system UID"); } enforceCallerIsSystem(); mSystemDataTransferProcessor.enablePermissionsSync(associationId); } @Override public void disablePermissionsSync(int associationId) { if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) { throw new SecurityException("Caller must be system UID"); } enforceCallerIsSystem(); mSystemDataTransferProcessor.disablePermissionsSync(associationId); } @Override public PermissionSyncRequest getPermissionSyncRequest(int associationId) { if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) { throw new SecurityException("Caller must be system UID"); } enforceCallerIsSystem(); return mSystemDataTransferProcessor.getPermissionSyncRequest(associationId); } Loading Loading @@ -734,6 +739,12 @@ public class CompanionDeviceManagerService extends SystemService { } } private void enforceCallerIsSystem() { if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) { throw new SecurityException("Caller must be system UID"); } } @Override public boolean canPairWithoutPrompt(String packageName, String macAddress, int userId) { final AssociationInfo association = Loading @@ -760,18 +771,23 @@ public class CompanionDeviceManagerService extends SystemService { } @Override public byte[] getBackupPayload(int userId) { if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) { throw new SecurityException("Caller must be system"); public void setLocalMetadata(int userId, String key, PersistableBundle value) { enforceCallerIsSystem(); mDataSyncProcessor.setLocalMetadata(userId, key, value); } @Override public byte[] getBackupPayload(int userId) { enforceCallerIsSystem(); return mBackupRestoreProcessor.getBackupPayload(userId); } @Override public void applyRestoredPayload(byte[] payload, int userId) { if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) { throw new SecurityException("Caller must be system"); } enforceCallerIsSystem(); mBackupRestoreProcessor.applyRestoredPayload(payload, userId); } Loading