diff --git a/core/api/current.txt b/core/api/current.txt index 815ccfe0b1c62..967645158d23b 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -8947,6 +8947,7 @@ package android.companion { public final class AssociationInfo implements android.os.Parcelable { method public int describeContents(); + method @Nullable public android.os.Parcelable getAssociatedDevice(); method @Nullable public android.net.MacAddress getDeviceMacAddress(); method @Nullable public String getDeviceProfile(); method @Nullable public CharSequence getDisplayName(); diff --git a/core/java/android/companion/AssociatedDevice.java b/core/java/android/companion/AssociatedDevice.java new file mode 100644 index 0000000000000..3758cdb680b10 --- /dev/null +++ b/core/java/android/companion/AssociatedDevice.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2022 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 android.companion; + +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * Loose wrapper around device parcelable. Device can be one of three types: + * + * + * + * This class serves as temporary wrapper to deliver a loosely-typed parcelable object from + * {@link com.android.companiondevicemanager.CompanionDeviceActivity} to the Companion app, + * and should only be used internally. + * + * @hide + */ +public final class AssociatedDevice implements Parcelable { + private static final int CLASSIC_BLUETOOTH = 0; + private static final int BLUETOOTH_LE = 1; + private static final int WIFI = 2; + + @NonNull private final Parcelable mDevice; + + public AssociatedDevice(@NonNull Parcelable device) { + mDevice = device; + } + + private AssociatedDevice(Parcel in) { + Creator creator = getDeviceCreator(in.readInt()); + mDevice = creator.createFromParcel(in); + } + + /** + * Return device info. Cast to expected device type. + */ + @NonNull + public Parcelable getDevice() { + return mDevice; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + // Parcel device type with int for efficiency + dest.writeInt(getDeviceType()); + mDevice.writeToParcel(dest, flags); + } + + @Override + public int describeContents() { + return 0; + } + + private int getDeviceType() { + if (mDevice instanceof android.bluetooth.BluetoothDevice) return CLASSIC_BLUETOOTH; + if (mDevice instanceof android.bluetooth.le.ScanResult) return BLUETOOTH_LE; + if (mDevice instanceof android.net.wifi.ScanResult) return WIFI; + throw new UnsupportedOperationException("Unsupported device type."); + } + + private static Creator getDeviceCreator(int deviceType) { + switch (deviceType) { + case CLASSIC_BLUETOOTH: return android.bluetooth.BluetoothDevice.CREATOR; + case BLUETOOTH_LE: return android.bluetooth.le.ScanResult.CREATOR; + case WIFI: return android.net.wifi.ScanResult.CREATOR; + default: throw new UnsupportedOperationException("Unsupported device type."); + } + } + + @NonNull + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public AssociatedDevice[] newArray(int size) { + return new AssociatedDevice[size]; + } + + @Override + public AssociatedDevice createFromParcel(@NonNull Parcel in) { + return new AssociatedDevice(in); + } + }; + + @Override + public String toString() { + return "AssociatedDevice { " + + "device = " + mDevice + + " }"; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AssociatedDevice that = (AssociatedDevice) o; + if (getDeviceType() != that.getDeviceType()) return false; + + // TODO(b/31972115): Take out this whole part ¯\_(ツ)_/¯ + if (mDevice instanceof android.bluetooth.le.ScanResult + || mDevice instanceof android.net.wifi.ScanResult) { + return mDevice.toString().equals(that.mDevice.toString()); + } + + return java.util.Objects.equals(mDevice, that.mDevice); + } + + @Override + public int hashCode() { + return java.util.Objects.hash(mDevice); + } +} diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java index 93748f81ffa16..93964b3f41808 100644 --- a/core/java/android/companion/AssociationInfo.java +++ b/core/java/android/companion/AssociationInfo.java @@ -52,6 +52,7 @@ public final class AssociationInfo implements Parcelable { private final @Nullable MacAddress mDeviceMacAddress; private final @Nullable CharSequence mDisplayName; private final @Nullable String mDeviceProfile; + private final @Nullable AssociatedDevice mAssociatedDevice; private final boolean mSelfManaged; private final boolean mNotifyOnDeviceNearby; @@ -78,8 +79,9 @@ public final class AssociationInfo implements Parcelable { */ public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName, @Nullable MacAddress macAddress, @Nullable CharSequence displayName, - @Nullable String deviceProfile, boolean selfManaged, boolean notifyOnDeviceNearby, - boolean revoked, long timeApprovedMs, long lastTimeConnectedMs) { + @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice, + boolean selfManaged, boolean notifyOnDeviceNearby, boolean revoked, + long timeApprovedMs, long lastTimeConnectedMs) { if (id <= 0) { throw new IllegalArgumentException("Association ID should be greater than 0"); } @@ -96,6 +98,7 @@ public final class AssociationInfo implements Parcelable { mDeviceMacAddress = macAddress; mDisplayName = displayName; mDeviceProfile = deviceProfile; + mAssociatedDevice = associatedDevice; mSelfManaged = selfManaged; mNotifyOnDeviceNearby = notifyOnDeviceNearby; @@ -159,6 +162,24 @@ public final class AssociationInfo implements Parcelable { return mDeviceProfile; } + /** + * Companion device that was associated. Note that this field is not persisted across sessions. + * + * Cast to expected device type before use: + * + * + * + * @return the companion device that was associated, or {@code null} if the device is + * self-managed. + */ + public @Nullable Parcelable getAssociatedDevice() { + return mAssociatedDevice == null ? null : mAssociatedDevice.getDevice(); + } + /** * @return whether the association is managed by the companion application it belongs to. * @see AssociationRequest.Builder#setSelfManaged(boolean) @@ -260,6 +281,7 @@ public final class AssociationInfo implements Parcelable { + ", mDisplayName='" + mDisplayName + '\'' + ", mDeviceProfile='" + mDeviceProfile + '\'' + ", mSelfManaged=" + mSelfManaged + + ", mAssociatedDevice=" + mAssociatedDevice + ", mNotifyOnDeviceNearby=" + mNotifyOnDeviceNearby + ", mRevoked=" + mRevoked + ", mTimeApprovedMs=" + new Date(mTimeApprovedMs) @@ -284,14 +306,15 @@ public final class AssociationInfo implements Parcelable { && Objects.equals(mPackageName, that.mPackageName) && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress) && Objects.equals(mDisplayName, that.mDisplayName) - && Objects.equals(mDeviceProfile, that.mDeviceProfile); + && Objects.equals(mDeviceProfile, that.mDeviceProfile) + && Objects.equals(mAssociatedDevice, that.mAssociatedDevice); } @Override public int hashCode() { return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName, - mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mRevoked, mTimeApprovedMs, - mLastTimeConnectedMs); + mDeviceProfile, mAssociatedDevice, mSelfManaged, mNotifyOnDeviceNearby, mRevoked, + mTimeApprovedMs, mLastTimeConnectedMs); } @Override @@ -309,6 +332,7 @@ public final class AssociationInfo implements Parcelable { dest.writeTypedObject(mDeviceMacAddress, 0); dest.writeCharSequence(mDisplayName); dest.writeString(mDeviceProfile); + dest.writeTypedObject(mAssociatedDevice, 0); dest.writeBoolean(mSelfManaged); dest.writeBoolean(mNotifyOnDeviceNearby); @@ -326,6 +350,7 @@ public final class AssociationInfo implements Parcelable { mDeviceMacAddress = in.readTypedObject(MacAddress.CREATOR); mDisplayName = in.readCharSequence(); mDeviceProfile = in.readString(); + mAssociatedDevice = in.readTypedObject(AssociatedDevice.CREATOR); mSelfManaged = in.readBoolean(); mNotifyOnDeviceNearby = in.readBoolean(); @@ -433,6 +458,7 @@ public final class AssociationInfo implements Parcelable { mOriginalInfo.mDeviceMacAddress, mOriginalInfo.mDisplayName, mOriginalInfo.mDeviceProfile, + mOriginalInfo.mAssociatedDevice, mOriginalInfo.mSelfManaged, mNotifyOnDeviceNearby, mRevoked, diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java index 75ab115315956..a2277e97c043e 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -153,6 +153,11 @@ public final class AssociationRequest implements Parcelable { */ private @Nullable CharSequence mDisplayName; + /** + * The device that was associated. Will be null for "self-managed" association. + */ + private @Nullable AssociatedDevice mAssociatedDevice; + /** * Whether the association is to be managed by the companion application. */ @@ -306,6 +311,11 @@ public final class AssociationRequest implements Parcelable { mDisplayName = displayName; } + /** @hide */ + public void setAssociatedDevice(AssociatedDevice associatedDevice) { + mAssociatedDevice = associatedDevice; + } + /** @hide */ @NonNull @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -438,6 +448,16 @@ public final class AssociationRequest implements Parcelable { //@formatter:off + /** + * The device that was associated. Will be null for "self-managed" association. + * + * @hide + */ + @DataClass.Generated.Member + public @Nullable AssociatedDevice getAssociatedDevice() { + return mAssociatedDevice; + } + /** * The app package name of the application the association will belong to. * Populated by the system. @@ -503,6 +523,7 @@ public final class AssociationRequest implements Parcelable { "deviceFilters = " + mDeviceFilters + ", " + "deviceProfile = " + mDeviceProfile + ", " + "displayName = " + mDisplayName + ", " + + "associatedDevice = " + mAssociatedDevice + ", " + "selfManaged = " + mSelfManaged + ", " + "forceConfirmation = " + mForceConfirmation + ", " + "packageName = " + mPackageName + ", " + @@ -530,6 +551,7 @@ public final class AssociationRequest implements Parcelable { && Objects.equals(mDeviceFilters, that.mDeviceFilters) && Objects.equals(mDeviceProfile, that.mDeviceProfile) && Objects.equals(mDisplayName, that.mDisplayName) + && Objects.equals(mAssociatedDevice, that.mAssociatedDevice) && mSelfManaged == that.mSelfManaged && mForceConfirmation == that.mForceConfirmation && Objects.equals(mPackageName, that.mPackageName) @@ -550,6 +572,7 @@ public final class AssociationRequest implements Parcelable { _hash = 31 * _hash + Objects.hashCode(mDeviceFilters); _hash = 31 * _hash + Objects.hashCode(mDeviceProfile); _hash = 31 * _hash + Objects.hashCode(mDisplayName); + _hash = 31 * _hash + Objects.hashCode(mAssociatedDevice); _hash = 31 * _hash + Boolean.hashCode(mSelfManaged); _hash = 31 * _hash + Boolean.hashCode(mForceConfirmation); _hash = 31 * _hash + Objects.hashCode(mPackageName); @@ -568,17 +591,19 @@ public final class AssociationRequest implements Parcelable { int flg = 0; if (mSingleDevice) flg |= 0x1; - if (mSelfManaged) flg |= 0x10; - if (mForceConfirmation) flg |= 0x20; - if (mSkipPrompt) flg |= 0x400; + if (mSelfManaged) flg |= 0x20; + if (mForceConfirmation) flg |= 0x40; + if (mSkipPrompt) flg |= 0x800; if (mDeviceProfile != null) flg |= 0x4; if (mDisplayName != null) flg |= 0x8; - if (mPackageName != null) flg |= 0x40; - if (mDeviceProfilePrivilegesDescription != null) flg |= 0x100; + if (mAssociatedDevice != null) flg |= 0x10; + if (mPackageName != null) flg |= 0x80; + if (mDeviceProfilePrivilegesDescription != null) flg |= 0x200; dest.writeInt(flg); dest.writeParcelableList(mDeviceFilters, flags); if (mDeviceProfile != null) dest.writeString(mDeviceProfile); if (mDisplayName != null) dest.writeCharSequence(mDisplayName); + if (mAssociatedDevice != null) dest.writeTypedObject(mAssociatedDevice, flags); if (mPackageName != null) dest.writeString(mPackageName); dest.writeInt(mUserId); if (mDeviceProfilePrivilegesDescription != null) dest.writeString(mDeviceProfilePrivilegesDescription); @@ -598,18 +623,20 @@ public final class AssociationRequest implements Parcelable { int flg = in.readInt(); boolean singleDevice = (flg & 0x1) != 0; - boolean selfManaged = (flg & 0x10) != 0; - boolean forceConfirmation = (flg & 0x20) != 0; - boolean skipPrompt = (flg & 0x400) != 0; + boolean selfManaged = (flg & 0x20) != 0; + boolean forceConfirmation = (flg & 0x40) != 0; + boolean skipPrompt = (flg & 0x800) != 0; List> deviceFilters = new ArrayList<>(); in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader(), (Class>) (Class) android.companion.DeviceFilter.class); String deviceProfile = (flg & 0x4) == 0 ? null : in.readString(); CharSequence displayName = (flg & 0x8) == 0 ? null : (CharSequence) in.readCharSequence(); - String packageName = (flg & 0x40) == 0 ? null : in.readString(); + AssociatedDevice associatedDevice = (flg & 0x10) == 0 + ? null : (AssociatedDevice) in.readTypedObject(AssociatedDevice.CREATOR); + String packageName = (flg & 0x80) == 0 ? null : in.readString(); int userId = in.readInt(); - String deviceProfilePrivilegesDescription = (flg & 0x100) == 0 ? null : in.readString(); + String deviceProfilePrivilegesDescription = (flg & 0x200) == 0 ? null : in.readString(); long creationTime = in.readLong(); this.mSingleDevice = singleDevice; @@ -620,6 +647,7 @@ public final class AssociationRequest implements Parcelable { com.android.internal.util.AnnotationValidations.validate( DeviceProfile.class, null, mDeviceProfile); this.mDisplayName = displayName; + this.mAssociatedDevice = associatedDevice; this.mSelfManaged = selfManaged; this.mForceConfirmation = forceConfirmation; this.mPackageName = packageName; @@ -648,10 +676,10 @@ public final class AssociationRequest implements Parcelable { }; @DataClass.Generated( - time = 1649179640045L, + time = 1663088980513L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java", - inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_COMPUTER\nprivate final boolean mSingleDevice\nprivate final @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List> mDeviceFilters\nprivate final @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate final boolean mSelfManaged\nprivate final boolean mForceConfirmation\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.UserIdInt int mUserId\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate final long mCreationTime\nprivate boolean mSkipPrompt\npublic @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String getDeviceProfile()\npublic @android.annotation.Nullable java.lang.CharSequence getDisplayName()\npublic boolean isSelfManaged()\npublic boolean isForceConfirmation()\npublic boolean isSingleDevice()\npublic void setPackageName(java.lang.String)\npublic void setUserId(int)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic void setDisplayName(java.lang.CharSequence)\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate boolean mSelfManaged\nprivate boolean mForceConfirmation\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDisplayName(java.lang.CharSequence)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setSelfManaged(boolean)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setForceConfirmation(boolean)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder implements []\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genConstDefs=false)") + inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_COMPUTER\nprivate final boolean mSingleDevice\nprivate final @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List> mDeviceFilters\nprivate final @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate @android.annotation.Nullable android.companion.AssociatedDevice mAssociatedDevice\nprivate final boolean mSelfManaged\nprivate final boolean mForceConfirmation\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.UserIdInt int mUserId\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate final long mCreationTime\nprivate boolean mSkipPrompt\npublic @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String getDeviceProfile()\npublic @android.annotation.Nullable java.lang.CharSequence getDisplayName()\npublic boolean isSelfManaged()\npublic boolean isForceConfirmation()\npublic boolean isSingleDevice()\npublic void setPackageName(java.lang.String)\npublic void setUserId(int)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic void setDisplayName(java.lang.CharSequence)\npublic void setAssociatedDevice(android.companion.AssociatedDevice)\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate boolean mSelfManaged\nprivate boolean mForceConfirmation\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDisplayName(java.lang.CharSequence)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setSelfManaged(boolean)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setForceConfirmation(boolean)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder implements []\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genConstDefs=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 4142bceb2c45d..90973c307a205 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -145,7 +145,7 @@ public final class CompanionDeviceManager { *
  • for WiFi - {@link android.net.wifi.ScanResult}
  • * * - * @deprecated use {@link #EXTRA_ASSOCIATION} instead. + * @deprecated use {@link AssociationInfo#getAssociatedDevice()} instead. */ @Deprecated public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE"; diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index 9818ee78b2b18..56715b4220693 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -46,6 +46,7 @@ import static java.util.Objects.requireNonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; +import android.companion.AssociatedDevice; import android.companion.AssociationInfo; import android.companion.AssociationRequest; import android.companion.CompanionDeviceManager; @@ -331,6 +332,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements private void onUserSelectedDevice(@NonNull DeviceFilterPair selectedDevice) { final MacAddress macAddress = selectedDevice.getMacAddress(); mRequest.setDisplayName(selectedDevice.getDisplayName()); + mRequest.setAssociatedDevice(new AssociatedDevice(selectedDevice.getDevice())); onAssociationApproved(macAddress); } diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java index 0ab5a8afe907e..80d9d972ed0af 100644 --- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java +++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java @@ -270,7 +270,8 @@ class AssociationRequestsProcessor { final long callingIdentity = Binder.clearCallingIdentity(); try { association = mService.createAssociation(userId, packageName, macAddress, - request.getDisplayName(), request.getDeviceProfile(), request.isSelfManaged()); + request.getDisplayName(), request.getDeviceProfile(), + request.getAssociatedDevice(), request.isSelfManaged()); } finally { Binder.restoreCallingIdentity(callingIdentity); } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 0426c79358bad..f2cb6028e8540 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -55,6 +55,7 @@ import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.NotificationManager; import android.app.PendingIntent; +import android.companion.AssociatedDevice; import android.companion.AssociationInfo; import android.companion.AssociationRequest; import android.companion.DeviceNotAssociatedException; @@ -872,23 +873,25 @@ public class CompanionDeviceManagerService extends SystemService { /** * @deprecated use - * {@link #createAssociation(int, String, MacAddress, CharSequence, String, boolean)} + * {@link #createAssociation(int, String, MacAddress, CharSequence, String, AssociatedDevice, + * boolean)} */ @Deprecated void legacyCreateAssociation(@UserIdInt int userId, @NonNull String deviceMacAddress, @NonNull String packageName, @Nullable String deviceProfile) { final MacAddress macAddress = MacAddress.fromString(deviceMacAddress); - createAssociation(userId, packageName, macAddress, null, deviceProfile, false); + createAssociation(userId, packageName, macAddress, null, deviceProfile, null, false); } AssociationInfo createAssociation(@UserIdInt int userId, @NonNull String packageName, @Nullable MacAddress macAddress, @Nullable CharSequence displayName, - @Nullable String deviceProfile, boolean selfManaged) { + @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice, + boolean selfManaged) { final int id = getNewAssociationIdForPackage(userId, packageName); final long timestamp = System.currentTimeMillis(); final AssociationInfo association = new AssociationInfo(id, userId, packageName, - macAddress, displayName, deviceProfile, selfManaged, + macAddress, displayName, deviceProfile, associatedDevice, selfManaged, /* notifyOnDeviceNearby */ false, /* revoked */ false, timestamp, Long.MAX_VALUE); Slog.i(TAG, "New CDM association created=" + association); mAssociationStore.addAssociation(association); diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java index 4b56c1b280362..c4f5766470584 100644 --- a/services/companion/java/com/android/server/companion/PersistentDataStore.java +++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java @@ -229,6 +229,11 @@ final class PersistentDataStore { /** * Reads previously persisted data for the given user "into" the provided containers. * + * Note that {@link AssociationInfo#getAssociatedDevice()} will always be {@code null} after + * retrieval from this datastore because it is not persisted (by design). This means that + * persisted data is not guaranteed to be identical to the initial data that was stored at the + * time of association. + * * @param userId Android UserID * @param associationsOut a container to read the {@link AssociationInfo}s "into". * @param previouslyUsedIdsPerPackageOut a container to read the used IDs "into". @@ -293,6 +298,9 @@ final class PersistentDataStore { /** * Persisted data to the disk. * + * Note that associatedDevice field in {@link AssociationInfo} is not persisted by this + * datastore implementation. + * * @param userId Android UserID * @param associations a set of user's associations. * @param previouslyUsedIdsPerPackage a set previously used Association IDs for the user. @@ -419,7 +427,7 @@ final class PersistentDataStore { final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L); out.add(new AssociationInfo(associationId, userId, appPackage, - MacAddress.fromString(deviceAddress), null, profile, + MacAddress.fromString(deviceAddress), null, profile, null, /* managedByCompanionApp */ false, notify, /* revoked */ false, timeApproved, Long.MAX_VALUE)); } @@ -556,9 +564,11 @@ final class PersistentDataStore { boolean notify, boolean revoked, long timeApproved, long lastTimeConnected) { AssociationInfo associationInfo = null; try { + // We do not persist AssociatedDevice, which means that AssociationInfo retrieved from + // datastore is not guaranteed to be identical to the one from initial association. associationInfo = new AssociationInfo(associationId, userId, appPackage, macAddress, - displayName, profile, selfManaged, notify, revoked, timeApproved, - lastTimeConnected); + displayName, profile, null, selfManaged, notify, revoked, + timeApproved, lastTimeConnected); } catch (Exception e) { if (DEBUG) Log.w(TAG, "Could not create AssociationInfo", e); } diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index 6388c7d52f10a..9c5d1a5b06104 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -226,7 +226,7 @@ public class VirtualDeviceManagerServiceTest { mContext.getSystemService(WindowManager.class), threadVerifier); mAssociationInfo = new AssociationInfo(1, 0, null, - MacAddress.BROADCAST_ADDRESS, "", null, true, false, false, 0, 0); + MacAddress.BROADCAST_ADDRESS, "", null, null, true, false, false, 0, 0); mVdms = new VirtualDeviceManagerService(mContext); mLocalService = mVdms.getLocalServiceInstance();