Improvements of Presence APIs.

 - use int instead of String for DataElement key
 - add a version to the BroadcastRequest
 - use DataElement and PublicCredential for PresenceScanFilter
 - add CTS tests for scan APIs.

Bug: 217283281
Test: atest CtsNearbyFastPairTests
Change-Id: Ida965f9fece2c655048acedba1a210db69f5d991
diff --git a/nearby/framework/java/android/nearby/BroadcastRequest.java b/nearby/framework/java/android/nearby/BroadcastRequest.java
index 5fe07d5..1f408d4 100644
--- a/nearby/framework/java/android/nearby/BroadcastRequest.java
+++ b/nearby/framework/java/android/nearby/BroadcastRequest.java
@@ -38,15 +38,31 @@
     /** Broadcast type for advertising using nearby presence protocol. */
     public static final int BROADCAST_TYPE_NEARBY_PRESENCE = 3;
 
+    /** @hide **/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({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;
 
+    /**
+     * V0 of Nearby Presence Protocol.
+     */
+    public static final int PRESENCE_VERSION_V0 = 0;
+
+    /**
+     * V1 of Nearby Presence Protocol.
+     */
+    public static final int PRESENCE_VERSION_V1 = 1;
+
     /** @hide **/
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({BROADCAST_TYPE_NEARBY_PRESENCE})
-    public @interface BroadcastType {
+    @IntDef({PRESENCE_VERSION_V0, PRESENCE_VERSION_V1})
+    public @interface BroadcastVersion {
     }
 
     public static final @NonNull Creator<BroadcastRequest> CREATOR =
@@ -70,17 +86,21 @@
             };
 
     private final @BroadcastType int mType;
+    private final @BroadcastVersion int mVersion;
     private final int mTxPower;
     private final List<Integer> mMediums;
 
-    BroadcastRequest(@BroadcastType int type, int txPower, List<Integer> mediums) {
+    BroadcastRequest(@BroadcastType int type, @BroadcastVersion int version, int txPower,
+            List<Integer> mediums) {
         this.mType = type;
+        this.mVersion = version;
         this.mTxPower = txPower;
         this.mMediums = mediums;
     }
 
     BroadcastRequest(@BroadcastType int type, Parcel in) {
         mType = type;
+        mVersion = in.readInt();
         mTxPower = in.readInt();
         mMediums = new ArrayList<>();
         in.readList(mMediums, Integer.class.getClassLoader(), Integer.class);
@@ -94,6 +114,13 @@
     }
 
     /**
+     * Returns the version fo the broadcast.
+     */
+    public @BroadcastVersion int getVersion() {
+        return mVersion;
+    }
+
+    /**
      * Returns the calibrated TX power when this request is broadcast.
      */
     public int getTxPower() {
@@ -111,6 +138,7 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(mType);
+        dest.writeInt(mVersion);
         dest.writeInt(mTxPower);
         dest.writeList(mMediums);
     }
diff --git a/nearby/framework/java/android/nearby/CredentialElement.java b/nearby/framework/java/android/nearby/CredentialElement.java
index a8b9ba9..5f31c17 100644
--- a/nearby/framework/java/android/nearby/CredentialElement.java
+++ b/nearby/framework/java/android/nearby/CredentialElement.java
@@ -27,7 +27,7 @@
  *
  * @hide
  */
-public class CredentialElement implements Parcelable {
+public final class CredentialElement implements Parcelable {
     private final String mKey;
     private final byte[] mValue;
 
@@ -84,7 +84,7 @@
     /**
      * Builder for {@link CredentialElement}.
      */
-    public static class Builder {
+    public static final class Builder {
         private String mKey;
         private byte[] mValue;
 
@@ -92,6 +92,7 @@
          * Set the key and value for this credential element.
          */
         @NonNull
+        @SuppressWarnings("MissingGetterMatchingBuilder")
         public CredentialElement.Builder setElement(@NonNull String key, @NonNull byte[] value) {
             mKey = key;
             mValue = value;
diff --git a/nearby/framework/java/android/nearby/DataElement.java b/nearby/framework/java/android/nearby/DataElement.java
index 68818e1..f037612 100644
--- a/nearby/framework/java/android/nearby/DataElement.java
+++ b/nearby/framework/java/android/nearby/DataElement.java
@@ -28,12 +28,12 @@
  *
  * @hide
  */
-public class DataElement implements Parcelable {
+public final class DataElement implements Parcelable {
 
-    private final String mKey;
+    private final int mKey;
     private final byte[] mValue;
 
-    private DataElement(String key, byte[] value) {
+    private DataElement(int key, byte[] value) {
         mKey = key;
         mValue = value;
     }
@@ -42,7 +42,7 @@
     public static final Creator<DataElement> CREATOR = new Creator<DataElement>() {
         @Override
         public DataElement createFromParcel(Parcel in) {
-            String key = in.readString();
+            int key = in.readInt();
             byte[] value = new byte[in.readInt()];
             in.readByteArray(value);
             return new Builder().setElement(key, value).build();
@@ -61,7 +61,7 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeString(mKey);
+        dest.writeInt(mKey);
         dest.writeInt(mValue.length);
         dest.writeByteArray(mValue);
     }
@@ -69,8 +69,7 @@
     /**
      * Returns the key of the data element.
      */
-    @NonNull
-    public String getKey() {
+    public int getKey() {
         return mKey;
     }
 
@@ -85,15 +84,16 @@
     /**
      * Builder for {@link DataElement}.
      */
-    public static class Builder {
-        private String mKey;
+    public static final class Builder {
+        private int mKey;
         private byte[] mValue;
 
         /**
          * Set the key and value for this data element.
          */
         @NonNull
-        public Builder setElement(@NonNull String key, @NonNull byte[] value) {
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        public Builder setElement(int key, @NonNull byte[] value) {
             mKey = key;
             mValue = value;
             return this;
@@ -104,8 +104,8 @@
          */
         @NonNull
         public DataElement build() {
-            Preconditions.checkState(mKey != null && mValue != null,
-                    "neither key or value can be null");
+            Preconditions.checkState(mValue != null,
+                    "value can be null");
             return new DataElement(mKey, mValue);
         }
     }
diff --git a/nearby/framework/java/android/nearby/PresenceBroadcastRequest.java b/nearby/framework/java/android/nearby/PresenceBroadcastRequest.java
index 223322a..7e387b5 100644
--- a/nearby/framework/java/android/nearby/PresenceBroadcastRequest.java
+++ b/nearby/framework/java/android/nearby/PresenceBroadcastRequest.java
@@ -36,10 +36,10 @@
     private final PrivateCredential mCredential;
     private final List<DataElement> mExtendedProperties;
 
-    private PresenceBroadcastRequest(int txPower, List<Integer> mediums, byte[] salt,
-            List<Integer> actions,
+    private PresenceBroadcastRequest(@BroadcastVersion int version, int txPower,
+            List<Integer> mediums, byte[] salt, List<Integer> actions,
             PrivateCredential credential, List<DataElement> extendedProperties) {
-        super(BROADCAST_TYPE_NEARBY_PRESENCE, txPower, mediums);
+        super(BROADCAST_TYPE_NEARBY_PRESENCE, version, txPower, mediums);
         mSalt = salt;
         mActions = actions;
         mCredential = credential;
@@ -82,6 +82,7 @@
     /**
      * Returns the salt associated with this broadcast request.
      */
+    @NonNull
     public byte[] getSalt() {
         return mSalt;
     }
@@ -135,11 +136,13 @@
         private final List<Integer> mActions;
         private final List<DataElement> mExtendedProperties;
 
+        private int mVersion;
         private int mTxPower;
         private byte[] mSalt;
         private PrivateCredential mCredential;
 
         public Builder() {
+            mVersion = PRESENCE_VERSION_V0;
             mTxPower = UNKNOWN_TX_POWER;
             mMediums = new ArrayList<>();
             mActions = new ArrayList<>();
@@ -147,6 +150,15 @@
         }
 
         /**
+         * Sets the version for this request.
+         */
+        @NonNull
+        public Builder setVersion(@BroadcastVersion int version) {
+            mVersion = version;
+            return this;
+        }
+
+        /**
          * Sets the calibrated tx power level for this request.
          */
         @NonNull
@@ -207,7 +219,7 @@
         public PresenceBroadcastRequest build() {
             Preconditions.checkState(!mMediums.isEmpty(), "mediums cannot be empty");
             Preconditions.checkState(mSalt != null && mSalt.length > 0, "salt cannot be empty");
-            return new PresenceBroadcastRequest(mTxPower, mMediums, mSalt, mActions,
+            return new PresenceBroadcastRequest(mVersion, mTxPower, mMediums, mSalt, mActions,
                     mCredential, mExtendedProperties);
         }
     }
diff --git a/nearby/framework/java/android/nearby/PresenceCredential.java b/nearby/framework/java/android/nearby/PresenceCredential.java
index 5f0c201..14b17ae 100644
--- a/nearby/framework/java/android/nearby/PresenceCredential.java
+++ b/nearby/framework/java/android/nearby/PresenceCredential.java
@@ -81,7 +81,7 @@
 
     private final @CredentialType int mType;
     private final @IdentityType int mIdentityType;
-    private final byte[] mSecreteId;
+    private final byte[] mSecretId;
     private final byte[] mAuthenticityKey;
     private final List<CredentialElement> mCredentialElements;
 
@@ -89,7 +89,7 @@
             byte[] secreteId, byte[] authenticityKey, List<CredentialElement> credentialElements) {
         mType = type;
         mIdentityType = identityType;
-        mSecreteId = secreteId;
+        mSecretId = secreteId;
         mAuthenticityKey = authenticityKey;
         mCredentialElements = credentialElements;
     }
@@ -97,8 +97,8 @@
     PresenceCredential(@CredentialType int type, Parcel in) {
         mType = type;
         mIdentityType = in.readInt();
-        mSecreteId = new byte[in.readInt()];
-        in.readByteArray(mSecreteId);
+        mSecretId = new byte[in.readInt()];
+        in.readByteArray(mSecretId);
         mAuthenticityKey = new byte[in.readInt()];
         in.readByteArray(mAuthenticityKey);
         mCredentialElements = new ArrayList<>();
@@ -144,11 +144,11 @@
     }
 
     /**
-     * Returns the secrete id of the credential.
+     * Returns the secret id of the credential.
      */
     @NonNull
-    public byte[] getSecreteId() {
-        return mSecreteId;
+    public byte[] getSecretId() {
+        return mSecretId;
     }
 
     /**
@@ -176,8 +176,8 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(mType);
         dest.writeInt(mIdentityType);
-        dest.writeInt(mSecreteId.length);
-        dest.writeByteArray(mSecreteId);
+        dest.writeInt(mSecretId.length);
+        dest.writeByteArray(mSecretId);
         dest.writeInt(mAuthenticityKey.length);
         dest.writeByteArray(mAuthenticityKey);
         dest.writeList(mCredentialElements);
diff --git a/nearby/framework/java/android/nearby/PresenceDevice.java b/nearby/framework/java/android/nearby/PresenceDevice.java
index 61326c6..0b3782c 100644
--- a/nearby/framework/java/android/nearby/PresenceDevice.java
+++ b/nearby/framework/java/android/nearby/PresenceDevice.java
@@ -63,6 +63,9 @@
     }
 
     private final String mDeviceId;
+    private final byte[] mSalt;
+    private final byte[] mSecretId;
+    private final byte[] mEncryptedIdentity;
     private final int mDeviceType;
     private final String mDeviceImageUrl;
     private final long mDiscoveryTimestampMillis;
@@ -89,6 +92,30 @@
         return mDeviceId;
     }
 
+    /**
+     * Returns the salt used when presence device is discovered.
+     */
+    @NonNull
+    public byte[] getSalt() {
+        return mSalt;
+    }
+
+    /**
+     * Returns the secret used when presence device is discovered.
+     */
+    @NonNull
+    public byte[] getSecretId() {
+        return mSecretId;
+    }
+
+    /**
+     * Returns the encrypted identity used when presence device is discovered.
+     */
+    @NonNull
+    public byte[] getEncryptedIdentity() {
+        return mEncryptedIdentity;
+    }
+
     /** The type of the device. */
     @DeviceType
     public int getDeviceType() {
@@ -115,12 +142,15 @@
     }
 
     private PresenceDevice(String deviceName, int mMedium, int rssi, String deviceId,
-            int deviceType,
+            byte[] salt, byte[] secretId, byte[] encryptedIdentity, int deviceType,
             String deviceImageUrl, long discoveryTimestampMillis,
             Bundle extendedProperties) {
         // TODO (b/217462253): change medium to a set in NearbyDevice.
         super(deviceName, mMedium, rssi);
         mDeviceId = deviceId;
+        mSalt = salt;
+        mSecretId = secretId;
+        mEncryptedIdentity = encryptedIdentity;
         mDeviceType = deviceType;
         mDeviceImageUrl = deviceImageUrl;
         mDiscoveryTimestampMillis = discoveryTimestampMillis;
@@ -192,6 +222,9 @@
         private int mRssi;
         private int mMedium;
         private String mDeviceId;
+        private byte[] mSalt;
+        private byte[] mSecretId;
+        private byte[] mEncryptedIdentity;
         private int mDeviceType;
         private String mDeviceImageUrl;
         private long mDiscoveryTimestampMillis;
@@ -245,6 +278,32 @@
             return this;
         }
 
+        /**
+         * Sets the identifier on the discovered Presence device.
+         */
+        @NonNull
+        public Builder setSalt(@NonNull byte[] salt) {
+            mSalt = salt;
+            return this;
+        }
+
+        /**
+         * Sets the secret Id of the discovered Presence device.
+         */
+        @NonNull
+        public Builder setSecretId(@NonNull byte[] secretId) {
+            mSecretId = secretId;
+            return this;
+        }
+
+        /**
+         * Sets the encrypted identity of the discovered Presence device.
+         */
+        @NonNull
+        public Builder setEncryptedIdentity(@NonNull byte[] encryptedIdentity) {
+            mEncryptedIdentity = encryptedIdentity;
+            return this;
+        }
 
         /**
          * Sets the type of discovered Presence device.
@@ -299,7 +358,9 @@
          */
         @NonNull
         public PresenceDevice build() {
-            return new PresenceDevice(mName, mMedium, mRssi, mDeviceId, mDeviceType,
+            return new PresenceDevice(mName, mMedium, mRssi, mDeviceId,
+                    mSalt, mSecretId, mEncryptedIdentity,
+                    mDeviceType,
                     mDeviceImageUrl,
                     mDiscoveryTimestampMillis, mExtendedProperties);
         }
diff --git a/nearby/framework/java/android/nearby/PresenceScanFilter.java b/nearby/framework/java/android/nearby/PresenceScanFilter.java
index 61e5049..e9b5faa 100644
--- a/nearby/framework/java/android/nearby/PresenceScanFilter.java
+++ b/nearby/framework/java/android/nearby/PresenceScanFilter.java
@@ -17,8 +17,6 @@
 package android.nearby;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArraySet;
@@ -36,25 +34,16 @@
  */
 public final class PresenceScanFilter extends ScanFilter implements Parcelable {
 
-    private final List<byte[]> mCertificates;
-    private final List<Integer> mPresenceIdentities;
+    private final List<PublicCredential> mCredentials;
     private final List<Integer> mPresenceActions;
-    private final Bundle mExtendedProperties;
+    private final List<DataElement> mExtendedProperties;
 
     /**
-     * A list of certificates to filter on.
+     * A list of credentials to filter on.
      */
     @NonNull
-    public List<byte[]> getCertificates() {
-        return mCertificates;
-    }
-
-    /**
-     * A list of presence identities for matching.
-     */
-    @NonNull
-    public List<Integer> getPresenceIdentities() {
-        return mPresenceIdentities;
+    public List<PublicCredential> getCredentials() {
+        return mCredentials;
     }
 
     /**
@@ -69,42 +58,33 @@
      * A bundle of extended properties for matching.
      */
     @NonNull
-    public Bundle getExtendedProperties() {
+    public List<DataElement> getExtendedProperties() {
         return mExtendedProperties;
     }
 
-    private PresenceScanFilter(int rssiThreshold, List<byte[]> certificates,
-            List<Integer> presenceIdentities, List<Integer> presenceActions,
-            Bundle extendedProperties) {
+    private PresenceScanFilter(int rssiThreshold, List<PublicCredential> credentials,
+            List<Integer> presenceActions, List<DataElement> extendedProperties) {
         super(ScanRequest.SCAN_TYPE_NEARBY_PRESENCE, rssiThreshold);
-        mCertificates = new ArrayList<>(certificates);
-        mPresenceIdentities = new ArrayList<>(presenceIdentities);
+        mCredentials = new ArrayList<>(credentials);
         mPresenceActions = new ArrayList<>(presenceActions);
         mExtendedProperties = extendedProperties;
     }
 
     private PresenceScanFilter(Parcel in) {
         super(ScanRequest.SCAN_TYPE_NEARBY_PRESENCE, in);
-        mCertificates = new ArrayList<>();
-        int size = in.readInt();
-        for (int i = 0; i < size; i++) {
-            int len = in.readInt();
-            byte[] certificate = new byte[len];
-            in.readByteArray(certificate);
-            mCertificates.add(certificate);
-        }
-        mPresenceIdentities = new ArrayList<>();
+        mCredentials = new ArrayList<>();
         if (in.readInt() != 0) {
-            in.readList(mPresenceIdentities, Integer.class.getClassLoader(), Integer.class);
+            in.readParcelableList(mCredentials, PublicCredential.class.getClassLoader(),
+                    PublicCredential.class);
         }
         mPresenceActions = new ArrayList<>();
         if (in.readInt() != 0) {
             in.readList(mPresenceActions, Integer.class.getClassLoader(), Integer.class);
         }
-        mExtendedProperties = new Bundle();
-        Bundle bundle = in.readBundle(getClass().getClassLoader());
-        for (String key : bundle.keySet()) {
-            mExtendedProperties.putString(key, bundle.getString(key));
+        mExtendedProperties = new ArrayList<>();
+        if (in.readInt() != 0) {
+            in.readParcelableList(mExtendedProperties, DataElement.class.getClassLoader(),
+                    DataElement.class);
         }
     }
 
@@ -138,20 +118,18 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
-        dest.writeInt(mCertificates.size());
-        for (byte[] certificate : mCertificates) {
-            dest.writeInt(certificate.length);
-            dest.writeByteArray(certificate);
-        }
-        dest.writeInt(mPresenceIdentities.size());
-        if (!mPresenceIdentities.isEmpty()) {
-            dest.writeList(mPresenceIdentities);
+        dest.writeInt(mCredentials.size());
+        if (!mCredentials.isEmpty()) {
+            dest.writeParcelableList(mCredentials, 0);
         }
         dest.writeInt(mPresenceActions.size());
         if (!mPresenceActions.isEmpty()) {
             dest.writeList(mPresenceActions);
         }
-        dest.writeBundle(mExtendedProperties);
+        dest.writeInt(mExtendedProperties.size());
+        if (!mExtendedProperties.isEmpty()) {
+            dest.writeList(mExtendedProperties);
+        }
     }
 
     /**
@@ -161,17 +139,17 @@
      */
     public static final class Builder {
         private int mRssiThreshold;
-        private final Set<byte[]> mCertificates;
+        private final Set<PublicCredential> mCredentials;
         private final Set<Integer> mPresenceIdentities;
         private final Set<Integer> mPresenceActions;
-        private final Bundle mExtendedProperties;
+        private final List<DataElement> mExtendedProperties;
 
         public Builder() {
             mRssiThreshold = -100;
-            mCertificates = new ArraySet<>();
+            mCredentials = new ArraySet<>();
             mPresenceIdentities = new ArraySet<>();
             mPresenceActions = new ArraySet<>();
-            mExtendedProperties = new Bundle();
+            mExtendedProperties = new ArrayList<>();
         }
 
         /**
@@ -184,21 +162,12 @@
         }
 
         /**
-         * Adds a list of certificates the scan filter is expected to match.
+         * Adds a list of credentials the scan filter is expected to match.
          */
 
         @NonNull
-        public Builder addCertificate(@NonNull byte[] certificate) {
-            mCertificates.add(certificate);
-            return this;
-        }
-
-        /**
-         * Adds a presence identity for filtering.
-         */
-        @NonNull
-        public Builder addPresenceIdentity(int identity) {
-            mPresenceIdentities.add(identity);
+        public Builder addCredential(@NonNull PublicCredential credential) {
+            mCredentials.add(credential);
             return this;
         }
 
@@ -215,8 +184,8 @@
          * Add an extended property for scan filtering.
          */
         @NonNull
-        public Builder addExtendedProperty(@NonNull String key, @Nullable String value) {
-            mExtendedProperties.putCharSequence(key, value);
+        public Builder addExtendedProperty(@NonNull DataElement dataElement) {
+            mExtendedProperties.add(dataElement);
             return this;
         }
 
@@ -225,9 +194,9 @@
          */
         @NonNull
         public PresenceScanFilter build() {
-            Preconditions.checkState(!mCertificates.isEmpty(), "certificates cannot be empty");
-            return new PresenceScanFilter(mRssiThreshold, new ArrayList<>(mCertificates),
-                    new ArrayList<>(mPresenceIdentities),
+            Preconditions.checkState(!mCredentials.isEmpty(), "credentials cannot be empty");
+            return new PresenceScanFilter(mRssiThreshold,
+                    new ArrayList<>(mCredentials),
                     new ArrayList<>(mPresenceActions),
                     mExtendedProperties);
         }
diff --git a/nearby/framework/java/android/nearby/PrivateCredential.java b/nearby/framework/java/android/nearby/PrivateCredential.java
index e05efa0..2860fdb 100644
--- a/nearby/framework/java/android/nearby/PrivateCredential.java
+++ b/nearby/framework/java/android/nearby/PrivateCredential.java
@@ -30,7 +30,7 @@
  *
  * @hide
  */
-public class PrivateCredential extends PresenceCredential implements Parcelable {
+public final class PrivateCredential extends PresenceCredential implements Parcelable {
 
     @NonNull
     public static final Creator<PrivateCredential> CREATOR = new Creator<PrivateCredential>() {
@@ -56,7 +56,7 @@
         mDeviceName = in.readString();
     }
 
-    public PrivateCredential(int identityType, byte[] secreteId,
+    private PrivateCredential(int identityType, byte[] secreteId,
             String deviceName, byte[] authenticityKey, List<CredentialElement> credentialElements,
             byte[] metaDataEncryptionKey) {
         super(CREDENTIAL_TYPE_PRIVATE, identityType, secreteId, authenticityKey,
@@ -82,15 +82,22 @@
         dest.writeString(mDeviceName);
     }
 
+    /**
+     * Returns the metadata encryption key associated with this credential.
+     */
+    @NonNull
     public byte[] getMetaDataEncryptionKey() {
         return mMetaDataEncryptionKey;
     }
 
+    /**
+     * Returns the device name associated with this credential.
+     */
+    @NonNull
     public String getDeviceName() {
         return mDeviceName;
     }
 
-
     /**
      * Builder class for {@link PresenceCredential}.
      *
@@ -140,7 +147,7 @@
          * Sets the metadata encryption key to the credential.
          */
         @NonNull
-        public Builder setMetaDataEncryptionKey(byte[] metaDataEncryptionKey) {
+        public Builder setMetaDataEncryptionKey(@NonNull byte[] metaDataEncryptionKey) {
             mMetaDataEncryptionKey = metaDataEncryptionKey;
             return this;
         }
@@ -149,7 +156,7 @@
          * Sets the device name of the credential.
          */
         @NonNull
-        public Builder setDeviceName(String deviceName) {
+        public Builder setDeviceName(@NonNull String deviceName) {
             mDeviceName = deviceName;
             return this;
         }
@@ -158,7 +165,7 @@
          * Adds an element to the credential.
          */
         @NonNull
-        public Builder addCredentialElement(CredentialElement credentialElement) {
+        public Builder addCredentialElement(@NonNull CredentialElement credentialElement) {
             mCredentialElements.add(credentialElement);
             return this;
         }
diff --git a/nearby/framework/java/android/nearby/PublicCredential.java b/nearby/framework/java/android/nearby/PublicCredential.java
index 560e7f0..78f6205 100644
--- a/nearby/framework/java/android/nearby/PublicCredential.java
+++ b/nearby/framework/java/android/nearby/PublicCredential.java
@@ -30,7 +30,7 @@
  *
  * @hide
  */
-public class PublicCredential extends PresenceCredential implements Parcelable {
+public final class PublicCredential extends PresenceCredential implements Parcelable {
     @NonNull
     public static final Creator<PublicCredential> CREATOR = new Creator<PublicCredential>() {
         @Override
@@ -58,7 +58,7 @@
         mMetaDataEncryptionKeyTag = metaDataEncryptionKeyTag;
     }
 
-    public PublicCredential(Parcel in) {
+    private PublicCredential(Parcel in) {
         super(CREDENTIAL_TYPE_PUBLIC, in);
         mPublicKey = new byte[in.readInt()];
         in.readByteArray(mPublicKey);
@@ -72,14 +72,26 @@
         return new PublicCredential(in);
     }
 
+    /**
+     * Returns the public key associated with this credential.
+     */
+    @NonNull
     public byte[] getPublicKey() {
         return mPublicKey;
     }
 
+    /**
+     * Returns the encrypted metadata associated with this credential.
+     */
+    @NonNull
     public byte[] getEncryptedMetadata() {
         return mEncryptedMetadata;
     }
 
+    /**
+     * Returns the metadata encryption key tag associated with this credential.
+     */
+    @NonNull
     public byte[] getMetaDataEncryptionKeyTag() {
         return mMetaDataEncryptionKeyTag;
     }
@@ -150,7 +162,7 @@
          * Adds an element to the credential.
          */
         @NonNull
-        public Builder addCredentialElement(CredentialElement credentialElement) {
+        public Builder addCredentialElement(@NonNull CredentialElement credentialElement) {
             mCredentialElements.add(credentialElement);
             return this;
         }
@@ -159,7 +171,7 @@
          * Sets the public key for the credential.
          */
         @NonNull
-        public Builder setPublicKey(byte[] publicKey) {
+        public Builder setPublicKey(@NonNull byte[] publicKey) {
             mPublicKey = publicKey;
             return this;
         }
@@ -168,7 +180,7 @@
          * Sets the encrypted metadata.
          */
         @NonNull
-        public Builder setEncryptedMetadata(byte[] encryptedMetadata) {
+        public Builder setEncryptedMetadata(@NonNull byte[] encryptedMetadata) {
             mEncryptedMetadata = encryptedMetadata;
             return this;
         }
@@ -177,7 +189,7 @@
          * Sets the metadata encryption key tag.
          */
         @NonNull
-        public Builder setMetaDataEncryptionKeyTag(byte[] metaDataEncryptionKeyTag) {
+        public Builder setMetaDataEncryptionKeyTag(@NonNull byte[] metaDataEncryptionKeyTag) {
             mMetaDataEncryptionKeyTag = metaDataEncryptionKeyTag;
             return this;
         }
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/DataElementTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/DataElementTest.java
index 95d64c3..d096ed1 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/DataElementTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/DataElementTest.java
@@ -35,7 +35,7 @@
 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
 public class DataElementTest {
 
-    private static final String KEY = "SUPPORT_MEDIA";
+    private static final int KEY = 1234;
     private static final byte[] VALUE = new byte[]{1, 1, 1, 1};
 
     @Test
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 a974008..a27d525 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceBroadcastRequestTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceBroadcastRequestTest.java
@@ -44,12 +44,13 @@
     private static final int TX_POWER = 1;
     private static final byte[] SALT = new byte[]{1, 2};
     private static final int ACTION_ID = 123;
-    private static final String SUPPORT_MEDIA_KEY = "SupportMedia";
-    private static final byte[] SUPPORT_MEDIA_VALUE = new byte[]{1, 2, 34};
     private static final int BLE_MEDIUM = 1;
     private static final byte[] SECRETE_ID = new byte[]{1, 2, 3, 4};
     private static final byte[] AUTHENTICITY_KEY = new byte[]{0, 1, 1, 1};
     private static final byte[] METADATA_ENCRYPTION_KEY = new byte[]{1, 1, 3, 4, 5};
+    private static final int KEY = 1234;
+    private static final byte[] VALUE = new byte[]{1, 1, 1, 1};
+
 
     private PresenceBroadcastRequest.Builder mBuilder;
 
@@ -61,14 +62,14 @@
                 .setAuthenticityKey(AUTHENTICITY_KEY)
                 .setMetaDataEncryptionKey(METADATA_ENCRYPTION_KEY)
                 .build();
+        DataElement element = new DataElement.Builder().setElement(KEY, VALUE).build();
         mBuilder = new PresenceBroadcastRequest.Builder()
                 .setSalt(SALT)
                 .setTxPower(TX_POWER)
                 .setCredential(credential)
                 .addAction(ACTION_ID)
                 .addMediums(BLE_MEDIUM)
-                .addExtendedProperty(new DataElement.Builder().setElement(SUPPORT_MEDIA_KEY,
-                        SUPPORT_MEDIA_VALUE).build());
+                .addExtendedProperty(element);
     }
 
     @Test
@@ -78,7 +79,7 @@
         assertThat(broadcastRequest.getTxPower()).isEqualTo(TX_POWER);
         assertThat(broadcastRequest.getActions()).containsExactly(ACTION_ID);
         assertThat(broadcastRequest.getExtendedProperties().get(0).getKey()).isEqualTo(
-                SUPPORT_MEDIA_KEY);
+                KEY);
         assertThat(broadcastRequest.getMediums()).containsExactly(BLE_MEDIUM);
         assertThat(broadcastRequest.getCredential().getIdentityType()).isEqualTo(
                 IDENTITY_TYPE_PRIVATE);
@@ -99,7 +100,7 @@
         assertThat(parcelRequest.getTxPower()).isEqualTo(TX_POWER);
         assertThat(parcelRequest.getActions()).containsExactly(ACTION_ID);
         assertThat(parcelRequest.getExtendedProperties().get(0).getKey()).isEqualTo(
-                SUPPORT_MEDIA_KEY);
+                KEY);
         assertThat(parcelRequest.getMediums()).containsExactly(BLE_MEDIUM);
         assertThat(parcelRequest.getCredential().getIdentityType()).isEqualTo(
                 IDENTITY_TYPE_PRIVATE);
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceDeviceTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceDeviceTest.java
new file mode 100644
index 0000000..c704022
--- /dev/null
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceDeviceTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.nearby.NearbyDevice;
+import android.nearby.PresenceDevice;
+import android.os.Build;
+import android.os.Parcel;
+
+import androidx.annotation.RequiresApi;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for {@link PresenceDevice}.
+ */
+@RunWith(AndroidJUnit4.class)
+@RequiresApi(Build.VERSION_CODES.TIRAMISU)
+public class PresenceDeviceTest {
+    private static final int DEVICE_TYPE = PresenceDevice.DeviceType.PHONE;
+    private static final String DEVICE_ID = "123";
+    private static final String IMAGE_URL = "http://example.com/imageUrl";
+    private static final String SUPPORT_MEDIA = "SupportMedia";
+    private static final String SUPPORT_MEDIA_VALUE = "true";
+    private static final int RSSI = -40;
+    private static final int MEDIUM = NearbyDevice.Medium.BLE;
+    private static final String DEVICE_NAME = "testDevice";
+
+    @Test
+    public void testBuilder() {
+        PresenceDevice device = new PresenceDevice.Builder()
+                .setDeviceType(DEVICE_TYPE)
+                .setDeviceId(DEVICE_ID)
+                .setDeviceImageUrl(IMAGE_URL)
+                .addExtendedProperty(SUPPORT_MEDIA, SUPPORT_MEDIA_VALUE)
+                .setRssi(RSSI)
+                .setMedium(MEDIUM)
+                .setName(DEVICE_NAME)
+                .build();
+
+        assertThat(device.getDeviceType()).isEqualTo(DEVICE_TYPE);
+        assertThat(device.getDeviceId()).isEqualTo(DEVICE_ID);
+        assertThat(device.getDeviceImageUrl()).isEqualTo(IMAGE_URL);
+        assertThat(device.getExtendedProperties().get(SUPPORT_MEDIA)).isEqualTo(
+                SUPPORT_MEDIA_VALUE);
+        assertThat(device.getRssi()).isEqualTo(RSSI);
+        assertThat(device.getMedium()).isEqualTo(MEDIUM);
+        assertThat(device.getName()).isEqualTo(DEVICE_NAME);
+    }
+
+    @Test
+    public void testWriteParcel() {
+        PresenceDevice device = new PresenceDevice.Builder()
+                .setDeviceId(DEVICE_ID)
+                .addExtendedProperty(SUPPORT_MEDIA, SUPPORT_MEDIA_VALUE)
+                .setRssi(RSSI)
+                .setMedium(MEDIUM)
+                .setName(DEVICE_NAME)
+                .build();
+
+        Parcel parcel = Parcel.obtain();
+        device.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        PresenceDevice parcelDevice = PresenceDevice.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+
+        assertThat(parcelDevice.getDeviceId()).isEqualTo(DEVICE_ID);
+        assertThat(parcelDevice.getExtendedProperties().get(SUPPORT_MEDIA)).isEqualTo(
+                SUPPORT_MEDIA_VALUE);
+        assertThat(parcelDevice.getRssi()).isEqualTo(RSSI);
+        assertThat(parcelDevice.getMedium()).isEqualTo(MEDIUM);
+        assertThat(parcelDevice.getName()).isEqualTo(DEVICE_NAME);
+    }
+}
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceScanFilterTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceScanFilterTest.java
new file mode 100644
index 0000000..017677f
--- /dev/null
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceScanFilterTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.cts;
+
+import static android.nearby.PresenceCredential.IDENTITY_TYPE_PRIVATE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.nearby.DataElement;
+import android.nearby.PresenceScanFilter;
+import android.nearby.PublicCredential;
+import android.nearby.ScanRequest;
+import android.os.Build;
+import android.os.Parcel;
+
+import androidx.annotation.RequiresApi;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link android.nearby.PresenceScanFilter}.
+ */
+@RunWith(AndroidJUnit4.class)
+@RequiresApi(Build.VERSION_CODES.TIRAMISU)
+public class PresenceScanFilterTest {
+
+    private static final int RSSI = -40;
+    private static final int ACTION = 123;
+    private static final byte[] SECRETE_ID = new byte[]{1, 2, 3, 4};
+    private static final byte[] AUTHENTICITY_KEY = new byte[]{0, 1, 1, 1};
+    private static final byte[] PUBLIC_KEY = new byte[]{1, 1, 2, 2};
+    private static final byte[] ENCRYPTED_METADATA = new byte[]{1, 2, 3, 4, 5};
+    private static final byte[] METADATA_ENCRYPTION_KEY_TAG = new byte[]{1, 1, 3, 4, 5};
+    private static final int KEY = 1234;
+    private static final byte[] VALUE = new byte[]{1, 1, 1, 1};
+
+
+    private PublicCredential mPublicCredential =
+            new PublicCredential.Builder()
+                    .setIdentityType(IDENTITY_TYPE_PRIVATE)
+                    .setSecretId(SECRETE_ID).setAuthenticityKey(AUTHENTICITY_KEY)
+                    .setPublicKey(PUBLIC_KEY).setEncryptedMetadata(ENCRYPTED_METADATA)
+                    .setMetaDataEncryptionKeyTag(METADATA_ENCRYPTION_KEY_TAG).build();
+    private PresenceScanFilter.Builder mBuilder = new PresenceScanFilter.Builder()
+            .setRssiThreshold(RSSI)
+            .addCredential(mPublicCredential)
+            .addPresenceAction(ACTION)
+            .addExtendedProperty(new DataElement.Builder().setElement(KEY, VALUE).build());
+
+    @Test
+    public void testBuilder() {
+        PresenceScanFilter filter = mBuilder.build();
+
+        assertThat(filter.getRssiThreshold()).isEqualTo(RSSI);
+        assertThat(filter.getCredentials().get(0).getIdentityType()).isEqualTo(
+                IDENTITY_TYPE_PRIVATE);
+        assertThat(filter.getPresenceActions()).containsExactly(ACTION);
+    }
+
+    @Test
+    public void testWriteParcel() {
+        PresenceScanFilter filter = mBuilder.build();
+
+        Parcel parcel = Parcel.obtain();
+        filter.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        PresenceScanFilter parcelFilter = PresenceScanFilter.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+
+        assertThat(parcelFilter.getType()).isEqualTo(ScanRequest.SCAN_TYPE_NEARBY_PRESENCE);
+        assertThat(parcelFilter.getRssiThreshold()).isEqualTo(RSSI);
+        assertThat(parcelFilter.getPresenceActions()).containsExactly(ACTION);
+    }
+}
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/PrivateCredentialTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/PrivateCredentialTest.java
index 1174400..054bb1a 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/PrivateCredentialTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/PrivateCredentialTest.java
@@ -65,7 +65,7 @@
 
         assertThat(credential.getType()).isEqualTo(CREDENTIAL_TYPE_PRIVATE);
         assertThat(credential.getIdentityType()).isEqualTo(IDENTITY_TYPE_PRIVATE);
-        assertThat(Arrays.equals(credential.getSecreteId(), SECRETE_ID)).isTrue();
+        assertThat(Arrays.equals(credential.getSecretId(), SECRETE_ID)).isTrue();
         assertThat(Arrays.equals(credential.getAuthenticityKey(), AUTHENTICITY_KEY)).isTrue();
         assertThat(Arrays.equals(credential.getMetaDataEncryptionKey(),
                 METADATA_ENCRYPTION_KEY)).isTrue();
@@ -87,7 +87,7 @@
 
         assertThat(credentialFromParcel.getType()).isEqualTo(CREDENTIAL_TYPE_PRIVATE);
         assertThat(credentialFromParcel.getIdentityType()).isEqualTo(IDENTITY_TYPE_PRIVATE);
-        assertThat(Arrays.equals(credentialFromParcel.getSecreteId(), SECRETE_ID)).isTrue();
+        assertThat(Arrays.equals(credentialFromParcel.getSecretId(), SECRETE_ID)).isTrue();
         assertThat(Arrays.equals(credentialFromParcel.getAuthenticityKey(),
                 AUTHENTICITY_KEY)).isTrue();
         assertThat(Arrays.equals(credentialFromParcel.getMetaDataEncryptionKey(),
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/PublicCredentialTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/PublicCredentialTest.java
index 2c28768..4359ba5 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/PublicCredentialTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/PublicCredentialTest.java
@@ -65,7 +65,7 @@
 
         assertThat(credential.getType()).isEqualTo(CREDENTIAL_TYPE_PUBLIC);
         assertThat(credential.getIdentityType()).isEqualTo(IDENTITY_TYPE_PRIVATE);
-        assertThat(Arrays.equals(credential.getSecreteId(), SECRETE_ID)).isTrue();
+        assertThat(Arrays.equals(credential.getSecretId(), SECRETE_ID)).isTrue();
         assertThat(Arrays.equals(credential.getAuthenticityKey(), AUTHENTICITY_KEY)).isTrue();
         assertThat(Arrays.equals(credential.getPublicKey(), PUBLIC_KEY)).isTrue();
         assertThat(Arrays.equals(credential.getEncryptedMetadata(), ENCRYPTED_METADATA)).isTrue();
@@ -86,7 +86,7 @@
 
         assertThat(credentialFromParcel.getType()).isEqualTo(CREDENTIAL_TYPE_PUBLIC);
         assertThat(credentialFromParcel.getIdentityType()).isEqualTo(IDENTITY_TYPE_PRIVATE);
-        assertThat(Arrays.equals(credentialFromParcel.getSecreteId(), SECRETE_ID)).isTrue();
+        assertThat(Arrays.equals(credentialFromParcel.getSecretId(), SECRETE_ID)).isTrue();
         assertThat(Arrays.equals(credentialFromParcel.getAuthenticityKey(),
                 AUTHENTICITY_KEY)).isTrue();
         assertThat(Arrays.equals(credentialFromParcel.getPublicKey(), PUBLIC_KEY)).isTrue();
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/ScanRequestTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/ScanRequestTest.java
index 3bb348b..25aa926 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/ScanRequestTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/ScanRequestTest.java
@@ -16,6 +16,7 @@
 
 package android.nearby.cts;
 
+import static android.nearby.PresenceCredential.IDENTITY_TYPE_PRIVATE;
 import static android.nearby.ScanRequest.SCAN_MODE_BALANCED;
 import static android.nearby.ScanRequest.SCAN_MODE_LOW_LATENCY;
 import static android.nearby.ScanRequest.SCAN_MODE_LOW_POWER;
@@ -27,6 +28,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.nearby.PresenceScanFilter;
+import android.nearby.PublicCredential;
 import android.nearby.ScanRequest;
 import android.os.Build;
 import android.os.WorkSource;
@@ -62,7 +65,7 @@
         assertThat(request.getScanMode()).isEqualTo(SCAN_MODE_LOW_POWER);
     }
 
-    /** Verify setting work source with null value in the scan request is allowed*/
+    /** Verify setting work source with null value in the scan request is allowed */
     @Test
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
     public void testSetWorkSource_nullValue() {
@@ -151,6 +154,38 @@
         assertThat(ScanRequest.scanModeToString(-2)).isEqualTo("SCAN_MODE_INVALID");
     }
 
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testScanFilter() {
+        final byte[] secreteId = new byte[]{1, 2, 3, 4};
+        final byte[] authenticityKey = new byte[]{0, 1, 1, 1};
+        final byte[] publicKey = new byte[]{1, 1, 2, 2};
+        final byte[] encryptedMetadata = new byte[]{1, 2, 3, 4, 5};
+        final byte[] metadataEncryptionKeyTag = new byte[]{1, 1, 3, 4, 5};
+
+        PublicCredential credential = new PublicCredential.Builder()
+                .setIdentityType(IDENTITY_TYPE_PRIVATE)
+                .setSecretId(secreteId)
+                .setAuthenticityKey(authenticityKey)
+                .setEncryptedMetadata(encryptedMetadata)
+                .setPublicKey(publicKey)
+                .setMetaDataEncryptionKeyTag(metadataEncryptionKeyTag).build();
+
+        final int rssi = -40;
+        final int action = 123;
+        PresenceScanFilter filter = new PresenceScanFilter.Builder()
+                .addCredential(credential)
+                .setRssiThreshold(rssi)
+                .addPresenceAction(action)
+                .build();
+
+        ScanRequest request = new ScanRequest.Builder().setScanType(
+                SCAN_TYPE_FAST_PAIR).addScanFilter(filter).build();
+
+        assertThat(request.getScanFilters()).isNotEmpty();
+        assertThat(request.getScanFilters().get(0).getRssiThreshold()).isEqualTo(rssi);
+    }
+
     private static WorkSource getWorkSource() {
         return new WorkSource(UID, APP_NAME);
     }