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:
+ *
+ *
+ * - for classic Bluetooth - {@link android.bluetooth.BluetoothDevice}
+ * - for Bluetooth LE - {@link android.bluetooth.le.ScanResult}
+ * - for WiFi - {@link android.net.wifi.ScanResult}
+ *
+ *
+ * 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 extends Parcelable> 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 extends Parcelable> 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:
+ *
+ *
+ * - for classic Bluetooth - {@link android.bluetooth.BluetoothDevice}
+ * - for Bluetooth LE - {@link android.bluetooth.le.ScanResult}
+ * - for WiFi - {@link android.net.wifi.ScanResult}
+ *
+ *
+ * @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();