Merge "Add forget function to remove database info and poc for subsequent" into tm-dev
diff --git a/nearby/framework/java/android/nearby/BroadcastCallback.java b/nearby/framework/java/android/nearby/BroadcastCallback.java
index 54c1916..cc94308 100644
--- a/nearby/framework/java/android/nearby/BroadcastCallback.java
+++ b/nearby/framework/java/android/nearby/BroadcastCallback.java
@@ -60,5 +60,5 @@
/**
* Called when broadcast status changes.
*/
- void onStatus(@BroadcastStatus int status);
+ void onStatusChanged(@BroadcastStatus int status);
}
diff --git a/nearby/framework/java/android/nearby/aidl/FastPairAntispoofkeyDeviceMetadataRequestParcel.aidl b/nearby/framework/java/android/nearby/BroadcastRequest.aidl
similarity index 60%
copy from nearby/framework/java/android/nearby/aidl/FastPairAntispoofkeyDeviceMetadataRequestParcel.aidl
copy to nearby/framework/java/android/nearby/BroadcastRequest.aidl
index 175d73c..53f7d42 100644
--- a/nearby/framework/java/android/nearby/aidl/FastPairAntispoofkeyDeviceMetadataRequestParcel.aidl
+++ b/nearby/framework/java/android/nearby/BroadcastRequest.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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
+ * 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,
@@ -14,13 +14,6 @@
* limitations under the License.
*/
-package android.nearby.aidl;
+package android.nearby;
-/**
- * Request details for metadata of a Fast Pair device keyed by either
- * antispoofkey or modelId.
- * {@hide}
- */
-parcelable FastPairAntispoofkeyDeviceMetadataRequestParcel {
- byte[] modelId;
-}
\ No newline at end of file
+parcelable BroadcastRequest;
diff --git a/nearby/framework/java/android/nearby/BroadcastRequest.java b/nearby/framework/java/android/nearby/BroadcastRequest.java
index 27468dd..71d51d2 100644
--- a/nearby/framework/java/android/nearby/BroadcastRequest.java
+++ b/nearby/framework/java/android/nearby/BroadcastRequest.java
@@ -38,6 +38,9 @@
@SuppressLint("ParcelNotFinal") // BroadcastRequest constructor is not public
public abstract class BroadcastRequest implements Parcelable {
+ /** An unknown nearby broadcast request type. */
+ public static final int BROADCAST_TYPE_UNKNOWN = -1;
+
/** Broadcast type for advertising using nearby presence protocol. */
public static final int BROADCAST_TYPE_NEARBY_PRESENCE = 3;
@@ -45,31 +48,49 @@
// Currently, only Nearby Presence broadcast is supported, in the future
// broadcasting using other nearby specifications will be added.
@Retention(RetentionPolicy.SOURCE)
- @IntDef({BROADCAST_TYPE_NEARBY_PRESENCE})
+ @IntDef({BROADCAST_TYPE_UNKNOWN, BROADCAST_TYPE_NEARBY_PRESENCE})
public @interface BroadcastType {
}
/**
* Tx Power when the value is not set in the broadcast.
*/
- public static final int UNKNOWN_TX_POWER = -100;
+ public static final int UNKNOWN_TX_POWER = -127;
/**
- * V0 of Nearby Presence Protocol.
+ * An unknown version of presence broadcast request.
+ */
+ public static final int PRESENCE_VERSION_UNKNOWN = -1;
+
+ /**
+ * A legacy presence version that is only suitable for legacy (31 bytes) BLE advertisements.
+ * This exists to support legacy presence version, and not recommended for use.
*/
public static final int PRESENCE_VERSION_V0 = 0;
/**
- * V1 of Nearby Presence Protocol.
+ * V1 of Nearby Presence Protocol. This version supports both legacy (31 bytes) BLE
+ * advertisements, and extended BLE advertisements.
*/
public static final int PRESENCE_VERSION_V1 = 1;
/** @hide **/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({PRESENCE_VERSION_V0, PRESENCE_VERSION_V1})
+ @IntDef({PRESENCE_VERSION_UNKNOWN, PRESENCE_VERSION_V0, PRESENCE_VERSION_V1})
public @interface BroadcastVersion {
}
+ /**
+ * The medium where the broadcast request should be sent.
+ *
+ * @hide
+ */
+ @IntDef({Medium.BLE, Medium.MDNS})
+ public @interface Medium {
+ int BLE = 1;
+ int MDNS = 2;
+ }
+
public static final @NonNull Creator<BroadcastRequest> CREATOR =
new Creator<BroadcastRequest>() {
@Override
@@ -93,10 +114,10 @@
private final @BroadcastType int mType;
private final @BroadcastVersion int mVersion;
private final int mTxPower;
- private final List<Integer> mMediums;
+ private final @Medium List<Integer> mMediums;
BroadcastRequest(@BroadcastType int type, @BroadcastVersion int version, int txPower,
- List<Integer> mediums) {
+ @Medium List<Integer> mediums) {
this.mType = type;
this.mVersion = version;
this.mTxPower = txPower;
@@ -137,6 +158,7 @@
* Returns the list of broadcast mediums.
*/
@NonNull
+ @Medium
public List<Integer> getMediums() {
return mMediums;
}
diff --git a/nearby/framework/java/android/nearby/FastPairAntispoofkeyDeviceMetadata.java b/nearby/framework/java/android/nearby/FastPairAntispoofKeyDeviceMetadata.java
similarity index 78%
rename from nearby/framework/java/android/nearby/FastPairAntispoofkeyDeviceMetadata.java
rename to nearby/framework/java/android/nearby/FastPairAntispoofKeyDeviceMetadata.java
index f756020..32fd043 100644
--- a/nearby/framework/java/android/nearby/FastPairAntispoofkeyDeviceMetadata.java
+++ b/nearby/framework/java/android/nearby/FastPairAntispoofKeyDeviceMetadata.java
@@ -19,33 +19,33 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
-import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataParcel;
+import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataParcel;
/**
- * Class for a type of registered Fast Pair device keyed by modelID, or antiSpoofKey.
+ * Class for a type of registered Fast Pair device keyed by modelID, or antispoofKey.
* @hide
*/
@SystemApi
-public class FastPairAntispoofkeyDeviceMetadata {
+public class FastPairAntispoofKeyDeviceMetadata {
- FastPairAntispoofkeyDeviceMetadataParcel mMetadataParcel;
- FastPairAntispoofkeyDeviceMetadata(
- FastPairAntispoofkeyDeviceMetadataParcel metadataParcel) {
+ FastPairAntispoofKeyDeviceMetadataParcel mMetadataParcel;
+ FastPairAntispoofKeyDeviceMetadata(
+ FastPairAntispoofKeyDeviceMetadataParcel metadataParcel) {
this.mMetadataParcel = metadataParcel;
}
/**
- * Builder used to create FastPairAntisppofkeyDeviceMetadata.
+ * Builder used to create FastPairAntispoofKeyDeviceMetadata.
*/
public static final class Builder {
- private final FastPairAntispoofkeyDeviceMetadataParcel mBuilderParcel;
+ private final FastPairAntispoofKeyDeviceMetadataParcel mBuilderParcel;
/**
* Default constructor of Builder.
*/
public Builder() {
- mBuilderParcel = new FastPairAntispoofkeyDeviceMetadataParcel();
+ mBuilderParcel = new FastPairAntispoofKeyDeviceMetadataParcel();
mBuilderParcel.antiSpoofPublicKey = null;
mBuilderParcel.deviceMetadata = null;
}
@@ -78,11 +78,11 @@
}
/**
- * Build {@link FastPairAntispoofkeyDeviceMetadata} with the currently set configuration.
+ * Build {@link FastPairAntispoofKeyDeviceMetadata} with the currently set configuration.
*/
@NonNull
- public FastPairAntispoofkeyDeviceMetadata build() {
- return new FastPairAntispoofkeyDeviceMetadata(mBuilderParcel);
+ public FastPairAntispoofKeyDeviceMetadata build() {
+ return new FastPairAntispoofKeyDeviceMetadata(mBuilderParcel);
}
}
}
diff --git a/nearby/framework/java/android/nearby/FastPairDataProviderBase.java b/nearby/framework/java/android/nearby/FastPairDataProviderBase.java
index 57467b7..dfb8760 100644
--- a/nearby/framework/java/android/nearby/FastPairDataProviderBase.java
+++ b/nearby/framework/java/android/nearby/FastPairDataProviderBase.java
@@ -24,13 +24,13 @@
import android.nearby.aidl.ByteArrayParcel;
import android.nearby.aidl.FastPairAccountDevicesMetadataRequestParcel;
import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel;
-import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataRequestParcel;
+import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataRequestParcel;
import android.nearby.aidl.FastPairEligibleAccountParcel;
import android.nearby.aidl.FastPairEligibleAccountsRequestParcel;
import android.nearby.aidl.FastPairManageAccountDeviceRequestParcel;
import android.nearby.aidl.FastPairManageAccountRequestParcel;
import android.nearby.aidl.IFastPairAccountDevicesMetadataCallback;
-import android.nearby.aidl.IFastPairAntispoofkeyDeviceMetadataCallback;
+import android.nearby.aidl.IFastPairAntispoofKeyDeviceMetadataCallback;
import android.nearby.aidl.IFastPairDataProvider;
import android.nearby.aidl.IFastPairEligibleAccountsCallback;
import android.nearby.aidl.IFastPairManageAccountCallback;
@@ -110,15 +110,15 @@
}
/**
- * Callback to be invoked when an antispoofkeyed device metadata is loaded.
+ * Callback to be invoked when an AntispoofKeyed device metadata is loaded.
*/
- public interface FastPairAntispoofkeyDeviceMetadataCallback {
+ public interface FastPairAntispoofKeyDeviceMetadataCallback {
/**
* Invoked once the meta data is loaded.
*/
- void onFastPairAntispoofkeyDeviceMetadataReceived(
- @NonNull FastPairAntispoofkeyDeviceMetadata metadata);
+ void onFastPairAntispoofKeyDeviceMetadataReceived(
+ @NonNull FastPairAntispoofKeyDeviceMetadata metadata);
/** Invoked in case of error. */
void onError(@ErrorCode int code, @Nullable String message);
}
@@ -166,9 +166,9 @@
* Fulfills the Fast Pair device metadata request by using callback to send back the
* device meta data of a given modelId.
*/
- public abstract void onLoadFastPairAntispoofkeyDeviceMetadata(
- @NonNull FastPairAntispoofkeyDeviceMetadataRequest request,
- @NonNull FastPairAntispoofkeyDeviceMetadataCallback callback);
+ public abstract void onLoadFastPairAntispoofKeyDeviceMetadata(
+ @NonNull FastPairAntispoofKeyDeviceMetadataRequest request,
+ @NonNull FastPairAntispoofKeyDeviceMetadataCallback callback);
/**
* Fulfills the account tied Fast Pair devices metadata request by using callback to send back
@@ -208,18 +208,18 @@
}
/**
- * Class for reading FastPairAntispoofkeyDeviceMetadataRequest.
+ * Class for reading FastPairAntispoofKeyDeviceMetadataRequest.
*/
- public static class FastPairAntispoofkeyDeviceMetadataRequest {
+ public static class FastPairAntispoofKeyDeviceMetadataRequest {
- private final FastPairAntispoofkeyDeviceMetadataRequestParcel mMetadataRequestParcel;
+ private final FastPairAntispoofKeyDeviceMetadataRequestParcel mMetadataRequestParcel;
- private FastPairAntispoofkeyDeviceMetadataRequest(
- final FastPairAntispoofkeyDeviceMetadataRequestParcel metaDataRequestParcel) {
+ private FastPairAntispoofKeyDeviceMetadataRequest(
+ final FastPairAntispoofKeyDeviceMetadataRequestParcel metaDataRequestParcel) {
this.mMetadataRequestParcel = metaDataRequestParcel;
}
- /** Get modelId, the key for FastPairAntispoofkeyDeviceMetadata. */
+ /** Get modelId, the key for FastPairAntispoofKeyDeviceMetadata. */
public @NonNull byte[] getModelId() {
return this.mMetadataRequestParcel.modelId;
}
@@ -338,26 +338,26 @@
}
/**
- * Callback class that sends back FastPairAntispoofkeyDeviceMetadata.
+ * Callback class that sends back FastPairAntispoofKeyDeviceMetadata.
*/
- private final class WrapperFastPairAntispoofkeyDeviceMetadataCallback implements
- FastPairAntispoofkeyDeviceMetadataCallback {
+ private final class WrapperFastPairAntispoofKeyDeviceMetadataCallback implements
+ FastPairAntispoofKeyDeviceMetadataCallback {
- private IFastPairAntispoofkeyDeviceMetadataCallback mCallback;
+ private IFastPairAntispoofKeyDeviceMetadataCallback mCallback;
- private WrapperFastPairAntispoofkeyDeviceMetadataCallback(
- IFastPairAntispoofkeyDeviceMetadataCallback callback) {
+ private WrapperFastPairAntispoofKeyDeviceMetadataCallback(
+ IFastPairAntispoofKeyDeviceMetadataCallback callback) {
mCallback = callback;
}
/**
- * Sends back FastPairAntispoofkeyDeviceMetadata.
+ * Sends back FastPairAntispoofKeyDeviceMetadata.
*/
@Override
- public void onFastPairAntispoofkeyDeviceMetadataReceived(
- @NonNull FastPairAntispoofkeyDeviceMetadata metadata) {
+ public void onFastPairAntispoofKeyDeviceMetadataReceived(
+ @NonNull FastPairAntispoofKeyDeviceMetadata metadata) {
try {
- mCallback.onFastPairAntispoofkeyDeviceMetadataReceived(metadata.mMetadataParcel);
+ mCallback.onFastPairAntispoofKeyDeviceMetadataReceived(metadata.mMetadataParcel);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (RuntimeException e) {
@@ -555,12 +555,12 @@
}
@Override
- public void loadFastPairAntispoofkeyDeviceMetadata(
- @NonNull FastPairAntispoofkeyDeviceMetadataRequestParcel requestParcel,
- IFastPairAntispoofkeyDeviceMetadataCallback callback) {
- onLoadFastPairAntispoofkeyDeviceMetadata(
- new FastPairAntispoofkeyDeviceMetadataRequest(requestParcel),
- new WrapperFastPairAntispoofkeyDeviceMetadataCallback(callback));
+ public void loadFastPairAntispoofKeyDeviceMetadata(
+ @NonNull FastPairAntispoofKeyDeviceMetadataRequestParcel requestParcel,
+ IFastPairAntispoofKeyDeviceMetadataCallback callback) {
+ onLoadFastPairAntispoofKeyDeviceMetadata(
+ new FastPairAntispoofKeyDeviceMetadataRequest(requestParcel),
+ new WrapperFastPairAntispoofKeyDeviceMetadataCallback(callback));
}
@Override
diff --git a/nearby/framework/java/android/nearby/aidl/FastPairAntispoofkeyDeviceMetadataRequestParcel.aidl b/nearby/framework/java/android/nearby/IBroadcastListener.aidl
similarity index 62%
copy from nearby/framework/java/android/nearby/aidl/FastPairAntispoofkeyDeviceMetadataRequestParcel.aidl
copy to nearby/framework/java/android/nearby/IBroadcastListener.aidl
index 175d73c..98c7e17 100644
--- a/nearby/framework/java/android/nearby/aidl/FastPairAntispoofkeyDeviceMetadataRequestParcel.aidl
+++ b/nearby/framework/java/android/nearby/IBroadcastListener.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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
+ * 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,
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package android.nearby.aidl;
+package android.nearby;
/**
- * Request details for metadata of a Fast Pair device keyed by either
- * antispoofkey or modelId.
+ * Callback when brodacast status changes.
+ *
* {@hide}
*/
-parcelable FastPairAntispoofkeyDeviceMetadataRequestParcel {
- byte[] modelId;
-}
\ No newline at end of file
+oneway interface IBroadcastListener {
+ /** Called when the broadcast status changes. */
+ void onStatusChanged(int status);
+}
diff --git a/nearby/framework/java/android/nearby/INearbyManager.aidl b/nearby/framework/java/android/nearby/INearbyManager.aidl
index 4fff563..91dd485 100644
--- a/nearby/framework/java/android/nearby/INearbyManager.aidl
+++ b/nearby/framework/java/android/nearby/INearbyManager.aidl
@@ -16,7 +16,9 @@
package android.nearby;
+import android.nearby.IBroadcastListener;
import android.nearby.IScanListener;
+import android.nearby.BroadcastRequest;
import android.nearby.ScanRequest;
/**
@@ -29,4 +31,8 @@
void registerScanListener(in ScanRequest scanRequest, in IScanListener listener);
void unregisterScanListener(in IScanListener listener);
+
+ void startBroadcast(in BroadcastRequest broadcastRequest, in IBroadcastListener callback);
+
+ void stopBroadcast(in IBroadcastListener callback);
}
\ No newline at end of file
diff --git a/nearby/framework/java/android/nearby/NearbyManager.java b/nearby/framework/java/android/nearby/NearbyManager.java
index a217677..eb34e00 100644
--- a/nearby/framework/java/android/nearby/NearbyManager.java
+++ b/nearby/framework/java/android/nearby/NearbyManager.java
@@ -59,6 +59,10 @@
@GuardedBy("sScanListeners")
private static final WeakHashMap<ScanCallback, WeakReference<ScanListenerTransport>>
sScanListeners = new WeakHashMap<>();
+ @GuardedBy("sBroadcastListeners")
+ private static final WeakHashMap<BroadcastCallback, WeakReference<BroadcastListenerTransport>>
+ sBroadcastListeners = new WeakHashMap<>();
+
private final INearbyManager mService;
/**
@@ -157,7 +161,23 @@
*/
public void startBroadcast(@NonNull BroadcastRequest broadcastRequest,
@CallbackExecutor @NonNull Executor executor, @NonNull BroadcastCallback callback) {
- // TODO(b/218187205): implement broadcast.
+ try {
+ synchronized (sBroadcastListeners) {
+ WeakReference<BroadcastListenerTransport> reference = sBroadcastListeners.get(
+ callback);
+ BroadcastListenerTransport transport = reference != null ? reference.get() : null;
+ if (transport == null) {
+ transport = new BroadcastListenerTransport(callback, executor);
+ } else {
+ Preconditions.checkState(transport.isRegistered());
+ transport.setExecutor(executor);
+ }
+ mService.startBroadcast(broadcastRequest, transport);
+ sBroadcastListeners.put(callback, new WeakReference<>(transport));
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -167,7 +187,19 @@
*/
@SuppressLint("ExecutorRegistration")
public void stopBroadcast(@NonNull BroadcastCallback callback) {
- // TODO(b/218187205): implement broadcast.
+ try {
+ synchronized (sBroadcastListeners) {
+ WeakReference<BroadcastListenerTransport> reference = sBroadcastListeners.remove(
+ callback);
+ BroadcastListenerTransport transport = reference != null ? reference.get() : null;
+ if (transport != null) {
+ transport.unregister();
+ mService.stopBroadcast(transport);
+ }
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -248,4 +280,38 @@
toClientNearbyDevice(nearbyDeviceParcelable, mScanType)));
}
}
+
+ private static class BroadcastListenerTransport extends IBroadcastListener.Stub {
+ private volatile @Nullable BroadcastCallback mBroadcastCallback;
+ private Executor mExecutor;
+
+ BroadcastListenerTransport(BroadcastCallback broadcastCallback,
+ @CallbackExecutor Executor executor) {
+ mBroadcastCallback = broadcastCallback;
+ mExecutor = executor;
+ }
+
+ void setExecutor(Executor executor) {
+ Preconditions.checkArgument(
+ executor != null, "invalid null executor");
+ mExecutor = executor;
+ }
+
+ boolean isRegistered() {
+ return mBroadcastCallback != null;
+ }
+
+ void unregister() {
+ mBroadcastCallback = null;
+ }
+
+ @Override
+ public void onStatusChanged(int status) {
+ mExecutor.execute(() -> {
+ if (mBroadcastCallback != null) {
+ mBroadcastCallback.onStatusChanged(status);
+ }
+ });
+ }
+ }
}
diff --git a/nearby/framework/java/android/nearby/PresenceBroadcastRequest.java b/nearby/framework/java/android/nearby/PresenceBroadcastRequest.java
index b4d4b55..d01be06 100644
--- a/nearby/framework/java/android/nearby/PresenceBroadcastRequest.java
+++ b/nearby/framework/java/android/nearby/PresenceBroadcastRequest.java
@@ -137,18 +137,20 @@
private final List<Integer> mMediums;
private final List<Integer> mActions;
private final List<DataElement> mExtendedProperties;
+ private final byte[] mSalt;
+ private final PrivateCredential mCredential;
private int mVersion;
private int mTxPower;
- private byte[] mSalt;
- private PrivateCredential mCredential;
- public Builder(@NonNull List<Integer> mediums, @NonNull byte[] salt) {
+ public Builder(@NonNull List<Integer> mediums, @NonNull byte[] salt,
+ @NonNull PrivateCredential credential) {
Preconditions.checkState(!mediums.isEmpty(), "mediums cannot be empty");
Preconditions.checkState(salt != null && salt.length > 0, "salt cannot be empty");
mVersion = PRESENCE_VERSION_V0;
mTxPower = UNKNOWN_TX_POWER;
+ mCredential = credential;
mActions = new ArrayList<>();
mExtendedProperties = new ArrayList<>();
@@ -185,16 +187,6 @@
}
/**
- * Sets the credential associated with the presence broadcast request.
- */
- @NonNull
- public Builder setCredential(@NonNull PrivateCredential credential) {
- Objects.requireNonNull(credential);
- mCredential = credential;
- return this;
- }
-
- /**
* Adds an extended property for the presence broadcast request.
*/
@NonNull
diff --git a/nearby/framework/java/android/nearby/PresenceDevice.java b/nearby/framework/java/android/nearby/PresenceDevice.java
index d5ea0b4..12fc2a3 100644
--- a/nearby/framework/java/android/nearby/PresenceDevice.java
+++ b/nearby/framework/java/android/nearby/PresenceDevice.java
@@ -162,6 +162,12 @@
dest.writeInt(medium);
}
dest.writeInt(getRssi());
+ dest.writeInt(mSalt.length);
+ dest.writeByteArray(mSalt);
+ dest.writeInt(mSecretId.length);
+ dest.writeByteArray(mSecretId);
+ dest.writeInt(mEncryptedIdentity.length);
+ dest.writeByteArray(mEncryptedIdentity);
dest.writeString(mDeviceId);
dest.writeInt(mDeviceType);
dest.writeInt(mDeviceImageUrl == null ? 0 : 1);
@@ -184,26 +190,47 @@
public static final Creator<PresenceDevice> CREATOR = new Creator<PresenceDevice>() {
@Override
public PresenceDevice createFromParcel(Parcel in) {
- Builder builder = new Builder();
+ String name = null;
if (in.readInt() == 1) {
- builder.setName(in.readString());
+ name = in.readString();
}
int size = in.readInt();
+ List<Integer> mediums = new ArrayList<>();
for (int i = 0; i < size; i++) {
- builder.addMedium(in.readInt());
+ mediums.add(in.readInt());
}
- builder.setRssi(in.readInt());
- builder.setDeviceId(in.readString());
- builder.setDeviceType(in.readInt());
+ int rssi = in.readInt();
+ byte[] salt = new byte[in.readInt()];
+ in.readByteArray(salt);
+ byte[] secretId = new byte[in.readInt()];
+ in.readByteArray(secretId);
+ byte[] encryptedIdentity = new byte[in.readInt()];
+ in.readByteArray(encryptedIdentity);
+ String deviceId = in.readString();
+ int deviceType = in.readInt();
+ String deviceImageUrl = null;
if (in.readInt() == 1) {
- builder.setDeviceImageUrl(in.readString());
+ deviceImageUrl = in.readString();
}
- builder.setDiscoveryTimestampMillis(in.readLong());
+ long discoveryTimeMillis = in.readLong();
int dataElementSize = in.readInt();
+ List<DataElement> dataElements = new ArrayList<>();
for (int i = 0; i < dataElementSize; i++) {
- builder.addExtendedProperty(
+ dataElements.add(
in.readParcelable(DataElement.class.getClassLoader(), DataElement.class));
}
+ Builder builder = new Builder(deviceId, salt, secretId, encryptedIdentity)
+ .setName(name)
+ .setRssi(rssi)
+ .setDeviceType(deviceType)
+ .setDeviceImageUrl(deviceImageUrl)
+ .setDiscoveryTimestampMillis(discoveryTimeMillis);
+ for (int i = 0; i < mediums.size(); i++) {
+ builder.addMedium(mediums.get(i));
+ }
+ for (int i = 0; i < dataElements.size(); i++) {
+ builder.addExtendedProperty(dataElements.get(i));
+ }
return builder.build();
}
@@ -220,21 +247,34 @@
private final List<DataElement> mExtendedProperties;
private final List<Integer> mMediums;
+ private final String mDeviceId;
+ private final byte[] mSalt;
+ private final byte[] mSecretId;
+ private final byte[] mEncryptedIdentity;
private String mName;
private int mRssi;
- private String mDeviceId;
- private byte[] mSalt;
- private byte[] mSecretId;
- private byte[] mEncryptedIdentity;
private int mDeviceType;
private String mDeviceImageUrl;
private long mDiscoveryTimestampMillis;
- public Builder() {
+ /**
+ * Constructs a {@link Builder}.
+ *
+ * @param deviceId the identifier on the discovered Presence device
+ * @param salt a random salt used in the beacon from the Presence device.
+ * @param secretId a secret identifier used in the beacon from the Presence device.
+ * @param encryptedIdentity the identity associated with the Presence device.
+ */
+ public Builder(@NonNull String deviceId, @NonNull byte[] salt, @NonNull byte[] secretId,
+ @NonNull byte[] encryptedIdentity) {
+ mDeviceId = deviceId;
+ mSalt = salt;
+ mSecretId = secretId;
+ mEncryptedIdentity = encryptedIdentity;
mMediums = new ArrayList<>();
mExtendedProperties = new ArrayList<>();
- mRssi = -100;
+ mRssi = -127;
}
/**
@@ -271,48 +311,6 @@
}
/**
- * Sets the identifier on the discovered Presence device.
- *
- * @param deviceId Identifier of the Presence device.
- */
- @NonNull
- public Builder setDeviceId(@NonNull String deviceId) {
- Objects.requireNonNull(deviceId);
- mDeviceId = deviceId;
- return this;
- }
-
- /**
- * Sets the identifier on the discovered Presence device.
- */
- @NonNull
- public Builder setSalt(@NonNull byte[] salt) {
- Objects.requireNonNull(salt);
- mSalt = salt;
- return this;
- }
-
- /**
- * Sets the secret id of the discovered Presence device.
- */
- @NonNull
- public Builder setSecretId(@NonNull byte[] secretId) {
- Objects.requireNonNull(secretId);
- mSecretId = secretId;
- return this;
- }
-
- /**
- * Sets the encrypted identity of the discovered Presence device.
- */
- @NonNull
- public Builder setEncryptedIdentity(@NonNull byte[] encryptedIdentity) {
- Objects.requireNonNull(encryptedIdentity);
- mEncryptedIdentity = encryptedIdentity;
- return this;
- }
-
- /**
* Sets the type of discovered Presence device.
*
* @param deviceType Type of the Presence device.
diff --git a/nearby/framework/java/android/nearby/aidl/FastPairAntispoofkeyDeviceMetadataParcel.aidl b/nearby/framework/java/android/nearby/aidl/FastPairAntispoofKeyDeviceMetadataParcel.aidl
similarity index 89%
rename from nearby/framework/java/android/nearby/aidl/FastPairAntispoofkeyDeviceMetadataParcel.aidl
rename to nearby/framework/java/android/nearby/aidl/FastPairAntispoofKeyDeviceMetadataParcel.aidl
index 8ca66d2..071d020 100644
--- a/nearby/framework/java/android/nearby/aidl/FastPairAntispoofkeyDeviceMetadataParcel.aidl
+++ b/nearby/framework/java/android/nearby/aidl/FastPairAntispoofKeyDeviceMetadataParcel.aidl
@@ -17,12 +17,12 @@
import android.nearby.aidl.FastPairDeviceMetadataParcel;
/**
- * Metadata of a Fast Pair device keyed by Antispoofkey,
+ * Metadata of a Fast Pair device keyed by AntispoofKey,
* Used by initial pairing without account association.
*
* {@hide}
*/
-parcelable FastPairAntispoofkeyDeviceMetadataParcel {
+parcelable FastPairAntispoofKeyDeviceMetadataParcel {
// Anti spoof public key.
byte[] antiSpoofPublicKey;
diff --git a/nearby/framework/java/android/nearby/aidl/FastPairAntispoofkeyDeviceMetadataRequestParcel.aidl b/nearby/framework/java/android/nearby/aidl/FastPairAntispoofKeyDeviceMetadataRequestParcel.aidl
similarity index 89%
rename from nearby/framework/java/android/nearby/aidl/FastPairAntispoofkeyDeviceMetadataRequestParcel.aidl
rename to nearby/framework/java/android/nearby/aidl/FastPairAntispoofKeyDeviceMetadataRequestParcel.aidl
index 175d73c..afdcf15 100644
--- a/nearby/framework/java/android/nearby/aidl/FastPairAntispoofkeyDeviceMetadataRequestParcel.aidl
+++ b/nearby/framework/java/android/nearby/aidl/FastPairAntispoofKeyDeviceMetadataRequestParcel.aidl
@@ -18,9 +18,9 @@
/**
* Request details for metadata of a Fast Pair device keyed by either
- * antispoofkey or modelId.
+ * antispoofKey or modelId.
* {@hide}
*/
-parcelable FastPairAntispoofkeyDeviceMetadataRequestParcel {
+parcelable FastPairAntispoofKeyDeviceMetadataRequestParcel {
byte[] modelId;
}
\ No newline at end of file
diff --git a/nearby/framework/java/android/nearby/aidl/IFastPairAntispoofkeyDeviceMetadataCallback.aidl b/nearby/framework/java/android/nearby/aidl/IFastPairAntispoofKeyDeviceMetadataCallback.aidl
similarity index 75%
rename from nearby/framework/java/android/nearby/aidl/IFastPairAntispoofkeyDeviceMetadataCallback.aidl
rename to nearby/framework/java/android/nearby/aidl/IFastPairAntispoofKeyDeviceMetadataCallback.aidl
index 87c9796..38abba4 100644
--- a/nearby/framework/java/android/nearby/aidl/IFastPairAntispoofkeyDeviceMetadataCallback.aidl
+++ b/nearby/framework/java/android/nearby/aidl/IFastPairAntispoofKeyDeviceMetadataCallback.aidl
@@ -14,14 +14,14 @@
package android.nearby.aidl;
-import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataParcel;
+import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataParcel;
/**
- * Provides callback interface for OEMs to send FastPair Antispoofkey Device metadata back.
+ * Provides callback interface for OEMs to send FastPair AntispoofKey Device metadata back.
*
* {@hide}
*/
-interface IFastPairAntispoofkeyDeviceMetadataCallback {
- void onFastPairAntispoofkeyDeviceMetadataReceived(in FastPairAntispoofkeyDeviceMetadataParcel metadata);
+interface IFastPairAntispoofKeyDeviceMetadataCallback {
+ void onFastPairAntispoofKeyDeviceMetadataReceived(in FastPairAntispoofKeyDeviceMetadataParcel metadata);
void onError(int code, String message);
}
\ No newline at end of file
diff --git a/nearby/framework/java/android/nearby/aidl/IFastPairDataProvider.aidl b/nearby/framework/java/android/nearby/aidl/IFastPairDataProvider.aidl
index 73aef9e..2956211 100644
--- a/nearby/framework/java/android/nearby/aidl/IFastPairDataProvider.aidl
+++ b/nearby/framework/java/android/nearby/aidl/IFastPairDataProvider.aidl
@@ -14,8 +14,8 @@
package android.nearby.aidl;
-import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataRequestParcel;
-import android.nearby.aidl.IFastPairAntispoofkeyDeviceMetadataCallback;
+import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataRequestParcel;
+import android.nearby.aidl.IFastPairAntispoofKeyDeviceMetadataCallback;
import android.nearby.aidl.FastPairAccountDevicesMetadataRequestParcel;
import android.nearby.aidl.IFastPairAccountDevicesMetadataCallback;
import android.nearby.aidl.FastPairEligibleAccountsRequestParcel;
@@ -31,8 +31,8 @@
* {@hide}
*/
oneway interface IFastPairDataProvider {
- void loadFastPairAntispoofkeyDeviceMetadata(in FastPairAntispoofkeyDeviceMetadataRequestParcel request,
- in IFastPairAntispoofkeyDeviceMetadataCallback callback);
+ void loadFastPairAntispoofKeyDeviceMetadata(in FastPairAntispoofKeyDeviceMetadataRequestParcel request,
+ in IFastPairAntispoofKeyDeviceMetadataCallback callback);
void loadFastPairAccountDevicesMetadata(in FastPairAccountDevicesMetadataRequestParcel request,
in IFastPairAccountDevicesMetadataCallback callback);
void loadFastPairEligibleAccounts(in FastPairEligibleAccountsRequestParcel request,
diff --git a/nearby/service/Android.bp b/nearby/service/Android.bp
index 12fce04..802e2c8 100644
--- a/nearby/service/Android.bp
+++ b/nearby/service/Android.bp
@@ -44,7 +44,7 @@
// Main lib for nearby services.
java_library {
- name: "service-nearby",
+ name: "service-nearby-pre-jarjar",
srcs: [":nearby-service-srcs"],
defaults: [
diff --git a/nearby/service/java/com/android/server/nearby/NearbyConfiguration.java b/nearby/service/java/com/android/server/nearby/NearbyConfiguration.java
new file mode 100644
index 0000000..8fdac87
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/NearbyConfiguration.java
@@ -0,0 +1,58 @@
+/*
+ * 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 com.android.server.nearby;
+
+import android.provider.DeviceConfig;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * A utility class for encapsulating Nearby feature flag configurations.
+ */
+public class NearbyConfiguration {
+
+ /**
+ * Flag use to enable presence legacy broadcast.
+ */
+ public static final String NEARBY_ENABLE_PRESENCE_BROADCAST_LEGACY =
+ "nearby_enable_presence_broadcast_legacy";
+
+ private boolean mEnablePresenceBroadcastLegacy;
+
+ public NearbyConfiguration() {
+ mEnablePresenceBroadcastLegacy = getDeviceConfigBoolean(
+ NEARBY_ENABLE_PRESENCE_BROADCAST_LEGACY, false /* defaultValue */);
+
+ }
+
+ /**
+ * Returns whether broadcasting legacy presence spec is enabled.
+ */
+ public boolean isPresenceBroadcastLegacyEnabled() {
+ return mEnablePresenceBroadcastLegacy;
+ }
+
+ private boolean getDeviceConfigBoolean(final String name, final boolean defaultValue) {
+ final String value = getDeviceConfigProperty(name);
+ return value != null ? Boolean.parseBoolean(value) : defaultValue;
+ }
+
+ @VisibleForTesting
+ protected String getDeviceConfigProperty(String name) {
+ return DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TETHERING, name);
+ }
+}
diff --git a/nearby/service/java/com/android/server/nearby/NearbyService.java b/nearby/service/java/com/android/server/nearby/NearbyService.java
index 6d149fc..74b327a 100644
--- a/nearby/service/java/com/android/server/nearby/NearbyService.java
+++ b/nearby/service/java/com/android/server/nearby/NearbyService.java
@@ -27,6 +27,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.location.ContextHubManager;
+import android.nearby.BroadcastRequest;
+import android.nearby.IBroadcastListener;
import android.nearby.INearbyManager;
import android.nearby.IScanListener;
import android.nearby.ScanRequest;
@@ -38,6 +40,7 @@
import com.android.server.nearby.injector.Injector;
import com.android.server.nearby.presence.ChreCommunication;
import com.android.server.nearby.presence.PresenceManager;
+import com.android.server.nearby.provider.BroadcastProviderManager;
import com.android.server.nearby.provider.DiscoveryProviderManager;
import com.android.server.nearby.provider.FastPairDataProvider;
@@ -71,11 +74,13 @@
}
};
private DiscoveryProviderManager mProviderManager;
+ private BroadcastProviderManager mBroadcastProviderManager;
public NearbyService(Context context) {
mContext = context;
mSystemInjector = new SystemInjector(context);
mProviderManager = new DiscoveryProviderManager(context, mSystemInjector);
+ mBroadcastProviderManager = new BroadcastProviderManager(context, mSystemInjector);
final LocatorContextWrapper lcw = new LocatorContextWrapper(context, null);
mFastPairManager = new FastPairManager(lcw);
mPresenceManager =
@@ -103,6 +108,16 @@
mProviderManager.unregisterScanListener(listener);
}
+ @Override
+ public void startBroadcast(BroadcastRequest broadcastRequest, IBroadcastListener listener) {
+ mBroadcastProviderManager.startBroadcast(broadcastRequest, listener);
+ }
+
+ @Override
+ public void stopBroadcast(IBroadcastListener listener) {
+ mBroadcastProviderManager.stopBroadcast(listener);
+ }
+
/**
* Called by the service initializer.
*
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
index 8a0ea66..af49da1 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
@@ -86,7 +86,7 @@
// Use api to get anti spoofing key from model id.
try {
Rpcs.GetObservedDeviceResponse response =
- dataProvider.loadFastPairAntispoofkeyDeviceMetadata(model);
+ dataProvider.loadFastPairAntispoofKeyDeviceMetadata(model);
if (response == null) {
Log.e(TAG, "server does not have model id "
+ Hex.bytesToStringLowercase(model));
diff --git a/nearby/service/java/com/android/server/nearby/presence/FastAdvertisement.java b/nearby/service/java/com/android/server/nearby/presence/FastAdvertisement.java
new file mode 100644
index 0000000..e4df673
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/presence/FastAdvertisement.java
@@ -0,0 +1,203 @@
+/*
+ * 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 com.android.server.nearby.presence;
+
+import android.annotation.Nullable;
+import android.nearby.BroadcastRequest;
+import android.nearby.PresenceBroadcastRequest;
+import android.nearby.PresenceCredential;
+
+import com.android.internal.util.Preconditions;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+
+/**
+ * A Nearby Presence advertisement to be advertised on BT4.2 devices.
+ *
+ * <p>Serializable between Java object and bytes formats. Java object is used at the upper scanning
+ * and advertising interface as an abstraction of the actual bytes. Bytes format is used at the
+ * underlying BLE and mDNS stacks, which do necessary slicing and merging based on advertising
+ * capacities.
+ */
+// The fast advertisement is defined in the format below:
+// Header (1 byte) | salt (2 bytes) | identity (14 bytes) | tx_power (1 byte) | actions (1~ bytes)
+// The header contains:
+// version (3 bits) | provision_mode_flag (1 bit) | identity_type (3 bits) |
+// extended_advertisement_mode (1 bit)
+public class FastAdvertisement {
+
+ private static final int FAST_ADVERTISEMENT_MAX_LENGTH = 24;
+
+ static final byte INVALID_TX_POWER = (byte) 0xFF;
+
+ static final int HEADER_LENGTH = 1;
+
+ static final int SALT_LENGTH = 2;
+
+ static final int IDENTITY_LENGTH = 14;
+
+ static final int TX_POWER_LENGTH = 1;
+
+ private static final int MAX_ACTION_COUNT = 6;
+
+ /**
+ * Creates a {@link FastAdvertisement} from a Presence Broadcast Request.
+ */
+ public static FastAdvertisement createFromRequest(PresenceBroadcastRequest request) {
+ byte[] salt = request.getSalt();
+ byte[] identity = request.getCredential().getMetadataEncryptionKey();
+ List<Integer> actions = request.getActions();
+ Preconditions.checkArgument(
+ salt.length == SALT_LENGTH,
+ "FastAdvertisement's salt does not match correct length");
+ Preconditions.checkArgument(
+ identity.length == IDENTITY_LENGTH,
+ "FastAdvertisement's identity does not match correct length");
+ Preconditions.checkArgument(
+ !actions.isEmpty(), "FastAdvertisement must contain at least one action");
+ Preconditions.checkArgument(
+ actions.size() <= MAX_ACTION_COUNT,
+ "FastAdvertisement advertised actions cannot exceed max count " + MAX_ACTION_COUNT);
+
+ return new FastAdvertisement(
+ request.getCredential().getIdentityType(),
+ identity,
+ salt,
+ actions,
+ (byte) request.getTxPower());
+ }
+
+ /** Serialize an {@link FastAdvertisement} object into bytes. */
+ public byte[] toBytes() {
+ ByteBuffer buffer = ByteBuffer.allocate(getLength());
+
+ buffer.put(FastAdvertisementUtils.constructHeader(getVersion(), mIdentityType));
+ buffer.put(mSalt);
+ buffer.put(getIdentity());
+
+ buffer.put(mTxPower == null ? INVALID_TX_POWER : mTxPower);
+ for (int action : mActions) {
+ buffer.put((byte) action);
+ }
+ return buffer.array();
+ }
+
+ private final int mLength;
+
+ private final int mLtvFieldCount;
+
+ @PresenceCredential.IdentityType private final int mIdentityType;
+
+ private final byte[] mIdentity;
+
+ private final byte[] mSalt;
+
+ private final List<Integer> mActions;
+
+ @Nullable
+ private final Byte mTxPower;
+
+ FastAdvertisement(
+ @PresenceCredential.IdentityType int identityType,
+ byte[] identity,
+ byte[] salt,
+ List<Integer> actions,
+ @Nullable Byte txPower) {
+ this.mIdentityType = identityType;
+ this.mIdentity = identity;
+ this.mSalt = salt;
+ this.mActions = actions;
+ this.mTxPower = txPower;
+ int ltvFieldCount = 3;
+ int length =
+ HEADER_LENGTH // header
+ + identity.length
+ + salt.length
+ + actions.size();
+ length += TX_POWER_LENGTH;
+ if (txPower != null) { // TX power
+ ltvFieldCount += 1;
+ }
+ this.mLength = length;
+ this.mLtvFieldCount = ltvFieldCount;
+ Preconditions.checkArgument(
+ length <= FAST_ADVERTISEMENT_MAX_LENGTH,
+ "FastAdvertisement exceeds maximum length");
+ }
+
+ /** Returns the version in the advertisement. */
+ @BroadcastRequest.BroadcastVersion
+ public int getVersion() {
+ return BroadcastRequest.PRESENCE_VERSION_V0;
+ }
+
+ /** Returns the identity type in the advertisement. */
+ @PresenceCredential.IdentityType
+ public int getIdentityType() {
+ return mIdentityType;
+ }
+
+ /** Returns the identity bytes in the advertisement. */
+ public byte[] getIdentity() {
+ return mIdentity.clone();
+ }
+
+ /** Returns the salt of the advertisement. */
+ public byte[] getSalt() {
+ return mSalt.clone();
+ }
+
+ /** Returns the actions in the advertisement. */
+ public List<Integer> getActions() {
+ return new ArrayList<>(mActions);
+ }
+
+ /** Returns the adjusted TX Power in the advertisement. Null if not available. */
+ @Nullable
+ public Byte getTxPower() {
+ return mTxPower;
+ }
+
+ /** Returns the length of the advertisement. */
+ public int getLength() {
+ return mLength;
+ }
+
+ /** Returns the count of LTV fields in the advertisement. */
+ public int getLtvFieldCount() {
+ return mLtvFieldCount;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "FastAdvertisement:<VERSION: %s, length: %s, ltvFieldCount: %s, identityType: %s,"
+ + " identity: %s, salt: %s, actions: %s, txPower: %s",
+ getVersion(),
+ getLength(),
+ getLtvFieldCount(),
+ getIdentityType(),
+ Arrays.toString(getIdentity()),
+ Arrays.toString(getSalt()),
+ getActions(),
+ getTxPower());
+ }
+}
diff --git a/nearby/service/java/com/android/server/nearby/presence/FastAdvertisementUtils.java b/nearby/service/java/com/android/server/nearby/presence/FastAdvertisementUtils.java
new file mode 100644
index 0000000..ab0a246
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/presence/FastAdvertisementUtils.java
@@ -0,0 +1,40 @@
+/*
+ * 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 com.android.server.nearby.presence;
+
+import android.nearby.BroadcastRequest;
+
+/**
+ * Provides serialization and deserialization util methods for {@link FastAdvertisement}.
+ */
+public final class FastAdvertisementUtils {
+
+ private static final int VERSION_MASK = 0b11100000;
+
+ private static final int IDENTITY_TYPE_MASK = 0b00001110;
+
+ /**
+ * Constructs the header of a {@link FastAdvertisement}.
+ */
+ public static byte constructHeader(@BroadcastRequest.BroadcastVersion int version,
+ int identityType) {
+ return (byte) (((version << 5) & VERSION_MASK) | ((identityType << 1)
+ & IDENTITY_TYPE_MASK));
+ }
+
+ private FastAdvertisementUtils() {}
+}
diff --git a/nearby/service/java/com/android/server/nearby/presence/PresenceDiscoveryResult.java b/nearby/service/java/com/android/server/nearby/presence/PresenceDiscoveryResult.java
index 7a77aa9..e48074d 100644
--- a/nearby/service/java/com/android/server/nearby/presence/PresenceDiscoveryResult.java
+++ b/nearby/service/java/com/android/server/nearby/presence/PresenceDiscoveryResult.java
@@ -79,10 +79,13 @@
* Converts a presence device from the discovery result.
*/
public PresenceDevice toPresenceDevice() {
- return new PresenceDevice.Builder()
+ return new PresenceDevice.Builder(
+ // Use the public credential hash as the device Id.
+ String.valueOf(mPublicCredential.hashCode()),
+ mSalt,
+ mPublicCredential.getSecretId(),
+ mPublicCredential.getEncryptedMetadata())
.setRssi(mRssi)
- .setSalt(mSalt)
- .setSecretId(mPublicCredential.getSecretId())
.addMedium(NearbyDevice.Medium.BLE).build();
}
diff --git a/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java b/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java
new file mode 100644
index 0000000..3602787
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java
@@ -0,0 +1,121 @@
+/*
+ * 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 com.android.server.nearby.provider;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.le.AdvertiseCallback;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertiseSettings;
+import android.bluetooth.le.BluetoothLeAdvertiser;
+import android.nearby.BroadcastCallback;
+import android.os.ParcelUuid;
+
+import com.android.server.nearby.injector.Injector;
+import com.android.server.nearby.presence.PresenceConstants;
+
+import java.util.concurrent.Executor;
+
+/**
+ * A provider for Bluetooth Low Energy advertisement.
+ */
+public class BleBroadcastProvider extends AdvertiseCallback {
+
+ /**
+ * Listener for Broadcast status changes.
+ */
+ interface BroadcastListener {
+ void onStatusChanged(int status);
+ }
+
+ private final Injector mInjector;
+ private final Executor mExecutor;
+
+ private BroadcastListener mBroadcastListener;
+ private boolean mIsAdvertising;
+
+ BleBroadcastProvider(Injector injector, Executor executor) {
+ mInjector = injector;
+ mExecutor = executor;
+ }
+
+ void start(byte[] advertisementPackets, BroadcastListener listener) {
+ if (mIsAdvertising) {
+ stop();
+ }
+ boolean advertiseStarted = false;
+ BluetoothAdapter adapter = mInjector.getBluetoothAdapter();
+ if (adapter != null) {
+ BluetoothLeAdvertiser bluetoothLeAdvertiser =
+ mInjector.getBluetoothAdapter().getBluetoothLeAdvertiser();
+ if (bluetoothLeAdvertiser != null) {
+ advertiseStarted = true;
+ AdvertiseSettings settings =
+ new AdvertiseSettings.Builder()
+ .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
+ .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
+ .setConnectable(true)
+ .build();
+ AdvertiseData advertiseData =
+ new AdvertiseData.Builder()
+ .addServiceData(new ParcelUuid(PresenceConstants.PRESENCE_UUID),
+ advertisementPackets).build();
+
+ try {
+ mBroadcastListener = listener;
+ bluetoothLeAdvertiser.startAdvertising(settings, advertiseData, this);
+ } catch (NullPointerException | IllegalStateException | SecurityException e) {
+ advertiseStarted = false;
+ }
+ }
+ }
+ if (!advertiseStarted) {
+ listener.onStatusChanged(BroadcastCallback.STATUS_FAILURE);
+ }
+ }
+
+ void stop() {
+ if (mIsAdvertising) {
+ BluetoothAdapter adapter = mInjector.getBluetoothAdapter();
+ if (adapter != null) {
+ BluetoothLeAdvertiser bluetoothLeAdvertiser =
+ mInjector.getBluetoothAdapter().getBluetoothLeAdvertiser();
+ if (bluetoothLeAdvertiser != null) {
+ bluetoothLeAdvertiser.stopAdvertising(this);
+ }
+ }
+ mBroadcastListener = null;
+ mIsAdvertising = false;
+ }
+ }
+
+ @Override
+ public void onStartSuccess(AdvertiseSettings settingsInEffect) {
+ mExecutor.execute(() -> {
+ if (mBroadcastListener != null) {
+ mBroadcastListener.onStatusChanged(BroadcastCallback.STATUS_OK);
+ }
+ mIsAdvertising = true;
+ });
+ }
+
+ @Override
+ public void onStartFailure(int errorCode) {
+ if (mBroadcastListener != null) {
+ mBroadcastListener.onStatusChanged(BroadcastCallback.STATUS_FAILURE);
+ }
+ }
+}
diff --git a/nearby/service/java/com/android/server/nearby/provider/BroadcastProviderManager.java b/nearby/service/java/com/android/server/nearby/provider/BroadcastProviderManager.java
new file mode 100644
index 0000000..72fe29a
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/provider/BroadcastProviderManager.java
@@ -0,0 +1,126 @@
+/*
+ * 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 com.android.server.nearby.provider;
+
+import android.content.Context;
+import android.nearby.BroadcastCallback;
+import android.nearby.BroadcastRequest;
+import android.nearby.IBroadcastListener;
+import android.nearby.PresenceBroadcastRequest;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.nearby.NearbyConfiguration;
+import com.android.server.nearby.injector.Injector;
+import com.android.server.nearby.presence.FastAdvertisement;
+import com.android.server.nearby.util.ForegroundThread;
+
+import java.util.concurrent.Executor;
+
+/**
+ * A manager for nearby broadcasts.
+ */
+public class BroadcastProviderManager implements BleBroadcastProvider.BroadcastListener {
+
+ private static final String TAG = "BroadcastProvider";
+
+ private final Object mLock;
+ private final BleBroadcastProvider mBleBroadcastProvider;
+ private final Executor mExecutor;
+ private final NearbyConfiguration mNearbyConfiguration;
+
+ private IBroadcastListener mBroadcastListener;
+
+ public BroadcastProviderManager(Context context, Injector injector) {
+ this(ForegroundThread.getExecutor(),
+ new BleBroadcastProvider(injector, ForegroundThread.getExecutor()));
+ }
+
+ @VisibleForTesting
+ BroadcastProviderManager(Executor executor, BleBroadcastProvider bleBroadcastProvider) {
+ mExecutor = executor;
+ mBleBroadcastProvider = bleBroadcastProvider;
+ mLock = new Object();
+ mNearbyConfiguration = new NearbyConfiguration();
+ mBroadcastListener = null;
+ }
+
+ /**
+ * Starts a nearby broadcast, the callback is sent through the given listener.
+ */
+ public void startBroadcast(BroadcastRequest broadcastRequest, IBroadcastListener listener) {
+ synchronized (mLock) {
+ NearbyConfiguration configuration = new NearbyConfiguration();
+ if (!configuration.isPresenceBroadcastLegacyEnabled()) {
+ reportBroadcastStatus(listener, BroadcastCallback.STATUS_FAILURE);
+ return;
+ }
+ if (broadcastRequest.getType() != BroadcastRequest.BROADCAST_TYPE_NEARBY_PRESENCE) {
+ reportBroadcastStatus(listener, BroadcastCallback.STATUS_FAILURE);
+ return;
+ }
+ PresenceBroadcastRequest presenceBroadcastRequest =
+ (PresenceBroadcastRequest) broadcastRequest;
+ if (presenceBroadcastRequest.getVersion() != BroadcastRequest.PRESENCE_VERSION_V0) {
+ reportBroadcastStatus(listener, BroadcastCallback.STATUS_FAILURE);
+ return;
+ }
+ FastAdvertisement fastAdvertisement = FastAdvertisement.createFromRequest(
+ presenceBroadcastRequest);
+ byte[] advertisementPackets = fastAdvertisement.toBytes();
+ mBroadcastListener = listener;
+ mExecutor.execute(() -> {
+ mBleBroadcastProvider.start(advertisementPackets, this);
+ });
+ }
+ }
+
+ /**
+ * Stops the nearby broadcast.
+ */
+ public void stopBroadcast(IBroadcastListener listener) {
+ synchronized (mLock) {
+ if (!mNearbyConfiguration.isPresenceBroadcastLegacyEnabled()) {
+ reportBroadcastStatus(listener, BroadcastCallback.STATUS_FAILURE);
+ return;
+ }
+ mBroadcastListener = null;
+ mExecutor.execute(() -> mBleBroadcastProvider.stop());
+ }
+ }
+
+ @Override
+ public void onStatusChanged(int status) {
+ IBroadcastListener listener = null;
+ synchronized (mLock) {
+ listener = mBroadcastListener;
+ }
+ // Don't invoke callback while holding the local lock, as this could cause deadlock.
+ if (listener != null) {
+ reportBroadcastStatus(listener, status);
+ }
+ }
+
+ private void reportBroadcastStatus(IBroadcastListener listener, int status) {
+ try {
+ listener.onStatusChanged(status);
+ } catch (RemoteException exception) {
+ Log.e(TAG, "remote exception when reporting status");
+ }
+ }
+}
diff --git a/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java b/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
index f4db578..35cb5d8 100644
--- a/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
@@ -22,7 +22,7 @@
import android.nearby.FastPairDataProviderBase;
import android.nearby.aidl.ByteArrayParcel;
import android.nearby.aidl.FastPairAccountDevicesMetadataRequestParcel;
-import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataRequestParcel;
+import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataRequestParcel;
import android.nearby.aidl.FastPairEligibleAccountsRequestParcel;
import android.nearby.aidl.FastPairManageAccountDeviceRequestParcel;
import android.nearby.aidl.FastPairManageAccountRequestParcel;
@@ -81,20 +81,20 @@
}
/**
- * Loads FastPairAntispoofkeyDeviceMetadata.
+ * Loads FastPairAntispoofKeyDeviceMetadata.
*
* @throws IllegalStateException If ProxyFastPairDataProvider is not available.
*/
@WorkerThread
@Nullable
- public Rpcs.GetObservedDeviceResponse loadFastPairAntispoofkeyDeviceMetadata(byte[] modelId) {
+ public Rpcs.GetObservedDeviceResponse loadFastPairAntispoofKeyDeviceMetadata(byte[] modelId) {
if (mProxyFastPairDataProvider != null) {
- FastPairAntispoofkeyDeviceMetadataRequestParcel requestParcel =
- new FastPairAntispoofkeyDeviceMetadataRequestParcel();
+ FastPairAntispoofKeyDeviceMetadataRequestParcel requestParcel =
+ new FastPairAntispoofKeyDeviceMetadataRequestParcel();
requestParcel.modelId = modelId;
return Utils.convertToGetObservedDeviceResponse(
mProxyFastPairDataProvider
- .loadFastPairAntispoofkeyDeviceMetadata(requestParcel));
+ .loadFastPairAntispoofKeyDeviceMetadata(requestParcel));
}
throw new IllegalStateException("No ProxyFastPairDataProvider yet constructed");
}
diff --git a/nearby/service/java/com/android/server/nearby/provider/ProxyFastPairDataProvider.java b/nearby/service/java/com/android/server/nearby/provider/ProxyFastPairDataProvider.java
index 36b4478..f0ade6c 100644
--- a/nearby/service/java/com/android/server/nearby/provider/ProxyFastPairDataProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/ProxyFastPairDataProvider.java
@@ -20,14 +20,14 @@
import android.content.Context;
import android.nearby.aidl.FastPairAccountDevicesMetadataRequestParcel;
import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel;
-import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataParcel;
-import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataRequestParcel;
+import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataParcel;
+import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataRequestParcel;
import android.nearby.aidl.FastPairEligibleAccountParcel;
import android.nearby.aidl.FastPairEligibleAccountsRequestParcel;
import android.nearby.aidl.FastPairManageAccountDeviceRequestParcel;
import android.nearby.aidl.FastPairManageAccountRequestParcel;
import android.nearby.aidl.IFastPairAccountDevicesMetadataCallback;
-import android.nearby.aidl.IFastPairAntispoofkeyDeviceMetadataCallback;
+import android.nearby.aidl.IFastPairAntispoofKeyDeviceMetadataCallback;
import android.nearby.aidl.IFastPairDataProvider;
import android.nearby.aidl.IFastPairEligibleAccountsCallback;
import android.nearby.aidl.IFastPairManageAccountCallback;
@@ -218,25 +218,25 @@
}
/**
- * Invokes system api loadFastPairAntispoofkeyDeviceMetadata.
+ * Invokes system api loadFastPairAntispoofKeyDeviceMetadata.
*
* @return the Fast Pair AntispoofKeyDeviceMetadata of a given device.
*/
@WorkerThread
@Nullable
- FastPairAntispoofkeyDeviceMetadataParcel loadFastPairAntispoofkeyDeviceMetadata(
- FastPairAntispoofkeyDeviceMetadataRequestParcel requestParcel) {
+ FastPairAntispoofKeyDeviceMetadataParcel loadFastPairAntispoofKeyDeviceMetadata(
+ FastPairAntispoofKeyDeviceMetadataRequestParcel requestParcel) {
final CountDownLatch waitForCompletionLatch = new CountDownLatch(1);
- final AtomicReference<FastPairAntispoofkeyDeviceMetadataParcel> response =
+ final AtomicReference<FastPairAntispoofKeyDeviceMetadataParcel> response =
new AtomicReference<>();
mServiceMonitor.runOnBinder(new ServiceMonitor.BinderOperation() {
@Override
public void run(IBinder binder) throws RemoteException {
IFastPairDataProvider provider = IFastPairDataProvider.Stub.asInterface(binder);
- IFastPairAntispoofkeyDeviceMetadataCallback callback =
- new IFastPairAntispoofkeyDeviceMetadataCallback.Stub() {
- public void onFastPairAntispoofkeyDeviceMetadataReceived(
- FastPairAntispoofkeyDeviceMetadataParcel metadata) {
+ IFastPairAntispoofKeyDeviceMetadataCallback callback =
+ new IFastPairAntispoofKeyDeviceMetadataCallback.Stub() {
+ public void onFastPairAntispoofKeyDeviceMetadataReceived(
+ FastPairAntispoofKeyDeviceMetadataParcel metadata) {
response.set(metadata);
waitForCompletionLatch.countDown();
}
@@ -245,7 +245,7 @@
waitForCompletionLatch.countDown();
}
};
- provider.loadFastPairAntispoofkeyDeviceMetadata(requestParcel, callback);
+ provider.loadFastPairAntispoofKeyDeviceMetadata(requestParcel, callback);
}
@Override
diff --git a/nearby/service/java/com/android/server/nearby/provider/Utils.java b/nearby/service/java/com/android/server/nearby/provider/Utils.java
index b84dfe9..2c0cde6 100644
--- a/nearby/service/java/com/android/server/nearby/provider/Utils.java
+++ b/nearby/service/java/com/android/server/nearby/provider/Utils.java
@@ -19,7 +19,7 @@
import android.accounts.Account;
import android.annotation.Nullable;
import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel;
-import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataParcel;
+import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataParcel;
import android.nearby.aidl.FastPairDeviceMetadataParcel;
import android.nearby.aidl.FastPairDiscoveryItemParcel;
import android.nearby.aidl.FastPairEligibleAccountParcel;
@@ -298,7 +298,7 @@
}
private static @Nullable Rpcs.Device convertToDevice(
- FastPairAntispoofkeyDeviceMetadataParcel metadata) {
+ FastPairAntispoofKeyDeviceMetadataParcel metadata) {
Rpcs.Device.Builder deviceBuilder = Rpcs.Device.newBuilder();
if (metadata.antiSpoofPublicKey != null) {
@@ -341,7 +341,7 @@
}
private static @Nullable ByteString convertToImage(
- FastPairAntispoofkeyDeviceMetadataParcel metadata) {
+ FastPairAntispoofKeyDeviceMetadataParcel metadata) {
if (metadata.deviceMetadata == null || metadata.deviceMetadata.image == null) {
return null;
}
@@ -350,7 +350,7 @@
}
private static @Nullable Rpcs.ObservedDeviceStrings
- convertToObservedDeviceStrings(FastPairAntispoofkeyDeviceMetadataParcel metadata) {
+ convertToObservedDeviceStrings(FastPairAntispoofKeyDeviceMetadataParcel metadata) {
if (metadata.deviceMetadata == null) {
return null;
}
@@ -455,7 +455,7 @@
static @Nullable Rpcs.GetObservedDeviceResponse
convertToGetObservedDeviceResponse(
- @Nullable FastPairAntispoofkeyDeviceMetadataParcel metadata) {
+ @Nullable FastPairAntispoofKeyDeviceMetadataParcel metadata) {
if (metadata == null) {
return null;
}
diff --git a/nearby/tests/cts/fastpair/Android.bp b/nearby/tests/cts/fastpair/Android.bp
index 6dc1af8..845ed84 100644
--- a/nearby/tests/cts/fastpair/Android.bp
+++ b/nearby/tests/cts/fastpair/Android.bp
@@ -23,12 +23,14 @@
"androidx.test.ext.junit",
"androidx.test.ext.truth",
"androidx.test.rules",
+ "bluetooth-test-util-lib",
"compatibility-device-util-axt",
"ctstestrunner-axt",
"truth-prebuilt",
],
libs: [
"android.test.base",
+ "framework-bluetooth.stubs.module_lib",
"framework-connectivity-t.impl",
],
srcs: ["src/**/*.java"],
diff --git a/nearby/tests/cts/fastpair/AndroidManifest.xml b/nearby/tests/cts/fastpair/AndroidManifest.xml
index e77d70f..ce841f2 100644
--- a/nearby/tests/cts/fastpair/AndroidManifest.xml
+++ b/nearby/tests/cts/fastpair/AndroidManifest.xml
@@ -18,6 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.nearby.cts">
<uses-sdk android:minSdkVersion="32" android:targetSdkVersion="32" />
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application>
diff --git a/nearby/tests/cts/fastpair/AndroidTest.xml b/nearby/tests/cts/fastpair/AndroidTest.xml
index 59cc779..360bbf3 100644
--- a/nearby/tests/cts/fastpair/AndroidTest.xml
+++ b/nearby/tests/cts/fastpair/AndroidTest.xml
@@ -16,7 +16,8 @@
<configuration description="Config for CTS Nearby Fast Pair test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="location" />
- <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <!-- Instant cannot access NearbyManager. -->
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<option name="config-descriptor:metadata" key="parameter" value="all_foldable_states" />
@@ -32,4 +33,4 @@
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
<option name="mainline-module-package-name" value="com.google.android.tethering" />
</object>
-</configuration>
\ No newline at end of file
+</configuration>
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/FastPairDataProviderBaseTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/FastPairDataProviderBaseTest.java
index fd9e294..68418f2 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/FastPairDataProviderBaseTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/FastPairDataProviderBaseTest.java
@@ -25,7 +25,7 @@
import android.accounts.Account;
import android.nearby.FastPairAccountKeyDeviceMetadata;
-import android.nearby.FastPairAntispoofkeyDeviceMetadata;
+import android.nearby.FastPairAntispoofKeyDeviceMetadata;
import android.nearby.FastPairDataProviderBase;
import android.nearby.FastPairDeviceMetadata;
import android.nearby.FastPairDiscoveryItem;
@@ -33,8 +33,8 @@
import android.nearby.aidl.ByteArrayParcel;
import android.nearby.aidl.FastPairAccountDevicesMetadataRequestParcel;
import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel;
-import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataParcel;
-import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataRequestParcel;
+import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataParcel;
+import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataRequestParcel;
import android.nearby.aidl.FastPairDeviceMetadataParcel;
import android.nearby.aidl.FastPairDiscoveryItemParcel;
import android.nearby.aidl.FastPairEligibleAccountParcel;
@@ -42,7 +42,7 @@
import android.nearby.aidl.FastPairManageAccountDeviceRequestParcel;
import android.nearby.aidl.FastPairManageAccountRequestParcel;
import android.nearby.aidl.IFastPairAccountDevicesMetadataCallback;
-import android.nearby.aidl.IFastPairAntispoofkeyDeviceMetadataCallback;
+import android.nearby.aidl.IFastPairAntispoofKeyDeviceMetadataCallback;
import android.nearby.aidl.IFastPairDataProvider;
import android.nearby.aidl.IFastPairEligibleAccountsCallback;
import android.nearby.aidl.IFastPairManageAccountCallback;
@@ -178,9 +178,9 @@
genHappyPathFastPairAccountkeyDeviceMetadata(),
genHappyPathFastPairAccountkeyDeviceMetadata());
- private static final FastPairAntispoofkeyDeviceMetadataRequestParcel
+ private static final FastPairAntispoofKeyDeviceMetadataRequestParcel
FAST_PAIR_ANTI_SPOOF_KEY_DEVICE_METADATA_REQUEST_PARCEL =
- genFastPairAntispoofkeyDeviceMetadataRequestParcel();
+ genFastPairAntispoofKeyDeviceMetadataRequestParcel();
private static final FastPairAccountDevicesMetadataRequestParcel
FAST_PAIR_ACCOUNT_DEVICES_METADATA_REQUEST_PARCEL =
genFastPairAccountDevicesMetadataRequestParcel();
@@ -193,9 +193,9 @@
private static final FastPairManageAccountDeviceRequestParcel
FAST_PAIR_MANAGE_ACCOUNT_DEVICE_REQUEST_PARCEL =
genFastPairManageAccountDeviceRequestParcel();
- private static final FastPairAntispoofkeyDeviceMetadata
+ private static final FastPairAntispoofKeyDeviceMetadata
HAPPY_PATH_FAST_PAIR_ANTI_SPOOF_KEY_DEVICE_METADATA =
- genHappyPathFastPairAntispoofkeyDeviceMetadata();
+ genHappyPathFastPairAntispoofKeyDeviceMetadata();
@Captor private ArgumentCaptor<FastPairEligibleAccountParcel[]>
mFastPairEligibleAccountParcelsArgumentCaptor;
@@ -203,8 +203,8 @@
mFastPairAccountKeyDeviceMetadataParcelsArgumentCaptor;
@Mock private FastPairDataProviderBase mMockFastPairDataProviderBase;
- @Mock private IFastPairAntispoofkeyDeviceMetadataCallback.Stub
- mAntispoofkeyDeviceMetadataCallback;
+ @Mock private IFastPairAntispoofKeyDeviceMetadataCallback.Stub
+ mAntispoofKeyDeviceMetadataCallback;
@Mock private IFastPairAccountDevicesMetadataCallback.Stub mAccountDevicesMetadataCallback;
@Mock private IFastPairEligibleAccountsCallback.Stub mEligibleAccountsCallback;
@Mock private IFastPairManageAccountCallback.Stub mManageAccountCallback;
@@ -225,29 +225,29 @@
@Test
@SdkSuppress(minSdkVersion = 32, codeName = "T")
- public void testHappyPathLoadFastPairAntispoofkeyDeviceMetadata() throws Exception {
+ public void testHappyPathLoadFastPairAntispoofKeyDeviceMetadata() throws Exception {
// AOSP sends calls to OEM via Parcelable.
- mHappyPathFastPairDataProvider.asProvider().loadFastPairAntispoofkeyDeviceMetadata(
+ mHappyPathFastPairDataProvider.asProvider().loadFastPairAntispoofKeyDeviceMetadata(
FAST_PAIR_ANTI_SPOOF_KEY_DEVICE_METADATA_REQUEST_PARCEL,
- mAntispoofkeyDeviceMetadataCallback);
+ mAntispoofKeyDeviceMetadataCallback);
// OEM receives request and verifies that it is as expected.
- final ArgumentCaptor<FastPairDataProviderBase.FastPairAntispoofkeyDeviceMetadataRequest>
- fastPairAntispoofkeyDeviceMetadataRequestCaptor =
+ final ArgumentCaptor<FastPairDataProviderBase.FastPairAntispoofKeyDeviceMetadataRequest>
+ fastPairAntispoofKeyDeviceMetadataRequestCaptor =
ArgumentCaptor.forClass(
- FastPairDataProviderBase.FastPairAntispoofkeyDeviceMetadataRequest.class);
- verify(mMockFastPairDataProviderBase).onLoadFastPairAntispoofkeyDeviceMetadata(
- fastPairAntispoofkeyDeviceMetadataRequestCaptor.capture(),
- any(FastPairDataProviderBase.FastPairAntispoofkeyDeviceMetadataCallback.class));
- ensureHappyPathAsExpected(fastPairAntispoofkeyDeviceMetadataRequestCaptor.getValue());
+ FastPairDataProviderBase.FastPairAntispoofKeyDeviceMetadataRequest.class);
+ verify(mMockFastPairDataProviderBase).onLoadFastPairAntispoofKeyDeviceMetadata(
+ fastPairAntispoofKeyDeviceMetadataRequestCaptor.capture(),
+ any(FastPairDataProviderBase.FastPairAntispoofKeyDeviceMetadataCallback.class));
+ ensureHappyPathAsExpected(fastPairAntispoofKeyDeviceMetadataRequestCaptor.getValue());
// AOSP receives responses and verifies that it is as expected.
- final ArgumentCaptor<FastPairAntispoofkeyDeviceMetadataParcel>
- fastPairAntispoofkeyDeviceMetadataParcelCaptor =
- ArgumentCaptor.forClass(FastPairAntispoofkeyDeviceMetadataParcel.class);
- verify(mAntispoofkeyDeviceMetadataCallback).onFastPairAntispoofkeyDeviceMetadataReceived(
- fastPairAntispoofkeyDeviceMetadataParcelCaptor.capture());
- ensureHappyPathAsExpected(fastPairAntispoofkeyDeviceMetadataParcelCaptor.getValue());
+ final ArgumentCaptor<FastPairAntispoofKeyDeviceMetadataParcel>
+ fastPairAntispoofKeyDeviceMetadataParcelCaptor =
+ ArgumentCaptor.forClass(FastPairAntispoofKeyDeviceMetadataParcel.class);
+ verify(mAntispoofKeyDeviceMetadataCallback).onFastPairAntispoofKeyDeviceMetadataReceived(
+ fastPairAntispoofKeyDeviceMetadataParcelCaptor.capture());
+ ensureHappyPathAsExpected(fastPairAntispoofKeyDeviceMetadataParcelCaptor.getValue());
}
@Test
@@ -345,14 +345,14 @@
@Test
@SdkSuppress(minSdkVersion = 32, codeName = "T")
- public void testErrorPathLoadFastPairAntispoofkeyDeviceMetadata() throws Exception {
- mErrorPathFastPairDataProvider.asProvider().loadFastPairAntispoofkeyDeviceMetadata(
+ public void testErrorPathLoadFastPairAntispoofKeyDeviceMetadata() throws Exception {
+ mErrorPathFastPairDataProvider.asProvider().loadFastPairAntispoofKeyDeviceMetadata(
FAST_PAIR_ANTI_SPOOF_KEY_DEVICE_METADATA_REQUEST_PARCEL,
- mAntispoofkeyDeviceMetadataCallback);
- verify(mMockFastPairDataProviderBase).onLoadFastPairAntispoofkeyDeviceMetadata(
- any(FastPairDataProviderBase.FastPairAntispoofkeyDeviceMetadataRequest.class),
- any(FastPairDataProviderBase.FastPairAntispoofkeyDeviceMetadataCallback.class));
- verify(mAntispoofkeyDeviceMetadataCallback).onError(
+ mAntispoofKeyDeviceMetadataCallback);
+ verify(mMockFastPairDataProviderBase).onLoadFastPairAntispoofKeyDeviceMetadata(
+ any(FastPairDataProviderBase.FastPairAntispoofKeyDeviceMetadataRequest.class),
+ any(FastPairDataProviderBase.FastPairAntispoofKeyDeviceMetadataCallback.class));
+ verify(mAntispoofKeyDeviceMetadataCallback).onError(
eq(ERROR_CODE_BAD_REQUEST), eq(ERROR_STRING));
}
@@ -420,12 +420,12 @@
}
@Override
- public void onLoadFastPairAntispoofkeyDeviceMetadata(
- @NonNull FastPairAntispoofkeyDeviceMetadataRequest request,
- @NonNull FastPairAntispoofkeyDeviceMetadataCallback callback) {
- mMockFastPairDataProviderBase.onLoadFastPairAntispoofkeyDeviceMetadata(
+ public void onLoadFastPairAntispoofKeyDeviceMetadata(
+ @NonNull FastPairAntispoofKeyDeviceMetadataRequest request,
+ @NonNull FastPairAntispoofKeyDeviceMetadataCallback callback) {
+ mMockFastPairDataProviderBase.onLoadFastPairAntispoofKeyDeviceMetadata(
request, callback);
- callback.onFastPairAntispoofkeyDeviceMetadataReceived(
+ callback.onFastPairAntispoofKeyDeviceMetadataReceived(
HAPPY_PATH_FAST_PAIR_ANTI_SPOOF_KEY_DEVICE_METADATA);
}
@@ -478,10 +478,10 @@
}
@Override
- public void onLoadFastPairAntispoofkeyDeviceMetadata(
- @NonNull FastPairAntispoofkeyDeviceMetadataRequest request,
- @NonNull FastPairAntispoofkeyDeviceMetadataCallback callback) {
- mMockFastPairDataProviderBase.onLoadFastPairAntispoofkeyDeviceMetadata(
+ public void onLoadFastPairAntispoofKeyDeviceMetadata(
+ @NonNull FastPairAntispoofKeyDeviceMetadataRequest request,
+ @NonNull FastPairAntispoofKeyDeviceMetadataCallback callback) {
+ mMockFastPairDataProviderBase.onLoadFastPairAntispoofKeyDeviceMetadata(
request, callback);
callback.onError(ERROR_CODE_BAD_REQUEST, ERROR_STRING);
}
@@ -519,11 +519,11 @@
}
}
- /* Generates AntispoofkeyDeviceMetadataRequestParcel. */
- private static FastPairAntispoofkeyDeviceMetadataRequestParcel
- genFastPairAntispoofkeyDeviceMetadataRequestParcel() {
- FastPairAntispoofkeyDeviceMetadataRequestParcel requestParcel =
- new FastPairAntispoofkeyDeviceMetadataRequestParcel();
+ /* Generates AntispoofKeyDeviceMetadataRequestParcel. */
+ private static FastPairAntispoofKeyDeviceMetadataRequestParcel
+ genFastPairAntispoofKeyDeviceMetadataRequestParcel() {
+ FastPairAntispoofKeyDeviceMetadataRequestParcel requestParcel =
+ new FastPairAntispoofKeyDeviceMetadataRequestParcel();
requestParcel.modelId = REQUEST_MODEL_ID;
return requestParcel;
@@ -579,11 +579,11 @@
return requestParcel;
}
- /* Generates Happy Path AntispoofkeyDeviceMetadata. */
- private static FastPairAntispoofkeyDeviceMetadata
- genHappyPathFastPairAntispoofkeyDeviceMetadata() {
- FastPairAntispoofkeyDeviceMetadata.Builder builder =
- new FastPairAntispoofkeyDeviceMetadata.Builder();
+ /* Generates Happy Path AntispoofKeyDeviceMetadata. */
+ private static FastPairAntispoofKeyDeviceMetadata
+ genHappyPathFastPairAntispoofKeyDeviceMetadata() {
+ FastPairAntispoofKeyDeviceMetadata.Builder builder =
+ new FastPairAntispoofKeyDeviceMetadata.Builder();
builder.setAntiSpoofPublicKey(ANTI_SPOOFING_KEY);
builder.setFastPairDeviceMetadata(genHappyPathFastPairDeviceMetadata());
@@ -791,9 +791,9 @@
return builder.build();
}
- /* Verifies Happy Path AntispoofkeyDeviceMetadataRequest. */
+ /* Verifies Happy Path AntispoofKeyDeviceMetadataRequest. */
private static void ensureHappyPathAsExpected(
- FastPairDataProviderBase.FastPairAntispoofkeyDeviceMetadataRequest request) {
+ FastPairDataProviderBase.FastPairAntispoofKeyDeviceMetadataRequest request) {
assertThat(request.getModelId()).isEqualTo(REQUEST_MODEL_ID);
}
@@ -829,9 +829,9 @@
ensureHappyPathAsExpected(request.getAccountKeyDeviceMetadata());
}
- /* Verifies Happy Path AntispoofkeyDeviceMetadataParcel. */
+ /* Verifies Happy Path AntispoofKeyDeviceMetadataParcel. */
private static void ensureHappyPathAsExpected(
- FastPairAntispoofkeyDeviceMetadataParcel metadataParcel) {
+ FastPairAntispoofKeyDeviceMetadataParcel metadataParcel) {
assertThat(metadataParcel).isNotNull();
assertThat(metadataParcel.antiSpoofPublicKey).isEqualTo(ANTI_SPOOFING_KEY);
ensureHappyPathAsExpected(metadataParcel.deviceMetadata);
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
index eedcce1..bfbfbbe 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
@@ -16,13 +16,17 @@
package android.nearby.cts;
+import static android.Manifest.permission.READ_DEVICE_CONFIG;
+import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
import static android.nearby.PresenceCredential.IDENTITY_TYPE_PRIVATE;
+import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
+import android.app.UiAutomation;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.cts.BTAdapterUtils;
import android.content.Context;
import android.nearby.BroadcastCallback;
import android.nearby.BroadcastRequest;
@@ -33,20 +37,22 @@
import android.nearby.ScanCallback;
import android.nearby.ScanRequest;
import android.os.Build;
+import android.provider.DeviceConfig;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
+import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SdkSuppress;
-import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
/**
* TODO(b/215435939) This class doesn't include any logic yet. Because SELinux denies access to
@@ -55,21 +61,27 @@
@RunWith(AndroidJUnit4.class)
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public class NearbyManagerTest {
- private static final byte[] SALT = new byte[] {1, 2};
+ private static final byte[] SALT = new byte[]{1, 2};
private static final byte[] SECRETE_ID = new byte[]{1, 2, 3, 4};
+ private static final byte[] META_DATA_ENCRYPTION_KEY = new byte[14];
private static final byte[] AUTHENTICITY_KEY = new byte[]{0, 1, 1, 1};
private static final int BLE_MEDIUM = 1;
- @Mock private Context mContext;
- @Mock private NearbyManager mNearbyManager;
+ private Context mContext;
+ private NearbyManager mNearbyManager;
+ private UiAutomation mUiAutomation =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation();
@Before
public void setUp() {
- initMocks(this);
+ mUiAutomation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG);
+ DeviceConfig.setProperty(NAMESPACE_TETHERING, "nearby_enable_presence_broadcast_legacy",
+ "true", false);
- when(mContext.getSystemService(Context.NEARBY_SERVICE)).thenReturn(mNearbyManager);
- when(mContext.getContentResolver()).thenReturn(
- InstrumentationRegistry.getInstrumentation().getContext().getContentResolver());
+ mContext = InstrumentationRegistry.getContext();
+ mNearbyManager = mContext.getSystemService(NearbyManager.class);
+
+ enableBluetooth();
}
@Test
@@ -101,19 +113,25 @@
@Test
@SdkSuppress(minSdkVersion = 32, codeName = "T")
- public void testStartStopBroadcast() {
+ public void testStartStopBroadcast() throws InterruptedException {
PrivateCredential credential = new PrivateCredential.Builder(SECRETE_ID, AUTHENTICITY_KEY)
.setIdentityType(IDENTITY_TYPE_PRIVATE)
+ .setMetadataEncryptionKey(META_DATA_ENCRYPTION_KEY)
.build();
BroadcastRequest broadcastRequest =
- new PresenceBroadcastRequest.Builder(Collections.singletonList(BLE_MEDIUM), SALT)
- .setCredential(credential)
+ new PresenceBroadcastRequest.Builder(
+ Collections.singletonList(BLE_MEDIUM), SALT, credential)
+ .addAction(123)
.build();
+ CountDownLatch latch = new CountDownLatch(1);
BroadcastCallback callback = status -> {
+ latch.countDown();
+ assertThat(status).isEqualTo(BroadcastCallback.STATUS_OK);
};
mNearbyManager.startBroadcast(broadcastRequest, Executors.newSingleThreadExecutor(),
callback);
+ latch.await(10, TimeUnit.SECONDS);
mNearbyManager.stopBroadcast(callback);
}
@@ -123,4 +141,12 @@
NearbyManager.setFastPairScanEnabled(mContext, false);
assertThat(NearbyManager.getFastPairScanEnabled(mContext, true)).isFalse();
}
+
+ private void enableBluetooth() {
+ BluetoothManager manager = mContext.getSystemService(BluetoothManager.class);
+ BluetoothAdapter bluetoothAdapter = manager.getAdapter();
+ if (!bluetoothAdapter.isEnabled()) {
+ assertThat(BTAdapterUtils.enableAdapter(bluetoothAdapter, mContext)).isTrue();
+ }
+ }
}
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceBroadcastRequestTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceBroadcastRequestTest.java
index 67d5aa4..3c831a2 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceBroadcastRequestTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceBroadcastRequestTest.java
@@ -66,10 +66,10 @@
.setMetadataEncryptionKey(METADATA_ENCRYPTION_KEY)
.build();
DataElement element = new DataElement(KEY, VALUE);
- mBuilder = new PresenceBroadcastRequest.Builder(Collections.singletonList(BLE_MEDIUM), SALT)
+ mBuilder = new PresenceBroadcastRequest.Builder(Collections.singletonList(BLE_MEDIUM), SALT,
+ credential)
.setTxPower(TX_POWER)
.setVersion(VERSION)
- .setCredential(credential)
.addAction(ACTION_ID)
.addExtendedProperty(element);
}
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceDeviceTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceDeviceTest.java
index e0d9200..5fefc68 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceDeviceTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceDeviceTest.java
@@ -55,19 +55,16 @@
@Test
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testBuilder() {
- PresenceDevice device = new PresenceDevice.Builder()
- .setDeviceType(DEVICE_TYPE)
- .setDeviceId(DEVICE_ID)
- .setDeviceImageUrl(IMAGE_URL)
- .addExtendedProperty(new DataElement(KEY, VALUE))
- .setRssi(RSSI)
- .addMedium(MEDIUM)
- .setName(DEVICE_NAME)
- .setDiscoveryTimestampMillis(DISCOVERY_MILLIS)
- .setSalt(SALT)
- .setSecretId(SECRET_ID)
- .setEncryptedIdentity(ENCRYPTED_IDENTITY)
- .build();
+ PresenceDevice device =
+ new PresenceDevice.Builder(DEVICE_ID, SALT, SECRET_ID, ENCRYPTED_IDENTITY)
+ .setDeviceType(DEVICE_TYPE)
+ .setDeviceImageUrl(IMAGE_URL)
+ .addExtendedProperty(new DataElement(KEY, VALUE))
+ .setRssi(RSSI)
+ .addMedium(MEDIUM)
+ .setName(DEVICE_NAME)
+ .setDiscoveryTimestampMillis(DISCOVERY_MILLIS)
+ .build();
assertThat(device.getDeviceType()).isEqualTo(DEVICE_TYPE);
assertThat(device.getDeviceId()).isEqualTo(DEVICE_ID);
@@ -87,13 +84,13 @@
@Test
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testWriteParcel() {
- PresenceDevice device = new PresenceDevice.Builder()
- .setDeviceId(DEVICE_ID)
- .addExtendedProperty(new DataElement(KEY, VALUE))
- .setRssi(RSSI)
- .addMedium(MEDIUM)
- .setName(DEVICE_NAME)
- .build();
+ PresenceDevice device =
+ new PresenceDevice.Builder(DEVICE_ID, SALT, SECRET_ID, ENCRYPTED_IDENTITY)
+ .addExtendedProperty(new DataElement(KEY, VALUE))
+ .setRssi(RSSI)
+ .addMedium(MEDIUM)
+ .setName(DEVICE_NAME)
+ .build();
Parcel parcel = Parcel.obtain();
device.writeToParcel(parcel, 0);
diff --git a/nearby/tests/multidevices/clients/AndroidManifest.xml b/nearby/tests/multidevices/clients/AndroidManifest.xml
index b6dc5e8..9641756 100644
--- a/nearby/tests/multidevices/clients/AndroidManifest.xml
+++ b/nearby/tests/multidevices/clients/AndroidManifest.xml
@@ -44,7 +44,7 @@
Must stay in the same process as Nearby Discovery Service.
-->
<service
- android:name=".fastpair.seeker.FastPairTestDataProviderService"
+ android:name=".fastpair.seeker.dataprovider.FastPairTestDataProviderService"
android:exported="true"
android:permission="android.permission.WRITE_SECURE_SETTINGS"
android:visibleToInstantApps="true">
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/BluetoothA2dpSinkService.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/BluetoothA2dpSinkService.kt
deleted file mode 100644
index f65dfab..0000000
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/BluetoothA2dpSinkService.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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.nearby.multidevices.fastpair.provider
-
-import android.Manifest.permission.BLUETOOTH_CONNECT
-import android.Manifest.permission.BLUETOOTH_SCAN
-import android.annotation.TargetApi
-import android.bluetooth.BluetoothClass
-import android.bluetooth.BluetoothDevice
-import android.bluetooth.BluetoothManager
-import android.bluetooth.BluetoothProfile
-import android.content.Context
-import android.os.Build
-import androidx.annotation.RequiresPermission
-import androidx.annotation.VisibleForTesting
-
-/** Maintains an environment for Bluetooth A2DP sink profile. */
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
-class BluetoothA2dpSinkService(private val context: Context) {
- private val bluetoothAdapter =
- (context.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager)?.adapter!!
- private var a2dpSinkProxy: BluetoothProfile? = null
-
- /**
- * Starts the Bluetooth A2DP sink profile proxy.
- *
- * @param onServiceConnected the callback for the first time onServiceConnected.
- */
- fun start(onServiceConnected: () -> Unit) {
- // Get the A2DP proxy before continuing with initialization.
- bluetoothAdapter.getProfileProxy(
- context,
- object : BluetoothProfile.ServiceListener {
- override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
- // When Bluetooth turns off and then on again, this is called again. But we only care
- // the first time. There doesn't seem to be a way to unregister our listener.
- if (a2dpSinkProxy == null) {
- a2dpSinkProxy = proxy
- onServiceConnected()
- }
- }
-
- override fun onServiceDisconnected(profile: Int) {}
- },
- BLUETOOTH_PROFILE_A2DP_SINK
- )
- }
-
- /**
- * Checks the device is paired or not.
- *
- * @param remoteBluetoothDevice the device to check is paired or not.
- */
- @RequiresPermission(BLUETOOTH_CONNECT)
- fun isPaired(remoteBluetoothDevice: BluetoothDevice?): Boolean =
- bluetoothAdapter.bondedDevices.contains(remoteBluetoothDevice)
-
- /**
- * Gets the current Bluetooth scan mode of the local Bluetooth adapter.
- */
- @RequiresPermission(BLUETOOTH_SCAN)
- fun getScanMode(): Int = bluetoothAdapter.scanMode
-
- /**
- * Clears the bounded devices.
- *
- * @param removeBondDevice the callback to remove bounded devices.
- */
- @RequiresPermission(BLUETOOTH_CONNECT)
- fun clearBoundedDevices(removeBondDevice: (BluetoothDevice) -> Unit) {
- for (device in bluetoothAdapter.bondedDevices) {
- if (device.bluetoothClass.majorDeviceClass == BluetoothClass.Device.Major.PHONE) {
- removeBondDevice(device)
- }
- }
- }
-
- /**
- * Clears the connected but unbounded devices.
- *
- * Sometimes a device will still be connected even though it's not bonded. :( Clear that too.
- *
- * @param disconnectDevice the callback to clear connected but unbounded devices.
- */
- fun clearConnectedUnboundedDevices(
- disconnectDevice: (BluetoothProfile, BluetoothDevice) -> Unit,
- ) {
- for (device in a2dpSinkProxy!!.connectedDevices) {
- disconnectDevice(a2dpSinkProxy!!, device)
- }
- }
-
- companion object {
- /** Hidden SystemApi field in [android.bluetooth.BluetoothProfile] interface. */
- @VisibleForTesting
- const val BLUETOOTH_PROFILE_A2DP_SINK = 11
- }
-}
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/BluetoothStateChangeReceiver.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/BluetoothStateChangeReceiver.kt
deleted file mode 100644
index f9c77f7..0000000
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/BluetoothStateChangeReceiver.kt
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.nearby.multidevices.fastpair.provider
-
-import android.Manifest.permission.BLUETOOTH
-import android.Manifest.permission.BLUETOOTH_CONNECT
-import android.bluetooth.BluetoothAdapter
-import android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE
-import android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE
-import android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE
-import android.bluetooth.BluetoothDevice
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.util.Log
-import androidx.annotation.RequiresPermission
-import androidx.annotation.VisibleForTesting
-
-/** Processes the state of the local Bluetooth adapter. */
-class BluetoothStateChangeReceiver(private val context: Context) : BroadcastReceiver() {
- @VisibleForTesting
- var listener: EventListener? = null
-
- /**
- * Registers this Bluetooth state change receiver.
- *
- * @param listener the listener for Bluetooth state events.
- */
- fun register(listener: EventListener) {
- this.listener = listener
- val bondStateFilter =
- IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED).apply {
- addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)
- addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED)
- }
- context.registerReceiver(
- this,
- bondStateFilter,
- /* broadcastPermission= */ null,
- /* scheduler= */ null
- )
- }
-
- /** Unregisters this Bluetooth state change receiver. */
- fun unregister() {
- context.unregisterReceiver(this)
- this.listener = null
- }
-
- /**
- * Callback method for receiving Intent broadcast for Bluetooth state.
- *
- * See [android.content.BroadcastReceiver#onReceive].
- *
- * @param context the Context in which the receiver is running.
- * @param intent the Intent being received.
- */
- @RequiresPermission(allOf = [BLUETOOTH, BLUETOOTH_CONNECT])
- override fun onReceive(context: Context, intent: Intent) {
- Log.i(TAG, "BluetoothStateChangeReceiver received intent, action=${intent.action}")
-
- when (intent.action) {
- BluetoothAdapter.ACTION_SCAN_MODE_CHANGED -> {
- val scanMode =
- intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, SCAN_MODE_NONE)
- val scanModeStr = scanModeToString(scanMode)
- Log.i(TAG, "ACTION_SCAN_MODE_CHANGED, the new scanMode: $scanModeStr")
- listener?.onScanModeChange(scanModeStr)
- }
- BluetoothDevice.ACTION_BOND_STATE_CHANGED -> {
- val remoteDevice = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
- val remoteDeviceString =
- if (remoteDevice != null) "${remoteDevice.name}-${remoteDevice.address}" else "none"
- var boundStateString = "ERROR"
- when (intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR)) {
- BluetoothDevice.BOND_NONE -> {
- boundStateString = "BOND_NONE"
- }
- BluetoothDevice.BOND_BONDING -> {
- boundStateString = "BOND_BONDING"
- }
- BluetoothDevice.BOND_BONDED -> {
- boundStateString = "BOND_BONDED"
- }
- }
- Log.i(
- TAG,
- "The bound state of the remote device ($remoteDeviceString) change to $boundStateString."
- )
- }
- else -> {}
- }
- }
-
- private fun scanModeToString(scanMode: Int): String {
- return when (scanMode) {
- SCAN_MODE_CONNECTABLE_DISCOVERABLE -> "DISCOVERABLE"
- SCAN_MODE_CONNECTABLE -> "CONNECTABLE"
- SCAN_MODE_NONE -> "NOT CONNECTABLE"
- else -> "UNKNOWN($scanMode)"
- }
- }
-
- /** Interface for listening the events from Bluetooth adapter. */
- interface EventListener {
- /**
- * Reports the current scan mode of the local Adapter.
- *
- * @param mode the current scan mode in string.
- */
- fun onScanModeChange(mode: String)
- }
-
- companion object {
- private const val TAG = "BluetoothStateReceiver"
- }
-}
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorController.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorController.kt
new file mode 100644
index 0000000..e700144
--- /dev/null
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorController.kt
@@ -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.nearby.multidevices.fastpair.provider
+
+import android.bluetooth.le.AdvertiseSettings
+import android.content.Context
+import android.nearby.fastpair.provider.FastPairSimulator
+import android.nearby.fastpair.provider.bluetooth.BluetoothController
+import com.google.android.mobly.snippet.util.Log
+import com.google.common.io.BaseEncoding
+
+class FastPairProviderSimulatorController(private val context: Context) :
+ FastPairSimulator.AdvertisingChangedCallback, BluetoothController.EventListener {
+ private lateinit var bluetoothController: BluetoothController
+ private lateinit var eventListener: EventListener
+ private var simulator: FastPairSimulator? = null
+
+ fun setupProviderSimulator(listener: EventListener) {
+ eventListener = listener
+
+ bluetoothController = BluetoothController(context, this)
+ bluetoothController.registerBluetoothStateReceiver()
+ bluetoothController.enableBluetooth()
+ bluetoothController.connectA2DPSinkProfile()
+ }
+
+ fun teardownProviderSimulator() {
+ simulator?.destroy()
+ bluetoothController.unregisterBluetoothStateReceiver()
+ }
+
+ fun startModelIdAdvertising(
+ modelId: String,
+ antiSpoofingKeyString: String,
+ listener: EventListener
+ ) {
+ eventListener = listener
+
+ val antiSpoofingKey = BaseEncoding.base64().decode(antiSpoofingKeyString)
+ simulator = FastPairSimulator(
+ context, FastPairSimulator.Options.builder(modelId)
+ .setAdvertisingModelId(modelId)
+ .setBluetoothAddress(null)
+ .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
+ .setAdvertisingChangedCallback(this)
+ .setAntiSpoofingPrivateKey(antiSpoofingKey)
+ .setUseRandomSaltForAccountKeyRotation(false)
+ .setDataOnlyConnection(false)
+ .setShowsPasskeyConfirmation(false)
+ .setRemoveAllDevicesDuringPairing(true)
+ .build()
+ )
+
+ // TODO(b/222070055): Workaround the FATAL EXCEPTION after the end of initial pairing.
+ simulator!!.setSuppressSubsequentPairingNotification(true)
+ }
+
+ fun getProviderSimulatorBleAddress() = simulator!!.bleAddress!!
+
+ /**
+ * Called when we change our BLE advertisement.
+ *
+ * @param isAdvertising the advertising status.
+ */
+ override fun onAdvertisingChanged(isAdvertising: Boolean) {
+ Log.i("FastPairSimulator onAdvertisingChanged(isAdvertising: $isAdvertising)")
+ eventListener.onAdvertisingChange(isAdvertising)
+ }
+
+ /** The callback for the first onServiceConnected of A2DP sink profile. */
+ override fun onA2DPSinkProfileConnected() {
+ eventListener.onA2DPSinkProfileConnected()
+ }
+
+ /**
+ * Reports the current bond state of the remote device.
+ *
+ * @param bondState the bond state of the remote device.
+ */
+ override fun onBondStateChanged(bondState: Int) {
+ }
+
+ /**
+ * Reports the current connection state of the remote device.
+ *
+ * @param connectionState the bond state of the remote device.
+ */
+ override fun onConnectionStateChanged(connectionState: Int) {
+ }
+
+ /**
+ * Reports the current scan mode of the local Adapter.
+ *
+ * @param mode the current scan mode of the local Adapter.
+ */
+ override fun onScanModeChange(mode: Int) {
+ eventListener.onScanModeChange(FastPairSimulator.scanModeToString(mode))
+ }
+
+ /** Interface for listening the events from Fast Pair Provider Simulator. */
+ interface EventListener {
+ /** Reports the first onServiceConnected of A2DP sink profile. */
+ fun onA2DPSinkProfileConnected()
+
+ /**
+ * Reports the current scan mode of the local Adapter.
+ *
+ * @param mode the current scan mode in string.
+ */
+ fun onScanModeChange(mode: String)
+
+ /**
+ * Indicates the advertising state of the Fast Pair provider simulator has changed.
+ *
+ * @param isAdvertising the current advertising state, true if advertising otherwise false.
+ */
+ fun onAdvertisingChange(isAdvertising: Boolean)
+ }
+}
\ No newline at end of file
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorSnippet.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorSnippet.kt
index a03085c..39edfe4 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorSnippet.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorSnippet.kt
@@ -17,77 +17,55 @@
package android.nearby.multidevices.fastpair.provider
import android.annotation.TargetApi
-import android.bluetooth.le.AdvertiseSettings
import android.content.Context
import android.os.Build
import androidx.test.platform.app.InstrumentationRegistry
-import android.nearby.fastpair.provider.FastPairSimulator
import com.google.android.mobly.snippet.Snippet
import com.google.android.mobly.snippet.rpc.AsyncRpc
import com.google.android.mobly.snippet.rpc.Rpc
-import com.google.android.mobly.snippet.util.Log
-import com.google.common.io.BaseEncoding.base64
/** Expose Mobly RPC methods for Python side to simulate fast pair provider role. */
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
class FastPairProviderSimulatorSnippet : Snippet {
private val context: Context = InstrumentationRegistry.getInstrumentation().context
- private val bluetoothA2dpSinkService = BluetoothA2dpSinkService(context)
- private val bluetoothStateChangeReceiver = BluetoothStateChangeReceiver(context)
- private lateinit var fastPairSimulator: FastPairSimulator
- private lateinit var providerStatusEvents: ProviderStatusEvents
+ private val fastPairProviderSimulatorController = FastPairProviderSimulatorController(context)
+
+ /** Sets up the Fast Pair provider simulator. */
+ @AsyncRpc(description = "Sets up FP provider simulator.")
+ fun setupProviderSimulator(callbackId: String) {
+ fastPairProviderSimulatorController.setupProviderSimulator(ProviderStatusEvents(callbackId))
+ }
/**
- * Starts the Fast Pair provider simulator.
+ * Starts model id advertising for scanning and initial pairing.
*
* @param callbackId the callback ID corresponding to the
* [FastPairProviderSimulatorSnippet#startProviderSimulator] call that started the scanning.
* @param modelId a 3-byte hex string for seeker side to recognize the device (ex: 0x00000C).
* @param antiSpoofingKeyString a public key for registered headsets.
*/
- @AsyncRpc(description = "Starts FP provider simulator for seekers to discover.")
- fun startProviderSimulator(callbackId: String, modelId: String, antiSpoofingKeyString: String) {
- providerStatusEvents = ProviderStatusEvents(callbackId)
- bluetoothStateChangeReceiver.register(listener = providerStatusEvents)
- bluetoothA2dpSinkService.start { createFastPairSimulator(modelId, antiSpoofingKeyString) }
+ @AsyncRpc(description = "Starts model id advertising for scanning and initial pairing.")
+ fun startModelIdAdvertising(
+ callbackId: String,
+ modelId: String,
+ antiSpoofingKeyString: String
+ ) {
+ fastPairProviderSimulatorController.startModelIdAdvertising(
+ modelId,
+ antiSpoofingKeyString,
+ ProviderStatusEvents(callbackId)
+ )
}
- /** Stops the Fast Pair provider simulator. */
- @Rpc(description = "Stops FP provider simulator.")
- fun stopProviderSimulator() {
- fastPairSimulator.destroy()
- bluetoothStateChangeReceiver.unregister()
+ /** Tears down the Fast Pair provider simulator. */
+ @Rpc(description = "Tears down FP provider simulator.")
+ fun teardownProviderSimulator() {
+ fastPairProviderSimulatorController.teardownProviderSimulator()
}
/** Gets BLE mac address of the Fast Pair provider simulator. */
@Rpc(description = "Gets BLE mac address of the Fast Pair provider simulator.")
fun getBluetoothLeAddress(): String {
- return fastPairSimulator.bleAddress!!
- }
-
- private fun createFastPairSimulator(modelId: String, antiSpoofingKeyString: String) {
- val antiSpoofingKey = base64().decode(antiSpoofingKeyString)
- fastPairSimulator =
- FastPairSimulator(
- context,
- FastPairSimulator.Options.builder(
- modelId
- )
- .setAdvertisingModelId(modelId)
- .setBluetoothAddress(null)
- .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
- .setCallback {
- val isAdvertising = fastPairSimulator.isAdvertising
- Log.i("FastPairSimulator callback(), isAdvertising: $isAdvertising")
- providerStatusEvents.onAdvertisingChange(isAdvertising)
- }
- .setAntiSpoofingPrivateKey(antiSpoofingKey)
- .setUseRandomSaltForAccountKeyRotation(false)
- .setDataOnlyConnection(false)
- .setIsMemoryTest(false)
- .setShowsPasskeyConfirmation(false)
- .setRemoveAllDevicesDuringPairing(true)
- .build()
- )
+ return fastPairProviderSimulatorController.getProviderSimulatorBleAddress()
}
}
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/ProviderStatusEvents.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/ProviderStatusEvents.kt
index eef4b8b..efa4f02 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/ProviderStatusEvents.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/ProviderStatusEvents.kt
@@ -20,7 +20,12 @@
/** The Mobly snippet events to report to the Python side. */
class ProviderStatusEvents(private val callbackId: String) :
- BluetoothStateChangeReceiver.EventListener {
+ FastPairProviderSimulatorController.EventListener {
+
+ /** Reports the first onServiceConnected of A2DP sink profile. */
+ override fun onA2DPSinkProfileConnected() {
+ postSnippetEvent(callbackId, "onA2DPSinkProfileConnected") {}
+ }
/**
* Indicates the Bluetooth scan mode of the Fast Pair provider simulator has changed.
@@ -36,7 +41,7 @@
*
* @param isAdvertising the current advertising state, true if advertising otherwise false.
*/
- fun onAdvertisingChange(isAdvertising: Boolean) {
+ override fun onAdvertisingChange(isAdvertising: Boolean) {
postSnippetEvent(callbackId, "onAdvertisingChange") {
putBoolean("isAdvertising", isAdvertising)
}
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt
index 6c28b97..4381d51 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt
@@ -21,6 +21,7 @@
import android.nearby.NearbyManager
import android.nearby.ScanCallback
import android.nearby.ScanRequest
+import android.nearby.multidevices.fastpair.seeker.dataprovider.FastPairTestDataCache
import androidx.test.core.app.ApplicationProvider
import com.google.android.mobly.snippet.Snippet
import com.google.android.mobly.snippet.rpc.AsyncRpc
@@ -72,15 +73,15 @@
appContext.sendBroadcast(scanIntent)
}
- /** Puts a model id to FastPairAntiSpoofKeyDeviceMetadata pair into test data cache.
+ /** Puts a model id to FastPairAntispoofKeyDeviceMetadata pair into test data cache.
*
* @param modelId a string of model id to be associated with.
- * @param json a string of FastPairAntiSpoofKeyDeviceMetadata JSON object.
+ * @param json a string of FastPairAntispoofKeyDeviceMetadata JSON object.
*/
- @Rpc(description = "Puts a model id to FastPairAntiSpoofKeyDeviceMetadata pair into test data cache.")
- fun putAntiSpoofKeyDeviceMetadata(modelId: String, json: String) {
- Log.i("Puts a model id to FastPairAntiSpoofKeyDeviceMetadata pair into test data cache.")
- FastPairTestDataCache.putAntiSpoofKeyDeviceMetadata(modelId, json)
+ @Rpc(description = "Puts a model id to FastPairAntispoofKeyDeviceMetadata pair into test data cache.")
+ fun putAntispoofKeyDeviceMetadata(modelId: String, json: String) {
+ Log.i("Puts a model id to FastPairAntispoofKeyDeviceMetadata pair into test data cache.")
+ FastPairTestDataCache.putAntispoofKeyDeviceMetadata(modelId, json)
}
/** Puts an array of FastPairAccountKeyDeviceMetadata into test data cache.
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataCache.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/dataprovider/FastPairTestDataCache.kt
similarity index 94%
rename from nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataCache.kt
rename to nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/dataprovider/FastPairTestDataCache.kt
index 28523c1..481f407 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataCache.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/dataprovider/FastPairTestDataCache.kt
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package android.nearby.multidevices.fastpair.seeker
+package android.nearby.multidevices.fastpair.seeker.dataprovider
import android.nearby.FastPairAccountKeyDeviceMetadata
-import android.nearby.FastPairAntispoofkeyDeviceMetadata
+import android.nearby.FastPairAntispoofKeyDeviceMetadata
import android.nearby.FastPairDeviceMetadata
import android.nearby.FastPairDiscoveryItem
-import com.google.common.io.BaseEncoding.base64
+import com.google.common.io.BaseEncoding
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
@@ -28,8 +28,8 @@
object FastPairTestDataCache {
private val gson = Gson()
val accountKeyDeviceMetadata = mutableListOf<FastPairAccountKeyDeviceMetadata>()
- val antiSpoofKeyDeviceMetadataMap =
- mutableMapOf<String, FastPairAntispoofkeyDeviceMetadata>()
+ val antispoofKeyDeviceMetadataMap =
+ mutableMapOf<String, FastPairAntispoofKeyDeviceMetadata>()
fun putAccountKeyDeviceMetadata(json: String) {
accountKeyDeviceMetadata +=
@@ -40,15 +40,15 @@
fun dumpAccountKeyDeviceMetadata(): String =
gson.toJson(accountKeyDeviceMetadata.map { FastPairAccountKeyDeviceMetadataData(it) })
- fun putAntiSpoofKeyDeviceMetadata(modelId: String, json: String) {
- antiSpoofKeyDeviceMetadataMap[modelId] =
- gson.fromJson(json, FastPairAntiSpoofKeyDeviceMetadataData::class.java)
- .toFastPairAntispoofkeyDeviceMetadata()
+ fun putAntispoofKeyDeviceMetadata(modelId: String, json: String) {
+ antispoofKeyDeviceMetadataMap[modelId] =
+ gson.fromJson(json, FastPairAntispoofKeyDeviceMetadataData::class.java)
+ .toFastPairAntispoofKeyDeviceMetadata()
}
fun reset() {
accountKeyDeviceMetadata.clear()
- antiSpoofKeyDeviceMetadataMap.clear()
+ antispoofKeyDeviceMetadataMap.clear()
}
data class FastPairAccountKeyDeviceMetadataData(
@@ -74,12 +74,12 @@
}
}
- data class FastPairAntiSpoofKeyDeviceMetadataData(
+ data class FastPairAntispoofKeyDeviceMetadataData(
@SerializedName("anti_spoofing_public_key_str") val antiSpoofPublicKey: String?,
@SerializedName("fast_pair_device_metadata") val deviceMeta: FastPairDeviceMetadataData?,
) {
- fun toFastPairAntispoofkeyDeviceMetadata(): FastPairAntispoofkeyDeviceMetadata {
- return FastPairAntispoofkeyDeviceMetadata.Builder()
+ fun toFastPairAntispoofKeyDeviceMetadata(): FastPairAntispoofKeyDeviceMetadata {
+ return FastPairAntispoofKeyDeviceMetadata.Builder()
.setAntiSpoofPublicKey(antiSpoofPublicKey?.base64Decode())
.setFastPairDeviceMetadata(deviceMeta?.toFastPairDeviceMetadata())
.build()
@@ -297,6 +297,6 @@
}
}
- private fun String.base64Decode(): ByteArray = base64().decode(this)
- private fun ByteArray.base64Encode(): String = base64().encode(this)
+ private fun String.base64Decode(): ByteArray = BaseEncoding.base64().decode(this)
+ private fun ByteArray.base64Encode(): String = BaseEncoding.base64().encode(this)
}
\ No newline at end of file
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataProvider.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/dataprovider/FastPairTestDataProvider.kt
similarity index 81%
rename from nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataProvider.kt
rename to nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/dataprovider/FastPairTestDataProvider.kt
index a1f0369..3bfd73e 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataProvider.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/dataprovider/FastPairTestDataProvider.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.nearby.multidevices.fastpair.seeker
+package android.nearby.multidevices.fastpair.seeker.dataprovider
import android.accounts.Account
import android.nearby.FastPairDataProviderBase
@@ -23,17 +23,17 @@
class FastPairTestDataProvider : FastPairDataProviderBase(TAG) {
- override fun onLoadFastPairAntispoofkeyDeviceMetadata(
- request: FastPairAntispoofkeyDeviceMetadataRequest,
- callback: FastPairAntispoofkeyDeviceMetadataCallback
+ override fun onLoadFastPairAntispoofKeyDeviceMetadata(
+ request: FastPairAntispoofKeyDeviceMetadataRequest,
+ callback: FastPairAntispoofKeyDeviceMetadataCallback
) {
val requestedModelId = request.modelId.bytesToStringLowerCase()
- Log.d(TAG, "onLoadFastPairAntispoofkeyDeviceMetadata(modelId: $requestedModelId)")
+ Log.d(TAG, "onLoadFastPairAntispoofKeyDeviceMetadata(modelId: $requestedModelId)")
- val fastPairAntiSpoofKeyDeviceMetadata =
- FastPairTestDataCache.antiSpoofKeyDeviceMetadataMap[requestedModelId]
- if (fastPairAntiSpoofKeyDeviceMetadata != null) {
- callback.onFastPairAntispoofkeyDeviceMetadataReceived(fastPairAntiSpoofKeyDeviceMetadata)
+ val fastPairAntispoofKeyDeviceMetadata =
+ FastPairTestDataCache.antispoofKeyDeviceMetadataMap[requestedModelId]
+ if (fastPairAntispoofKeyDeviceMetadata != null) {
+ callback.onFastPairAntispoofKeyDeviceMetadataReceived(fastPairAntispoofKeyDeviceMetadata)
} else {
callback.onError(ERROR_CODE_BAD_REQUEST, "No metadata available for $requestedModelId")
}
@@ -44,7 +44,11 @@
callback: FastPairAccountDevicesMetadataCallback
) {
val requestedAccount = request.account
- Log.d(TAG, "onLoadFastPairAccountDevicesMetadata(account: $requestedAccount)")
+ val requestedAccountKeys = request.accountKeys
+ Log.d(
+ TAG, "onLoadFastPairAccountDevicesMetadata(" +
+ "account: $requestedAccount, accountKeys:$requestedAccountKeys)"
+ )
Log.d(TAG, FastPairTestDataCache.dumpAccountKeyDeviceMetadata())
callback.onFastPairAccountDevicesMetadataReceived(
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataProviderService.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/dataprovider/FastPairTestDataProviderService.kt
similarity index 95%
rename from nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataProviderService.kt
rename to nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/dataprovider/FastPairTestDataProviderService.kt
index 5a1c832..3c0c84c 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairTestDataProviderService.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/dataprovider/FastPairTestDataProviderService.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.nearby.multidevices.fastpair.seeker
+package android.nearby.multidevices.fastpair.seeker.dataprovider
import android.app.Service
import android.content.Intent
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/Android.bp b/nearby/tests/multidevices/clients/test_support/fastpair_provider/Android.bp
index dc3a919..920834a 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/Android.bp
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/Android.bp
@@ -30,6 +30,6 @@
"fast-pair-lite-protos",
"framework-annotations-lib",
"kotlin-stdlib",
- "service-nearby",
+ "service-nearby-pre-jarjar",
],
}
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/AppLogger.java b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/AppLogger.java
deleted file mode 100644
index befc64b..0000000
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/AppLogger.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.nearby.fastpair.provider.simulator.app;
-
-import android.util.Log;
-
-import com.google.errorprone.annotations.FormatMethod;
-
-/** Sends log to logcat with TAG. */
-public class AppLogger {
- private static final String TAG = "FastPairSimulator";
-
- @FormatMethod
- public static void log(String message, Object... objects) {
- Log.i(TAG, String.format(message, objects));
- }
-
- @FormatMethod
- public static void warning(String message, Object... objects) {
- Log.w(TAG, String.format(message, objects));
- }
-
- @FormatMethod
- public static void error(String message, Object... objects) {
- Log.e(TAG, String.format(message, objects));
- }
-
- private AppLogger() {
- }
-}
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/MainActivity.java b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/MainActivity.java
index 9252173..e916c53 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/MainActivity.java
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/MainActivity.java
@@ -20,8 +20,6 @@
import static android.nearby.fastpair.provider.simulator.SimulatorStreamProtocol.Event.Code.BLUETOOTH_STATE_BOND;
import static android.nearby.fastpair.provider.simulator.SimulatorStreamProtocol.Event.Code.BLUETOOTH_STATE_CONNECTION;
import static android.nearby.fastpair.provider.simulator.SimulatorStreamProtocol.Event.Code.BLUETOOTH_STATE_SCAN_MODE;
-import static android.nearby.fastpair.provider.simulator.app.AppLogger.log;
-import static android.nearby.fastpair.provider.simulator.app.AppLogger.warning;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.io.BaseEncoding.base64;
@@ -41,6 +39,7 @@
import android.nearby.fastpair.provider.FastPairSimulator.BatteryValue;
import android.nearby.fastpair.provider.FastPairSimulator.KeyInputCallback;
import android.nearby.fastpair.provider.FastPairSimulator.PasskeyEventCallback;
+import android.nearby.fastpair.provider.bluetooth.BluetoothController;
import android.nearby.fastpair.provider.simulator.SimulatorStreamProtocol.Event;
import android.nearby.fastpair.provider.simulator.testing.RemoteDevice;
import android.nearby.fastpair.provider.simulator.testing.RemoteDevicesManager;
@@ -96,6 +95,9 @@
*/
@SuppressLint("SetTextI18n")
public class MainActivity extends Activity {
+ public static final String TAG = "FastPairProviderSimulatorApp";
+ private final Logger mLogger = new Logger(TAG);
+
/** Device has a display and the ability to input Yes/No. */
private static final int IO_CAPABILITY_IO = 1;
@@ -213,7 +215,7 @@
return;
}
- log("Send data to output stream: %s", eventBuilder.getCode().getNumber());
+ mLogger.log("Send data to output stream: %s", eventBuilder.getCode().getNumber());
mRemoteDevicesManager.writeDataToRemoteDevice(
mRemoteDeviceId,
eventBuilder.build().toByteString(),
@@ -446,7 +448,7 @@
private void setupRemoteDevices() {
if (Strings.isNullOrEmpty(getIntent().getStringExtra(EXTRA_REMOTE_DEVICE_ID))) {
- log("Can't get remote device id");
+ mLogger.log("Can't get remote device id");
return;
}
mRemoteDeviceId = getIntent().getStringExtra(EXTRA_REMOTE_DEVICE_ID);
@@ -463,7 +465,7 @@
REMOTE_DEVICE_OUTPUT_STREAM_URI),
mInputStreamListener));
} catch (IOException e) {
- warning("Failed to create stream IO handler: %s", e);
+ mLogger.log(e, "Failed to create stream IO handler");
}
}
@@ -503,7 +505,7 @@
private boolean setModelId(String modelId) {
String validModelId = getValidModelId(modelId);
if (TextUtils.isEmpty(validModelId)) {
- log("Can't do setModelId because inputted modelId is invalid!");
+ mLogger.log("Can't do setModelId because inputted modelId is invalid!");
return false;
}
@@ -612,7 +614,7 @@
try {
Preconditions.checkArgument(base16().decode(bluetoothAddress).length == 6);
} catch (IllegalArgumentException e) {
- log("Invalid BLUETOOTH_ADDRESS extra (%s), using default.", bluetoothAddress);
+ mLogger.log("Invalid BLUETOOTH_ADDRESS extra (%s), using default.", bluetoothAddress);
bluetoothAddress = null;
}
final String finalBluetoothAddress = bluetoothAddress;
@@ -669,11 +671,10 @@
mAppLaunchSwitch.isChecked() ? MODEL_ID_APP_LAUNCH : modelId)
.setBluetoothAddress(finalBluetoothAddress)
.setTxPowerLevel(toTxPowerLevel(txPower))
- .setCallback(this::updateStatusView)
+ .setAdvertisingChangedCallback(isAdvertising -> updateStatusView())
.setAntiSpoofingPrivateKey(antiSpoofingKey)
.setUseRandomSaltForAccountKeyRotation(useRandomSaltForAccountKeyRotation)
.setDataOnlyConnection(device != null && device.getDataOnlyConnection())
- .setIsMemoryTest(mInputStreamListener != null)
.setShowsPasskeyConfirmation(
device.getDeviceType().equals(DeviceType.ANDROID_AUTO))
.setRemoveAllDevicesDuringPairing(mRemoveAllDevicesDuringPairing)
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulator.java b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulator.java
index 931f2e0..232e84b 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulator.java
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulator.java
@@ -163,6 +163,10 @@
public static final String TAG = "FastPairSimulator";
private final Logger mLogger;
+ private static final int BECOME_DISCOVERABLE_TIMEOUT_SEC = 3;
+
+ private static final int SCAN_MODE_REFRESH_SEC = 30;
+
/**
* Headphones. Generated by
* http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html
@@ -170,18 +174,16 @@
private static final Value CLASS_OF_DEVICE =
new Value(base16().decode("200418"), ByteOrder.BIG_ENDIAN);
- private static final byte[] SUPPORTED_SERVICES_LTV =
- new Ltv(
- TransportDiscoveryService.SERVICE_UUIDS_16_BIT_LIST_TYPE,
- toBytes(ByteOrder.LITTLE_ENDIAN, A2DP_SINK_SERVICE_UUID))
- .getBytes();
+ private static final byte[] SUPPORTED_SERVICES_LTV = new Ltv(
+ TransportDiscoveryService.SERVICE_UUIDS_16_BIT_LIST_TYPE,
+ toBytes(ByteOrder.LITTLE_ENDIAN, A2DP_SINK_SERVICE_UUID)
+ ).getBytes();
private static final byte[] TDS_CONTROL_POINT_RESPONSE_PARAMETER =
Bytes.concat(new byte[]{BLUETOOTH_SIG_ORGANIZATION_ID}, SUPPORTED_SERVICES_LTV);
private static final String SIMULATOR_FAKE_BLE_ADDRESS = "11:22:33:44:55:66";
private static final long ADVERTISING_REFRESH_DELAY_1_MIN = TimeUnit.MINUTES.toMillis(1);
- private static final long ADVERTISING_REFRESH_DELAY_5_MINS = TimeUnit.MINUTES.toMillis(5);
/** The user will be prompted to accept or deny the incoming pairing request */
public static final int PAIRING_VARIANT_CONSENT = 3;
@@ -250,140 +252,139 @@
private final ScheduledExecutorService mExecutor =
Executors.newSingleThreadScheduledExecutor(); // exempt
private final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
- private final BroadcastReceiver mBroadcastReceiver =
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mShouldFailPairing) {
- mLogger.log("Pairing disabled by test app switch");
- return;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (mShouldFailPairing) {
+ mLogger.log("Pairing disabled by test app switch");
+ return;
+ }
+ if (mIsDestroyed) {
+ // Sometimes this receiver does not successfully unregister in destroy()
+ // which causes events to occur after the simulator is stopped, so ignore
+ // those events.
+ mLogger.log("Intent received after simulator destroyed, ignoring");
+ return;
+ }
+ BluetoothDevice device = intent.getParcelableExtra(
+ BluetoothDevice.EXTRA_DEVICE);
+ switch (intent.getAction()) {
+ case BluetoothAdapter.ACTION_SCAN_MODE_CHANGED:
+ if (isDiscoverable()) {
+ mIsDiscoverableLatch.countDown();
}
- if (mIsDestroyed) {
- // Sometimes this receiver does not successfully unregister in destroy()
- // which causes events to occur after the simulator is stopped, so ignore
- // those events.
- mLogger.log("Intent received after simulator destroyed, ignoring");
- return;
+ break;
+ case BluetoothDevice.ACTION_PAIRING_REQUEST:
+ int variant = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
+ ERROR);
+ int key = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, ERROR);
+ mLogger.log(
+ "Pairing request, variant=%d, key=%s", variant,
+ key == ERROR ? "(none)" : key);
+
+ // Prevent Bluetooth Settings from getting the pairing request.
+ abortBroadcast();
+
+ mPairingDevice = device;
+ if (mSecret == null) {
+ // We haven't done the handshake over GATT to agree on the shared
+ // secret. For now, just accept anyway (so we can still simulate
+ // old 1.0 model IDs).
+ mLogger.log("No handshake, auto-accepting anyway.");
+ setPasskeyConfirmation(true);
+ } else if (variant
+ == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION) {
+ // Store the passkey. And check it, since there's a race (see
+ // method for why). Usually this check is a no-op and we'll get
+ // the passkey later over GATT.
+ mLocalPasskey = key;
+ checkPasskey();
+ } else if (variant == PAIRING_VARIANT_DISPLAY_PASSKEY) {
+ if (mPasskeyEventCallback != null) {
+ mPasskeyEventCallback.onPasskeyRequested(
+ FastPairSimulator.this::enterPassKey);
+ } else {
+ mLogger.log("passkeyEventCallback is not set!");
+ enterPassKey(key);
+ }
+ } else if (variant == PAIRING_VARIANT_CONSENT) {
+ setPasskeyConfirmation(true);
+
+ } else if (variant == BluetoothDevice.PAIRING_VARIANT_PIN) {
+ if (mPasskeyEventCallback != null) {
+ mPasskeyEventCallback.onPasskeyRequested(
+ (int pin) -> {
+ byte[] newPin = convertPinToBytes(
+ String.format(Locale.ENGLISH, "%d", pin));
+ mPairingDevice.setPin(newPin);
+ });
+ }
+ } else {
+ // Reject the pairing request if it's not using the Numeric
+ // Comparison (aka Passkey Confirmation) method.
+ setPasskeyConfirmation(false);
}
- BluetoothDevice device = intent.getParcelableExtra(
- BluetoothDevice.EXTRA_DEVICE);
- switch (intent.getAction()) {
- case BluetoothAdapter.ACTION_SCAN_MODE_CHANGED:
- if (isDiscoverable()) {
- mIsDiscoverableLatch.countDown();
+ break;
+ case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
+ int bondState =
+ intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+ BluetoothDevice.BOND_NONE);
+ mLogger.log("Bond state to %s changed to %d", device, bondState);
+ switch (bondState) {
+ case BluetoothDevice.BOND_BONDING:
+ // If we've started bonding, we shouldn't be advertising.
+ mAdvertiser.stopAdvertising();
+ // Not discoverable anymore, but still connectable.
+ setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ break;
+ case BluetoothDevice.BOND_BONDED:
+ // Once bonded, advertise the account keys.
+ mAdvertiser.startAdvertising(accountKeysServiceData());
+ setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+
+ // If it is subsequent pair, we need to add paired device here.
+ if (mIsSubsequentPair
+ && mSecret != null
+ && mSecret.length == AES_BLOCK_LENGTH) {
+ addAccountKey(mSecret, mPairingDevice);
}
break;
- case BluetoothDevice.ACTION_PAIRING_REQUEST:
- int variant = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
- ERROR);
- int key = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, ERROR);
- mLogger.log(
- "Pairing request, variant=%d, key=%s", variant,
- key == ERROR ? "(none)" : key);
-
- // Prevent Bluetooth Settings from getting the pairing request.
- abortBroadcast();
-
- mPairingDevice = device;
- if (mSecret == null) {
- // We haven't done the handshake over GATT to agree on the shared
- // secret. For now, just accept anyway (so we can still simulate
- // old 1.0 model IDs).
- mLogger.log("No handshake, auto-accepting anyway.");
- setPasskeyConfirmation(true);
- } else if (variant
- == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION) {
- // Store the passkey. And check it, since there's a race (see
- // method for why). Usually this check is a no-op and we'll get
- // the passkey later over GATT.
- mLocalPasskey = key;
- checkPasskey();
- } else if (variant == PAIRING_VARIANT_DISPLAY_PASSKEY) {
- if (mPasskeyEventCallback != null) {
- mPasskeyEventCallback.onPasskeyRequested(
- FastPairSimulator.this::enterPassKey);
- } else {
- mLogger.log("passkeyEventCallback is not set!");
- enterPassKey(key);
- }
- } else if (variant == PAIRING_VARIANT_CONSENT) {
- setPasskeyConfirmation(true);
-
- } else if (variant == BluetoothDevice.PAIRING_VARIANT_PIN) {
- if (mPasskeyEventCallback != null) {
- mPasskeyEventCallback.onPasskeyRequested(
- (int pin) -> {
- byte[] newPin = convertPinToBytes(
- String.format(Locale.ENGLISH, "%d", pin));
- mPairingDevice.setPin(newPin);
- });
- }
- } else {
- // Reject the pairing request if it's not using the Numeric
- // Comparison (aka Passkey Confirmation) method.
- setPasskeyConfirmation(false);
- }
- break;
- case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
- int bondState =
- intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
- BluetoothDevice.BOND_NONE);
- mLogger.log("Bond state to %s changed to %d", device, bondState);
- switch (bondState) {
- case BluetoothDevice.BOND_BONDING:
- // If we've started bonding, we shouldn't be advertising.
- mAdvertiser.stopAdvertising();
- // Not discoverable anymore, but still connectable.
- setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
- break;
- case BluetoothDevice.BOND_BONDED:
- // Once bonded, advertise the account keys.
- mAdvertiser.startAdvertising(accountKeysServiceData());
- setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
-
- // If it is subsequent pair, we need to add paired device here.
- if (mIsSubsequentPair
- && mSecret != null
- && mSecret.length == AES_BLOCK_LENGTH) {
- addAccountKey(mSecret, mPairingDevice);
- }
- break;
- case BluetoothDevice.BOND_NONE:
- // If the bonding process fails, we should be advertising again.
- mAdvertiser.startAdvertising(getServiceData());
- break;
- default:
- break;
- }
- break;
- case BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED:
- mLogger.log(
- "Connection state to %s changed to %d",
- device,
- intent.getIntExtra(
- BluetoothAdapter.EXTRA_CONNECTION_STATE,
- BluetoothAdapter.STATE_DISCONNECTED));
- break;
- case BluetoothAdapter.ACTION_STATE_CHANGED:
- int state = intent.getIntExtra(EXTRA_STATE, -1);
- mLogger.log("Bluetooth adapter state=%s", state);
- switch (state) {
- case STATE_ON:
- startRfcommServer();
- break;
- case STATE_OFF:
- stopRfcommServer();
- break;
- default: // fall out
- }
+ case BluetoothDevice.BOND_NONE:
+ // If the bonding process fails, we should be advertising again.
+ mAdvertiser.startAdvertising(getServiceData());
break;
default:
- mLogger.log(new IllegalArgumentException(intent.toString()),
- "Received unexpected intent");
break;
}
- }
- };
+ break;
+ case BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED:
+ mLogger.log(
+ "Connection state to %s changed to %d",
+ device,
+ intent.getIntExtra(
+ BluetoothAdapter.EXTRA_CONNECTION_STATE,
+ BluetoothAdapter.STATE_DISCONNECTED));
+ break;
+ case BluetoothAdapter.ACTION_STATE_CHANGED:
+ int state = intent.getIntExtra(EXTRA_STATE, -1);
+ mLogger.log("Bluetooth adapter state=%s", state);
+ switch (state) {
+ case STATE_ON:
+ startRfcommServer();
+ break;
+ case STATE_OFF:
+ stopRfcommServer();
+ break;
+ default: // fall out
+ }
+ break;
+ default:
+ mLogger.log(new IllegalArgumentException(intent.toString()),
+ "Received unexpected intent");
+ break;
+ }
+ }
+ };
@Nullable
private byte[] convertPinToBytes(@Nullable String pin) {
@@ -508,7 +509,6 @@
@Nullable
private CountDownLatch mWriteNameCountDown;
private final RfcommServer mRfcommServer = new RfcommServer();
- private final boolean mDataOnlyConnection;
private boolean mSupportDynamicBufferSize = false;
private NotifiableGattServlet mBeaconActionsServlet;
private final FastPairSimulatorDatabase mFastPairSimulatorDatabase;
@@ -570,10 +570,14 @@
this.mUseLogFullEvent = useLogFullEvent;
}
- /** An optional way to get status updates. */
- public interface Callback {
- /** Called when we change our BLE advertisement. */
- void onAdvertisingChanged();
+ /** An optional way to get advertising status updates. */
+ public interface AdvertisingChangedCallback {
+ /**
+ * Called when we change our BLE advertisement.
+ *
+ * @param isAdvertising the advertising status.
+ */
+ void onAdvertisingChanged(boolean isAdvertising);
}
/** A way for tests to get callbacks when passkey confirmation is invoked. */
@@ -618,7 +622,7 @@
private final boolean mEnableNameCharacteristic;
- private final Callback mCallback;
+ private final AdvertisingChangedCallback mAdvertisingChangedCallback;
private final boolean mIncludeTransportDataDescriptor;
@@ -627,8 +631,6 @@
private final boolean mUseRandomSaltForAccountKeyRotation;
- private final boolean mIsMemoryTest;
-
private final boolean mBecomeDiscoverable;
private final boolean mShowsPasskeyConfirmation;
@@ -648,11 +650,10 @@
boolean dataOnlyConnection,
int txPowerLevel,
boolean enableNameCharacteristic,
- Callback callback,
+ AdvertisingChangedCallback advertisingChangedCallback,
boolean includeTransportDataDescriptor,
@Nullable byte[] antiSpoofingPrivateKey,
boolean useRandomSaltForAccountKeyRotation,
- boolean isMemoryTest,
boolean becomeDiscoverable,
boolean showsPasskeyConfirmation,
boolean enableBeaconActionsCharacteristic,
@@ -665,11 +666,10 @@
this.mDataOnlyConnection = dataOnlyConnection;
this.mTxPowerLevel = txPowerLevel;
this.mEnableNameCharacteristic = enableNameCharacteristic;
- this.mCallback = callback;
+ this.mAdvertisingChangedCallback = advertisingChangedCallback;
this.mIncludeTransportDataDescriptor = includeTransportDataDescriptor;
this.mAntiSpoofingPrivateKey = antiSpoofingPrivateKey;
this.mUseRandomSaltForAccountKeyRotation = useRandomSaltForAccountKeyRotation;
- this.mIsMemoryTest = isMemoryTest;
this.mBecomeDiscoverable = becomeDiscoverable;
this.mShowsPasskeyConfirmation = showsPasskeyConfirmation;
this.mEnableBeaconActionsCharacteristic = enableBeaconActionsCharacteristic;
@@ -708,8 +708,8 @@
return mEnableNameCharacteristic;
}
- public Callback getCallback() {
- return mCallback;
+ public AdvertisingChangedCallback getAdvertisingChangedCallback() {
+ return mAdvertisingChangedCallback;
}
public boolean getIncludeTransportDataDescriptor() {
@@ -725,10 +725,6 @@
return mUseRandomSaltForAccountKeyRotation;
}
- public boolean getIsMemoryTest() {
- return mIsMemoryTest;
- }
-
public boolean getBecomeDiscoverable() {
return mBecomeDiscoverable;
}
@@ -766,13 +762,12 @@
.setModelId(Ascii.toUpperCase(modelId))
.setAdvertisingModelId(Ascii.toUpperCase(modelId))
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
- .setCallback(() -> {
+ .setAdvertisingChangedCallback(isAdvertising -> {
})
.setIncludeTransportDataDescriptor(true)
.setUseRandomSaltForAccountKeyRotation(false)
.setEnableNameCharacteristic(true)
.setDataOnlyConnection(false)
- .setIsMemoryTest(false)
.setBecomeDiscoverable(true)
.setShowsPasskeyConfirmation(false)
.setEnableBeaconActionsCharacteristic(true)
@@ -799,7 +794,7 @@
private boolean mEnableNameCharacteristic;
- private Callback mCallback;
+ private AdvertisingChangedCallback mAdvertisingChangedCallback;
private boolean mIncludeTransportDataDescriptor;
@@ -808,8 +803,6 @@
private boolean mUseRandomSaltForAccountKeyRotation;
- private boolean mIsMemoryTest;
-
private boolean mBecomeDiscoverable;
private boolean mShowsPasskeyConfirmation;
@@ -832,12 +825,11 @@
this.mDataOnlyConnection = option.mDataOnlyConnection;
this.mTxPowerLevel = option.mTxPowerLevel;
this.mEnableNameCharacteristic = option.mEnableNameCharacteristic;
- this.mCallback = option.mCallback;
+ this.mAdvertisingChangedCallback = option.mAdvertisingChangedCallback;
this.mIncludeTransportDataDescriptor = option.mIncludeTransportDataDescriptor;
this.mAntiSpoofingPrivateKey = option.mAntiSpoofingPrivateKey;
this.mUseRandomSaltForAccountKeyRotation =
option.mUseRandomSaltForAccountKeyRotation;
- this.mIsMemoryTest = option.mIsMemoryTest;
this.mBecomeDiscoverable = option.mBecomeDiscoverable;
this.mShowsPasskeyConfirmation = option.mShowsPasskeyConfirmation;
this.mEnableBeaconActionsCharacteristic = option.mEnableBeaconActionsCharacteristic;
@@ -874,9 +866,10 @@
return this;
}
- /** @see Callback */
- public Builder setCallback(Callback callback) {
- this.mCallback = callback;
+ /** @see AdvertisingChangedCallback */
+ public Builder setAdvertisingChangedCallback(
+ AdvertisingChangedCallback advertisingChangedCallback) {
+ this.mAdvertisingChangedCallback = advertisingChangedCallback;
return this;
}
@@ -913,11 +906,6 @@
return this;
}
- public Builder setIsMemoryTest(boolean isMemoryTest) {
- this.mIsMemoryTest = isMemoryTest;
- return this;
- }
-
public Builder setBecomeDiscoverable(boolean becomeDiscoverable) {
this.mBecomeDiscoverable = becomeDiscoverable;
return this;
@@ -963,11 +951,10 @@
mDataOnlyConnection,
mTxPowerLevel,
mEnableNameCharacteristic,
- mCallback,
+ mAdvertisingChangedCallback,
mIncludeTransportDataDescriptor,
mAntiSpoofingPrivateKey,
mUseRandomSaltForAccountKeyRotation,
- mIsMemoryTest,
mBecomeDiscoverable,
mShowsPasskeyConfirmation,
mEnableBeaconActionsCharacteristic,
@@ -1001,7 +988,6 @@
this.mBluetoothAddress =
new Value(BluetoothAddress.decode(bluetoothAddress), ByteOrder.BIG_ENDIAN);
this.mBleAddress = options.getBleAddress();
- this.mDataOnlyConnection = options.getDataOnlyConnection();
this.mAdvertiser = new OreoFastPairAdvertiser(this);
mFastPairSimulatorDatabase = new FastPairSimulatorDatabase(context);
@@ -1011,7 +997,7 @@
"Provider default device name is %s",
deviceName != null ? new String(deviceName, StandardCharsets.UTF_8) : null);
- if (mDataOnlyConnection) {
+ if (mOptions.getDataOnlyConnection()) {
// To get BLE address, we need to start advertising first, and then
// {@code#setBleAddress} will be called with BLE address.
mAdvertiser.startAdvertising(modelIdServiceData(/* forAdvertising= */ true));
@@ -1055,19 +1041,11 @@
*/
@SuppressWarnings("FutureReturnValueIgnored")
private void scheduleAdvertisingRefresh() {
- mExecutor.scheduleAtFixedRate(
- () -> {
- if (mIsAdvertising) {
- mAdvertiser.startAdvertising(getServiceData());
- }
- },
- mOptions.getIsMemoryTest()
- ? ADVERTISING_REFRESH_DELAY_5_MINS
- : ADVERTISING_REFRESH_DELAY_1_MIN,
- mOptions.getIsMemoryTest()
- ? ADVERTISING_REFRESH_DELAY_5_MINS
- : ADVERTISING_REFRESH_DELAY_1_MIN,
- TimeUnit.MILLISECONDS);
+ mExecutor.scheduleAtFixedRate(() -> {
+ if (mIsAdvertising) {
+ mAdvertiser.startAdvertising(getServiceData());
+ }
+ }, ADVERTISING_REFRESH_DELAY_1_MIN, ADVERTISING_REFRESH_DELAY_1_MIN, TimeUnit.MILLISECONDS);
}
public void destroy() {
@@ -1103,7 +1081,7 @@
public void setIsAdvertising(boolean isAdvertising) {
if (this.mIsAdvertising != isAdvertising) {
this.mIsAdvertising = isAdvertising;
- mOptions.getCallback().onAdvertisingChanged();
+ mOptions.getAdvertisingChangedCallback().onAdvertisingChanged(isAdvertising);
}
}
@@ -1113,7 +1091,7 @@
public void setBleAddress(String bleAddress) {
this.mBleAddress = bleAddress;
- if (mDataOnlyConnection) {
+ if (mOptions.getDataOnlyConnection()) {
mBluetoothAddress = new Value(BluetoothAddress.decode(bleAddress),
ByteOrder.BIG_ENDIAN);
start(bleAddress);
@@ -2086,7 +2064,7 @@
if (isDiscoverable()) {
mIsDiscoverableLatch.countDown();
}
- if (mIsDiscoverableLatch.await(3, TimeUnit.SECONDS)) {
+ if (mIsDiscoverableLatch.await(BECOME_DISCOVERABLE_TIMEOUT_SEC, TimeUnit.SECONDS)) {
mLogger.log("Successfully became switched discoverable mode %s", discoverable);
} else {
throw new TimeoutException();
@@ -2105,10 +2083,8 @@
if (scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
mRevertDiscoverableFuture =
- mExecutor.schedule(
- () -> setScanMode(SCAN_MODE_CONNECTABLE),
- mOptions.getIsMemoryTest() ? 300 : 30,
- TimeUnit.SECONDS);
+ mExecutor.schedule(() -> setScanMode(SCAN_MODE_CONNECTABLE),
+ SCAN_MODE_REFRESH_SEC, TimeUnit.SECONDS);
}
} catch (Exception e) {
mLogger.log(e, "Error setting scan mode to %d", scanMode);
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulatorDatabase.java b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulatorDatabase.java
index 254ec51..cbe39ff 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulatorDatabase.java
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulatorDatabase.java
@@ -37,7 +37,7 @@
public class FastPairSimulatorDatabase {
private static final String SHARED_PREF_NAME =
- "android.nearby.multidevices.fastpair.provider.fastpairsimulator";
+ "android.nearby.fastpair.provider.fastpairsimulator";
private static final String KEY_DEVICE_NAME = "DEVICE_NAME";
private static final String KEY_ACCOUNT_KEYS = "ACCOUNT_KEYS";
private static final int MAX_NUMBER_OF_ACCOUNT_KEYS = 8;
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/OreoFastPairAdvertiser.java b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/OreoFastPairAdvertiser.java
index dd664ea..bb77c11 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/OreoFastPairAdvertiser.java
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/OreoFastPairAdvertiser.java
@@ -54,53 +54,54 @@
public OreoFastPairAdvertiser(FastPairSimulator simulator) {
this.mSimulator = simulator;
this.mAdvertiser = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();
- this.mAdvertisingSetCallback =
- new AdvertisingSetCallback() {
- @Override
- public void onAdvertisingSetStarted(
- AdvertisingSet set, int txPower, int status) {
- if (status == AdvertisingSetCallback.ADVERTISE_SUCCESS) {
- mLogger.log("Advertising succeeded, advertising at %s dBm", txPower);
- simulator.setIsAdvertising(true);
- mAdvertisingSet = set;
+ this.mAdvertisingSetCallback = new AdvertisingSetCallback() {
- try {
- // Requires custom Android build, see callback below.
- Reflect.on(set).withMethod("getOwnAddress").invoke();
- } catch (ReflectionException e) {
- mLogger.log(e, "Error calling getOwnAddress for AdvertisingSet");
- }
- } else {
- mLogger.log(
- new IllegalStateException(),
- "Advertising failed, error code=%d", status);
- }
- }
+ @Override
+ public void onAdvertisingSetStarted(
+ AdvertisingSet set, int txPower, int status) {
+ if (status == AdvertisingSetCallback.ADVERTISE_SUCCESS) {
+ mLogger.log("Advertising succeeded, advertising at %s dBm", txPower);
+ simulator.setIsAdvertising(true);
+ mAdvertisingSet = set;
- @Override
- public void onAdvertisingDataSet(AdvertisingSet set, int status) {
- if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) {
- mLogger.log(
- new IllegalStateException(),
- "Updating advertisement failed, error code=%d",
- status);
- stopAdvertising();
- }
+ try {
+ // Requires custom Android build, see callback below.
+ Reflect.on(set).withMethod("getOwnAddress").invoke();
+ } catch (ReflectionException e) {
+ mLogger.log(e, "Error calling getOwnAddress for AdvertisingSet");
}
+ } else {
+ mLogger.log(
+ new IllegalStateException(),
+ "Advertising failed, error code=%d", status);
+ }
+ }
- // Called via reflection with AdvertisingSet.getOwnAddress().
- public void onOwnAddressRead(
- AdvertisingSet set, int addressType, String address) {
- if (!address.equals(simulator.getBleAddress())) {
- mLogger.log(
- "Read own BLE address=%s at %s",
- address,
- new SimpleDateFormat("HH:mm:ss:SSS", Locale.US)
- .format(Calendar.getInstance().getTime()));
- simulator.setBleAddress(address);
- }
- }
- };
+ @Override
+ public void onAdvertisingDataSet(AdvertisingSet set, int status) {
+ if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) {
+ mLogger.log(
+ new IllegalStateException(),
+ "Updating advertisement failed, error code=%d",
+ status);
+ stopAdvertising();
+ }
+ }
+
+ // Called via reflection with AdvertisingSet.getOwnAddress().
+ public void onOwnAddressRead(
+ AdvertisingSet set, int addressType, String address) {
+ if (!address.equals(simulator.getBleAddress())) {
+ mLogger.log(
+ "Read own BLE address=%s at %s",
+ address,
+ new SimpleDateFormat("HH:mm:ss:SSS", Locale.US)
+ .format(Calendar.getInstance().getTime()));
+ // Implicitly start the advertising once BLE address callback arrived.
+ simulator.setBleAddress(address);
+ }
+ }
+ };
}
@Override
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/BluetoothController.kt b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/bluetooth/BluetoothController.kt
similarity index 92%
rename from nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/BluetoothController.kt
rename to nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/bluetooth/BluetoothController.kt
index ed04eae..6a3e59e 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/BluetoothController.kt
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/bluetooth/BluetoothController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.nearby.fastpair.provider.simulator.app
+package android.nearby.fastpair.provider.bluetooth
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
@@ -25,9 +25,9 @@
import android.content.Intent
import android.content.IntentFilter
import android.nearby.fastpair.provider.FastPairSimulator
-import android.nearby.fastpair.provider.simulator.app.AppLogger.*
-import android.nearby.fastpair.provider.simulator.testing.Reflect
-import android.nearby.fastpair.provider.simulator.testing.ReflectionException
+import android.nearby.fastpair.provider.utils.Logger
+import android.nearby.fastpair.provider.utils.Reflect
+import android.nearby.fastpair.provider.utils.ReflectionException
import android.os.SystemClock
import android.provider.Settings
@@ -36,6 +36,7 @@
private val context: Context,
private val listener: EventListener,
) : BroadcastReceiver() {
+ private val mLogger = Logger(TAG)
private val bluetoothAdapter: BluetoothAdapter =
(context.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager)?.adapter!!
private var remoteDevice: BluetoothDevice? = null
@@ -71,14 +72,14 @@
.withMethod("setIoCapability", Int::class.javaPrimitiveType)[
ioCapabilityClassic]
} catch (e: ReflectionException) {
- warning("Error setIoCapability to %s: %s", ioCapabilityClassic, e)
+ mLogger.log(e, "Error setIoCapability to %s: %s", ioCapabilityClassic)
}
try {
Reflect.on(bluetoothAdapter)
.withMethod("setLeIoCapability", Int::class.javaPrimitiveType)[
ioCapabilityBLE]
} catch (e: ReflectionException) {
- warning("Error setLeIoCapability to %s: %s", ioCapabilityBLE, e)
+ mLogger.log(e, "Error setLeIoCapability to %s: %s", ioCapabilityBLE)
}
// Toggling airplane mode on/off to restart Bluetooth stack and reset the BLE.
@@ -90,7 +91,10 @@
TURN_AIRPLANE_MODE_ON
)
} catch (expectedOnNonCustomAndroid: SecurityException) {
- warning("Requires custom Android to toggle airplane mode")
+ mLogger.log(
+ expectedOnNonCustomAndroid,
+ "Requires custom Android to toggle airplane mode"
+ )
// Fall back to turn off Bluetooth.
bluetoothAdapter.disable()
}
@@ -102,7 +106,10 @@
TURN_AIRPLANE_MODE_OFF
)
} catch (expectedOnNonCustomAndroid: SecurityException) {
- error("SecurityException while toggled airplane mode.")
+ mLogger.log(
+ expectedOnNonCustomAndroid,
+ "SecurityException while toggled airplane mode."
+ )
} finally {
// Double confirm that Bluetooth is turned on.
bluetoothAdapter.enable()
@@ -183,8 +190,6 @@
* @param intent the Intent being received.
*/
override fun onReceive(context: Context, intent: Intent) {
- log("BluetoothController received intent, action=%s", intent.action)
-
when (intent.action) {
BluetoothDevice.ACTION_BOND_STATE_CHANGED -> {
// After a device starts bonding, we only pay attention to intents about that device.
@@ -198,7 +203,7 @@
BluetoothDevice.BOND_NONE -> null
else -> remoteDevice
}
- log(
+ mLogger.log(
"ACTION_BOND_STATE_CHANGED, the bound state of the remote device (%s) change to %s.",
remoteDevice?.remoteDeviceToString(),
bondState.bondStateToString()
@@ -211,7 +216,7 @@
BluetoothAdapter.EXTRA_CONNECTION_STATE,
BluetoothAdapter.STATE_DISCONNECTED
)
- log(
+ mLogger.log(
"ACTION_CONNECTION_STATE_CHANGED, the new connectionState: %s",
remoteDeviceConnectionState
)
@@ -223,7 +228,7 @@
BluetoothAdapter.EXTRA_SCAN_MODE,
BluetoothAdapter.SCAN_MODE_NONE
)
- log(
+ mLogger.log(
"ACTION_SCAN_MODE_CHANGED, the new scanMode: %s",
FastPairSimulator.scanModeToString(scanMode)
)
@@ -277,10 +282,12 @@
}
companion object {
+ private const val TAG = "BluetoothController"
+
/** Hidden SystemApi field in [BluetoothProfile] interface. */
private const val BLUETOOTH_PROFILE_A2DP_SINK = 11
private const val TURN_AIRPLANE_MODE_OFF = 0
private const val TURN_AIRPLANE_MODE_ON = 1
}
-}
+}
\ No newline at end of file
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/testing/Reflect.java b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/utils/Reflect.java
similarity index 98%
rename from nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/testing/Reflect.java
rename to nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/utils/Reflect.java
index 16fbc71..5ae5310 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/testing/Reflect.java
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/utils/Reflect.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.nearby.fastpair.provider.simulator.testing;
+package android.nearby.fastpair.provider.utils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/testing/ReflectionException.java b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/utils/ReflectionException.java
similarity index 93%
rename from nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/testing/ReflectionException.java
rename to nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/utils/ReflectionException.java
index 2a65f39..959fd11 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/testing/ReflectionException.java
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/utils/ReflectionException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.nearby.fastpair.provider.simulator.testing;
+package android.nearby.fastpair.provider.utils;
/**
* An exception thrown during a reflection operation. Like ReflectiveOperationException, except
diff --git a/nearby/tests/multidevices/clients/tests/Android.bp b/nearby/tests/multidevices/clients/tests/Android.bp
deleted file mode 100644
index a29a298..0000000
--- a/nearby/tests/multidevices/clients/tests/Android.bp
+++ /dev/null
@@ -1,39 +0,0 @@
-// 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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-// Run the tests: atest --host NearbyMultiDevicesClientsRoboTest
-android_robolectric_test {
- name: "NearbyMultiDevicesClientsRoboTest",
- srcs: ["src/**/*.kt"],
- instrumentation_for: "NearbyMultiDevicesClientsSnippets",
- java_resources: ["robolectric.properties"],
-
- static_libs: [
- "NearbyMultiDevicesClientsLib",
- "androidx.test.ext.junit",
- "androidx.test.rules",
- "junit",
- "mobly-snippet-lib",
- "platform-test-annotations",
- "truth-prebuilt",
- ],
- test_options: {
- // timeout in seconds.
- timeout: 36000,
- },
-}
\ No newline at end of file
diff --git a/nearby/tests/multidevices/clients/tests/AndroidManifest.xml b/nearby/tests/multidevices/clients/tests/AndroidManifest.xml
deleted file mode 100644
index c8e17e8..0000000
--- a/nearby/tests/multidevices/clients/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.nearby.multidevices"/>
diff --git a/nearby/tests/multidevices/clients/tests/robolectric.properties b/nearby/tests/multidevices/clients/tests/robolectric.properties
deleted file mode 100644
index 2ea03bb..0000000
--- a/nearby/tests/multidevices/clients/tests/robolectric.properties
+++ /dev/null
@@ -1,16 +0,0 @@
-#
-# Copyright (C) 2022 Google Inc.
-#
-# 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.
-#
-sdk=NEWEST_SDK
\ No newline at end of file
diff --git a/nearby/tests/multidevices/clients/tests/src/com/android/nearby/multidevices/common/Mockotlin.kt b/nearby/tests/multidevices/clients/tests/src/com/android/nearby/multidevices/common/Mockotlin.kt
deleted file mode 100644
index 8e70d9f..0000000
--- a/nearby/tests/multidevices/clients/tests/src/com/android/nearby/multidevices/common/Mockotlin.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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 com.android.nearby.multidevices.common
-
-import org.mockito.ArgumentCaptor
-import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
-
-/**
- * Helper methods to wrap common Mockito functions that don't do quite what you would expect in
- * Kotlin. The returned null values need to be recast to their original type in Kotlin otherwise it
- * breaks.
- */
-object Mockotlin {
-
- /**
- * Delegates to [Mockito.any].
- * @return null as T
- */
- fun <T> any() = Mockito.any<T>() as T
-
- /**
- * Delegates to [Mockito.eq].
- * @return null as T
- */
- fun <T> eq(match: T) = Mockito.eq(match) as T
-
- /**
- * Delegates to [Mockito.isA].
- * @return null as T
- */
- fun <T> isA(match: Class<T>): T = Mockito.isA(match) as T
-
- /** Delegates to [Mockito.when ], uses the same API as the mockitokotlin2 library. */
- fun <T> whenever(methodCall: T) = Mockito.`when`(methodCall)!!
-
- /**
- * Delegates to [Mockito.any] and calls it with Class<T>.
- * @return Class<T>
- */
- inline fun <reified T> anyClass(): Class<T> {
- Mockito.any(T::class.java)
- return T::class.java
- }
-
- /**
- * Delegates to [Mockito.anyListOf] and calls it with Class<T>.
- * @return List<T>
- */
- fun <T> anyListOf(): List<T> = Mockito.anyList<T>()
-
- /**
- * Delegates to [Mockito.mock].
- * @return T
- */
- inline fun <reified T> mock() = Mockito.mock(T::class.java)!!
-
- /** This is the same as calling `MockitoAnnotations.initMocks(this)` */
- fun Any.initMocks() {
- MockitoAnnotations.initMocks(this)
- }
-
- /**
- * Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException
- * when null is returned.
- */
- fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
-}
diff --git a/nearby/tests/multidevices/clients/tests/src/com/android/nearby/multidevices/fastpair/provider/BluetoothStateChangeReceiverTest.kt b/nearby/tests/multidevices/clients/tests/src/com/android/nearby/multidevices/fastpair/provider/BluetoothStateChangeReceiverTest.kt
deleted file mode 100644
index f23ccbb..0000000
--- a/nearby/tests/multidevices/clients/tests/src/com/android/nearby/multidevices/fastpair/provider/BluetoothStateChangeReceiverTest.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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 com.android.nearby.multidevices.fastpair.provider
-
-import android.Manifest
-import android.bluetooth.BluetoothAdapter
-import android.content.Context
-import android.content.Intent
-import android.nearby.multidevices.fastpair.provider.BluetoothStateChangeReceiver
-import androidx.annotation.RequiresPermission
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import com.google.android.mobly.snippet.util.Log
-import com.google.common.truth.Truth.assertThat
-import com.android.nearby.multidevices.common.Mockotlin.mock
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.verify
-
-/** Robolectric tests for [BluetoothStateChangeReceiver]. */
-@RunWith(AndroidJUnit4::class)
-class BluetoothStateChangeReceiverTest {
- private lateinit var bluetoothStateChangeReceiver: BluetoothStateChangeReceiver
- private lateinit var context: Context
- private val mockListener = mock<BluetoothStateChangeReceiver.EventListener>()
-
- @Before
- fun setUp() {
- context = InstrumentationRegistry.getInstrumentation().context
- bluetoothStateChangeReceiver = BluetoothStateChangeReceiver(context)
- Log.apkLogTag = "BluetoothStateChangeReceiverTest"
- }
-
- @Test
- fun testRegister_setsListener() {
- bluetoothStateChangeReceiver.register(mockListener)
-
- assertThat(bluetoothStateChangeReceiver.listener).isNotNull()
- }
-
- @Test
- fun testUnregister_clearListener() {
- bluetoothStateChangeReceiver.register(mockListener)
-
- bluetoothStateChangeReceiver.unregister()
-
- assertThat(bluetoothStateChangeReceiver.listener).isNull()
- }
-
- @Test
- @RequiresPermission(Manifest.permission.BLUETOOTH)
- fun testOnReceive_actionScanModeChanged_reportsOnScanModeChange() {
- val intent =
- Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED)
- .putExtra(
- BluetoothAdapter.EXTRA_SCAN_MODE,
- BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE
- )
- bluetoothStateChangeReceiver.register(mockListener)
-
- bluetoothStateChangeReceiver.onReceive(context, intent)
-
- verify(mockListener).onScanModeChange("DISCOVERABLE")
- }
-}
diff --git a/nearby/tests/multidevices/host/Android.bp b/nearby/tests/multidevices/host/Android.bp
index b0adfba..bad8a26 100644
--- a/nearby/tests/multidevices/host/Android.bp
+++ b/nearby/tests/multidevices/host/Android.bp
@@ -37,6 +37,5 @@
python_library_host {
name: "NearbyMultiDevicesHostHelper",
- srcs: ["*.py"],
- exclude_srcs: ["*_test.py"],
+ srcs: ["test_helper/*.py"],
}
\ No newline at end of file
diff --git a/nearby/tests/multidevices/host/seeker_discover_provider_test.py b/nearby/tests/multidevices/host/seeker_discover_provider_test.py
index f875250..03f3661 100644
--- a/nearby/tests/multidevices/host/seeker_discover_provider_test.py
+++ b/nearby/tests/multidevices/host/seeker_discover_provider_test.py
@@ -1,23 +1,38 @@
-# Lint as: python3
+# 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.
+
"""CTS-V Nearby Mainline Fast Pair end-to-end test case: seeker can discover the provider."""
import logging
import sys
-from mobly import asserts
from mobly import base_test
from mobly import test_runner
from mobly.controllers import android_device
-import fast_pair_provider_simulator
-import fast_pair_seeker
+from test_helper import fast_pair_provider_simulator
+from test_helper import fast_pair_seeker
# Default model ID to simulate on provider side.
DEFAULT_MODEL_ID = '00000C'
# Default public key to simulate as registered headsets.
DEFAULT_ANTI_SPOOFING_KEY = 'Cbj9eCJrTdDgSYxLkqtfADQi86vIaMvxJsQ298sZYWE='
-# Default time in seconds for events waiting.
-DEFAULT_TIMEOUT_SEC = 60
+# Time in seconds for events waiting.
+SETUP_TIMEOUT_SEC = 5
+BECOME_DISCOVERABLE_TIMEOUT_SEC = 10
+START_ADVERTISING_TIMEOUT_SEC = 5
+SCAN_TIMEOUT_SEC = 30
# Abbreviations for common use type.
FastPairProviderSimulator = fast_pair_provider_simulator.FastPairProviderSimulator
@@ -45,16 +60,16 @@
def setup_test(self) -> None:
super().setup_test()
- self._provider.start_provider_simulator(DEFAULT_MODEL_ID,
- DEFAULT_ANTI_SPOOFING_KEY)
- self._provider.wait_for_discoverable_mode(DEFAULT_TIMEOUT_SEC)
- self._provider.wait_for_advertising_start(DEFAULT_TIMEOUT_SEC)
+ self._provider.setup_provider_simulator(SETUP_TIMEOUT_SEC)
+ self._provider.start_model_id_advertising(DEFAULT_MODEL_ID, DEFAULT_ANTI_SPOOFING_KEY)
+ self._provider.wait_for_discoverable_mode(BECOME_DISCOVERABLE_TIMEOUT_SEC)
+ self._provider.wait_for_advertising_start(START_ADVERTISING_TIMEOUT_SEC)
self._seeker.start_scan()
def teardown_test(self) -> None:
super().teardown_test()
self._seeker.stop_scan()
- self._provider.stop_provider_simulator()
+ self._provider.teardown_provider_simulator()
# Create per-test excepts of logcat.
for dut in self.duts:
dut.services.create_output_excerpts_all(self.current_test_info)
@@ -62,7 +77,7 @@
def test_seeker_start_scanning_find_provider(self) -> None:
provider_ble_mac_address = self._provider.get_ble_mac_address()
self._seeker.wait_and_assert_provider_found(
- timeout_seconds=DEFAULT_TIMEOUT_SEC,
+ timeout_seconds=SCAN_TIMEOUT_SEC,
expected_model_id=DEFAULT_MODEL_ID,
expected_ble_mac_address=provider_ble_mac_address)
diff --git a/nearby/tests/multidevices/host/test_helper/__init__.py b/nearby/tests/multidevices/host/test_helper/__init__.py
new file mode 100644
index 0000000..b0cae91
--- /dev/null
+++ b/nearby/tests/multidevices/host/test_helper/__init__.py
@@ -0,0 +1,13 @@
+# 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.
diff --git a/nearby/tests/multidevices/host/event_helper.py b/nearby/tests/multidevices/host/test_helper/event_helper.py
similarity index 78%
rename from nearby/tests/multidevices/host/event_helper.py
rename to nearby/tests/multidevices/host/test_helper/event_helper.py
index a642246..8abf05c 100644
--- a/nearby/tests/multidevices/host/event_helper.py
+++ b/nearby/tests/multidevices/host/test_helper/event_helper.py
@@ -1,3 +1,17 @@
+# 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.
+
"""This is a shared library to help handling Mobly event waiting logic."""
import time
diff --git a/nearby/tests/multidevices/host/fast_pair_provider_simulator.py b/nearby/tests/multidevices/host/test_helper/fast_pair_provider_simulator.py
similarity index 65%
rename from nearby/tests/multidevices/host/fast_pair_provider_simulator.py
rename to nearby/tests/multidevices/host/test_helper/fast_pair_provider_simulator.py
index 1f62dfb..8563e8e 100644
--- a/nearby/tests/multidevices/host/fast_pair_provider_simulator.py
+++ b/nearby/tests/multidevices/host/test_helper/fast_pair_provider_simulator.py
@@ -1,3 +1,17 @@
+# 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.
+
"""Fast Pair provider simulator role."""
from mobly import asserts
@@ -5,12 +19,13 @@
from mobly.controllers.android_device_lib import snippet_event
import retry
-import event_helper
+from test_helper import event_helper
# The package name of the provider simulator snippet.
FP_PROVIDER_SIMULATOR_SNIPPETS_PACKAGE = 'android.nearby.multidevices'
# Events reported from the provider simulator snippet.
+ON_A2DP_SINK_PROFILE_CONNECT_EVENT = 'onA2DPSinkProfileConnected'
ON_SCAN_MODE_CHANGE_EVENT = 'onScanModeChange'
ON_ADVERTISING_CHANGE_EVENT = 'onAdvertisingChange'
@@ -39,21 +54,50 @@
self._ad.load_snippet(
name='fp', package=FP_PROVIDER_SIMULATOR_SNIPPETS_PACKAGE)
- def start_provider_simulator(self, model_id: str,
- anti_spoofing_key: str) -> None:
- """Starts the Fast Pair provider simulator.
+ def setup_provider_simulator(self, timeout_seconds: int) -> None:
+ """Sets up the Fast Pair provider simulator.
+
+ Args:
+ timeout_seconds: The number of seconds to wait before giving up.
+ """
+ setup_status_callback = self._ad.fp.setupProviderSimulator()
+
+ def _on_a2dp_sink_profile_connect_event_received(_, elapsed_time: int) -> bool:
+ self._ad.log.info('Provider simulator connected to A2DP sink in %d seconds.',
+ elapsed_time)
+ return True
+
+ def _on_a2dp_sink_profile_connect_event_waiting(elapsed_time: int) -> None:
+ self._ad.log.info(
+ 'Still waiting "%s" event callback from provider side '
+ 'after %d seconds...', ON_A2DP_SINK_PROFILE_CONNECT_EVENT, elapsed_time)
+
+ def _on_a2dp_sink_profile_connect_event_missed() -> None:
+ asserts.fail(f'Timed out after {timeout_seconds} seconds waiting for '
+ f'the specific "{ON_A2DP_SINK_PROFILE_CONNECT_EVENT}" event.')
+
+ wait_for_event(
+ callback_event_handler=setup_status_callback,
+ event_name=ON_A2DP_SINK_PROFILE_CONNECT_EVENT,
+ timeout_seconds=timeout_seconds,
+ on_received=_on_a2dp_sink_profile_connect_event_received,
+ on_waiting=_on_a2dp_sink_profile_connect_event_waiting,
+ on_missed=_on_a2dp_sink_profile_connect_event_missed)
+
+ def start_model_id_advertising(self, model_id: str, anti_spoofing_key: str) -> None:
+ """Starts model id advertising for scanning and initial pairing.
Args:
model_id: A 3-byte hex string for seeker side to recognize the device (ex:
0x00000C).
anti_spoofing_key: A public key for registered headsets.
"""
- self._provider_status_callback = self._ad.fp.startProviderSimulator(
- model_id, anti_spoofing_key)
+ self._provider_status_callback = (
+ self._ad.fp.startModelIdAdvertising(model_id, anti_spoofing_key))
- def stop_provider_simulator(self) -> None:
- """Stops the Fast Pair provider simulator."""
- self._ad.fp.stopProviderSimulator()
+ def teardown_provider_simulator(self) -> None:
+ """Tears down the Fast Pair provider simulator."""
+ self._ad.fp.teardownProviderSimulator()
@retry.retry(tries=3)
def get_ble_mac_address(self) -> str:
diff --git a/nearby/tests/multidevices/host/fast_pair_seeker.py b/nearby/tests/multidevices/host/test_helper/fast_pair_seeker.py
similarity index 84%
rename from nearby/tests/multidevices/host/fast_pair_seeker.py
rename to nearby/tests/multidevices/host/test_helper/fast_pair_seeker.py
index df02f51..85c37df 100644
--- a/nearby/tests/multidevices/host/fast_pair_seeker.py
+++ b/nearby/tests/multidevices/host/test_helper/fast_pair_seeker.py
@@ -1,10 +1,24 @@
+# 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.
+
"""Fast Pair seeker role."""
from mobly import asserts
from mobly.controllers import android_device
from mobly.controllers.android_device_lib import snippet_event
-import event_helper
+from test_helper import event_helper
# The package name of the Nearby Mainline Fast Pair seeker Mobly snippet.
FP_SEEKER_SNIPPETS_PACKAGE = 'android.nearby.multidevices'
diff --git a/nearby/tests/robotests/Android.bp b/nearby/tests/robotests/Android.bp
index 83702f8..56c0107 100644
--- a/nearby/tests/robotests/Android.bp
+++ b/nearby/tests/robotests/Android.bp
@@ -43,7 +43,7 @@
"androidx.mediarouter_mediarouter-nodeps",
"error_prone_annotations",
"mockito-robolectric-prebuilt",
- "service-nearby",
+ "service-nearby-pre-jarjar",
"truth-prebuilt",
"robolectric_android-all-stub",
"Robolectric_all-target",
diff --git a/nearby/tests/unit/Android.bp b/nearby/tests/unit/Android.bp
index 851dd08..9b35452 100644
--- a/nearby/tests/unit/Android.bp
+++ b/nearby/tests/unit/Android.bp
@@ -41,7 +41,7 @@
"libprotobuf-java-lite",
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
- "service-nearby",
+ "service-nearby-pre-jarjar",
"truth-prebuilt",
// "Robolectric_all-target",
],
diff --git a/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java b/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java
index c20cf22..faa3016 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java
@@ -16,8 +16,11 @@
package com.android.server.nearby;
+import static android.Manifest.permission.READ_DEVICE_CONFIG;
+
import static org.mockito.MockitoAnnotations.initMocks;
+import android.app.UiAutomation;
import android.content.Context;
import android.nearby.IScanListener;
import android.nearby.ScanRequest;
@@ -33,13 +36,16 @@
private Context mContext;
private NearbyService mService;
private ScanRequest mScanRequest;
+ private UiAutomation mUiAutomation =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation();
+
@Mock
private IScanListener mScanListener;
@Before
public void setup() {
initMocks(this);
-
+ mUiAutomation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG);
mContext = InstrumentationRegistry.getInstrumentation().getContext();
mService = new NearbyService(mContext);
mScanRequest = createScanRequest();
diff --git a/nearby/tests/unit/src/com/android/server/nearby/presence/FastAdvertisementTest.java b/nearby/tests/unit/src/com/android/server/nearby/presence/FastAdvertisementTest.java
new file mode 100644
index 0000000..73d7695
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/presence/FastAdvertisementTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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 com.android.server.nearby.presence;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.nearby.BroadcastRequest;
+import android.nearby.PresenceBroadcastRequest;
+import android.nearby.PresenceCredential;
+import android.nearby.PrivateCredential;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Collections;
+
+/**
+ * Unit test for {@link FastAdvertisement}.
+ */
+public class FastAdvertisementTest {
+
+ private static final int IDENTITY_TYPE = PresenceCredential.IDENTITY_TYPE_PRIVATE;
+ private static final byte[] IDENTITY = new byte[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+ private static final int MEDIUM_TYPE_BLE = 0;
+ private static final byte[] SALT = {2, 3};
+ private static final byte TX_POWER = 4;
+ private static final int PRESENCE_ACTION = 123;
+ private static final byte[] SECRET_ID = new byte[]{1, 2, 3, 4};
+ private static final byte[] AUTHENTICITY_KEY = new byte[]{12, 13, 14};
+ private static final byte[] EXPECTED_ADV_BYTES =
+ new byte[]{2, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 123};
+
+ private PresenceBroadcastRequest.Builder mBuilder;
+ private PrivateCredential mCredential;
+
+ @Before
+ public void setUp() {
+ mCredential =
+ new PrivateCredential.Builder(SECRET_ID, AUTHENTICITY_KEY)
+ .setIdentityType(PresenceCredential.IDENTITY_TYPE_PRIVATE)
+ .setMetadataEncryptionKey(IDENTITY)
+ .build();
+ mBuilder =
+ new PresenceBroadcastRequest.Builder(Collections.singletonList(MEDIUM_TYPE_BLE),
+ SALT, mCredential)
+ .setTxPower(TX_POWER)
+ .setVersion(BroadcastRequest.PRESENCE_VERSION_V0)
+ .addAction(PRESENCE_ACTION);
+ }
+
+ @Test
+ public void testFastAdvertisementCreateFromRequest() {
+ FastAdvertisement originalAdvertisement = FastAdvertisement.createFromRequest(
+ mBuilder.build());
+
+ assertThat(originalAdvertisement.getActions()).containsExactly(PRESENCE_ACTION);
+ assertThat(originalAdvertisement.getIdentity()).isEqualTo(IDENTITY);
+ assertThat(originalAdvertisement.getIdentityType()).isEqualTo(IDENTITY_TYPE);
+ assertThat(originalAdvertisement.getLtvFieldCount()).isEqualTo(4);
+ assertThat(originalAdvertisement.getLength()).isEqualTo(19);
+ assertThat(originalAdvertisement.getVersion()).isEqualTo(
+ BroadcastRequest.PRESENCE_VERSION_V0);
+ assertThat(originalAdvertisement.getSalt()).isEqualTo(SALT);
+ }
+
+ @Test
+ public void testFastAdvertisementSerialization() {
+ FastAdvertisement originalAdvertisement = FastAdvertisement.createFromRequest(
+ mBuilder.build());
+ byte[] bytes = originalAdvertisement.toBytes();
+
+ assertThat(bytes).hasLength(originalAdvertisement.getLength());
+ assertThat(bytes).isEqualTo(EXPECTED_ADV_BYTES);
+ }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/BleBroadcastProviderTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/BleBroadcastProviderTest.java
new file mode 100644
index 0000000..f485e18
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/BleBroadcastProviderTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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 com.android.server.nearby.provider;
+
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.le.AdvertiseSettings;
+import android.content.Context;
+import android.nearby.BroadcastCallback;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.nearby.injector.ContextHubManagerAdapter;
+import com.android.server.nearby.injector.Injector;
+
+import com.google.common.util.concurrent.MoreExecutors;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Unit test for {@link BleBroadcastProvider}.
+ */
+public class BleBroadcastProviderTest {
+ @Rule
+ public final MockitoRule mocks = MockitoJUnit.rule();
+
+ @Mock
+ private BleBroadcastProvider.BroadcastListener mBroadcastListener;
+ private BleBroadcastProvider mBleBroadcastProvider;
+
+ @Before
+ public void setUp() {
+ mBleBroadcastProvider = new BleBroadcastProvider(new TestInjector(),
+ MoreExecutors.directExecutor());
+ }
+
+ @Test
+ public void testOnStatus_success() {
+ byte[] advertiseBytes = new byte[]{1, 2, 3, 4};
+ mBleBroadcastProvider.start(advertiseBytes, mBroadcastListener);
+ verify(mBroadcastListener).onStatusChanged(eq(BroadcastCallback.STATUS_FAILURE));
+
+ AdvertiseSettings settings = new AdvertiseSettings.Builder().build();
+ mBleBroadcastProvider.onStartSuccess(settings);
+ verify(mBroadcastListener).onStatusChanged(eq(BroadcastCallback.STATUS_OK));
+ }
+
+ @Test
+ public void testOnStatus_failure() {
+ byte[] advertiseBytes = new byte[]{1, 2, 3, 4};
+ mBleBroadcastProvider.start(advertiseBytes, mBroadcastListener);
+
+ mBleBroadcastProvider.onStartFailure(BroadcastCallback.STATUS_FAILURE);
+ verify(mBroadcastListener, times(2)).onStatusChanged(eq(BroadcastCallback.STATUS_FAILURE));
+ }
+
+ private static class TestInjector implements Injector {
+
+ @Override
+ public BluetoothAdapter getBluetoothAdapter() {
+ Context context = ApplicationProvider.getApplicationContext();
+ BluetoothManager bluetoothManager = context.getSystemService(BluetoothManager.class);
+ return bluetoothManager.getAdapter();
+ }
+
+ @Override
+ public ContextHubManagerAdapter getContextHubManagerAdapter() {
+ return null;
+ }
+ }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/BroadcastProviderManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/BroadcastProviderManagerTest.java
new file mode 100644
index 0000000..71ece63
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/BroadcastProviderManagerTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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 com.android.server.nearby.provider;
+
+import static android.Manifest.permission.READ_DEVICE_CONFIG;
+import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
+import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
+
+import static com.android.server.nearby.NearbyConfiguration.NEARBY_ENABLE_PRESENCE_BROADCAST_LEGACY;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+
+import android.app.UiAutomation;
+import android.content.Context;
+import android.nearby.BroadcastCallback;
+import android.nearby.BroadcastRequest;
+import android.nearby.IBroadcastListener;
+import android.nearby.PresenceBroadcastRequest;
+import android.nearby.PresenceCredential;
+import android.nearby.PrivateCredential;
+import android.provider.DeviceConfig;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.google.common.util.concurrent.MoreExecutors;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.Collections;
+
+/**
+ * Unit test for {@link BroadcastProviderManager}.
+ */
+public class BroadcastProviderManagerTest {
+ private static final byte[] IDENTITY = new byte[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+ private static final int MEDIUM_TYPE_BLE = 0;
+ private static final byte[] SALT = {2, 3};
+ private static final byte TX_POWER = 4;
+ private static final int PRESENCE_ACTION = 123;
+ private static final byte[] SECRET_ID = new byte[]{1, 2, 3, 4};
+ private static final byte[] AUTHENTICITY_KEY = new byte[]{12, 13, 14};
+
+ @Rule
+ public final MockitoRule mocks = MockitoJUnit.rule();
+
+ @Mock
+ IBroadcastListener mBroadcastListener;
+ @Mock
+ BleBroadcastProvider mBleBroadcastProvider;
+ private Context mContext;
+ private BroadcastProviderManager mBroadcastProviderManager;
+ private BroadcastRequest mBroadcastRequest;
+ private UiAutomation mUiAutomation =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation();
+
+ @Before
+ public void setUp() {
+ mUiAutomation.adoptShellPermissionIdentity(WRITE_DEVICE_CONFIG, READ_DEVICE_CONFIG);
+ DeviceConfig.setProperty(NAMESPACE_TETHERING, NEARBY_ENABLE_PRESENCE_BROADCAST_LEGACY,
+ "true", false);
+
+ mContext = ApplicationProvider.getApplicationContext();
+ mBroadcastProviderManager = new BroadcastProviderManager(MoreExecutors.directExecutor(),
+ mBleBroadcastProvider);
+
+ PrivateCredential privateCredential =
+ new PrivateCredential.Builder(SECRET_ID, AUTHENTICITY_KEY)
+ .setIdentityType(PresenceCredential.IDENTITY_TYPE_PRIVATE)
+ .setMetadataEncryptionKey(IDENTITY)
+ .build();
+ mBroadcastRequest =
+ new PresenceBroadcastRequest.Builder(Collections.singletonList(MEDIUM_TYPE_BLE),
+ SALT, privateCredential)
+ .setTxPower(TX_POWER)
+ .setVersion(BroadcastRequest.PRESENCE_VERSION_V0)
+ .addAction(PRESENCE_ACTION).build();
+ }
+
+ @Test
+ public void testStartAdvertising() {
+ mBroadcastProviderManager.startBroadcast(mBroadcastRequest, mBroadcastListener);
+ verify(mBleBroadcastProvider).start(any(byte[].class), any(
+ BleBroadcastProvider.BroadcastListener.class));
+ }
+
+ @Test
+ public void testStartAdvertising_featureDisabled() throws Exception {
+ DeviceConfig.setProperty(NAMESPACE_TETHERING, NEARBY_ENABLE_PRESENCE_BROADCAST_LEGACY,
+ "false", false);
+ mBroadcastProviderManager = new BroadcastProviderManager(MoreExecutors.directExecutor(),
+ mBleBroadcastProvider);
+ mBroadcastProviderManager.startBroadcast(mBroadcastRequest, mBroadcastListener);
+ verify(mBroadcastListener).onStatusChanged(eq(BroadcastCallback.STATUS_FAILURE));
+ }
+
+ @Test
+ public void testOnStatusChanged() throws Exception {
+ mBroadcastProviderManager.startBroadcast(mBroadcastRequest, mBroadcastListener);
+ mBroadcastProviderManager.onStatusChanged(BroadcastCallback.STATUS_OK);
+ verify(mBroadcastListener).onStatusChanged(eq(BroadcastCallback.STATUS_OK));
+ }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/UtilsTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/UtilsTest.java
index 08a3c7c..6d04f2b 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/provider/UtilsTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/UtilsTest.java
@@ -20,7 +20,7 @@
import android.accounts.Account;
import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel;
-import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataParcel;
+import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataParcel;
import android.nearby.aidl.FastPairDeviceMetadataParcel;
import android.nearby.aidl.FastPairDiscoveryItemParcel;
import android.nearby.aidl.FastPairEligibleAccountParcel;
@@ -240,7 +240,7 @@
public void testHappyPathConvertToGetObservedDeviceResponse() {
Rpcs.GetObservedDeviceResponse response =
Utils.convertToGetObservedDeviceResponse(
- genHappyPathFastPairAntispoofkeyDeviceMetadataParcel());
+ genHappyPathFastPairAntispoofKeyDeviceMetadataParcel());
assertThat(response).isEqualTo(genHappyPathObservedDeviceResponse());
}
@@ -255,14 +255,14 @@
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testConvertToGetObservedDeviceResponseWithEmptyInputNotCrash() {
Utils.convertToGetObservedDeviceResponse(
- genEmptyFastPairAntispoofkeyDeviceMetadataParcel());
+ genEmptyFastPairAntispoofKeyDeviceMetadataParcel());
}
@Test
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testConvertToGetObservedDeviceResponseWithEmptyDeviceMetadataNotCrash() {
Utils.convertToGetObservedDeviceResponse(
- genFastPairAntispoofkeyDeviceMetadataParcelWithEmptyDeviceMetadata());
+ genFastPairAntispoofKeyDeviceMetadataParcelWithEmptyDeviceMetadata());
}
@Test
@@ -706,28 +706,28 @@
.build();
}
- private static FastPairAntispoofkeyDeviceMetadataParcel
- genHappyPathFastPairAntispoofkeyDeviceMetadataParcel() {
- FastPairAntispoofkeyDeviceMetadataParcel parcel =
- new FastPairAntispoofkeyDeviceMetadataParcel();
+ private static FastPairAntispoofKeyDeviceMetadataParcel
+ genHappyPathFastPairAntispoofKeyDeviceMetadataParcel() {
+ FastPairAntispoofKeyDeviceMetadataParcel parcel =
+ new FastPairAntispoofKeyDeviceMetadataParcel();
parcel.antiSpoofPublicKey = ANTI_SPOOFING_KEY;
parcel.deviceMetadata = genHappyPathFastPairDeviceMetadataParcel();
return parcel;
}
- private static FastPairAntispoofkeyDeviceMetadataParcel
- genFastPairAntispoofkeyDeviceMetadataParcelWithEmptyDeviceMetadata() {
- FastPairAntispoofkeyDeviceMetadataParcel parcel =
- new FastPairAntispoofkeyDeviceMetadataParcel();
+ private static FastPairAntispoofKeyDeviceMetadataParcel
+ genFastPairAntispoofKeyDeviceMetadataParcelWithEmptyDeviceMetadata() {
+ FastPairAntispoofKeyDeviceMetadataParcel parcel =
+ new FastPairAntispoofKeyDeviceMetadataParcel();
parcel.antiSpoofPublicKey = ANTI_SPOOFING_KEY;
parcel.deviceMetadata = genEmptyFastPairDeviceMetadataParcel();
return parcel;
}
- private static FastPairAntispoofkeyDeviceMetadataParcel
- genEmptyFastPairAntispoofkeyDeviceMetadataParcel() {
- return new FastPairAntispoofkeyDeviceMetadataParcel();
+ private static FastPairAntispoofKeyDeviceMetadataParcel
+ genEmptyFastPairAntispoofKeyDeviceMetadataParcel() {
+ return new FastPairAntispoofKeyDeviceMetadataParcel();
}
}