Hook CHRE host implementation with Presence API.

Demo Nearby nanoapp e2e.

To test on device, once the device is booted, screen on will update the
fitler and the filtered result will show in the log:

adb logcat -s NearbyService -s CHRE

Ignore-AOSP-First: internal mainline development.

Works together with cl/433682470. Flash with CHRE update first before
device testing.

Test: atest NearbyUnitTests, atest android.nearby.cts, and on device test as above.

Bug: 221082271
Change-Id: I335ed77d68884c40d687118e23319a2f128c744c
diff --git a/nearby/framework/java/android/nearby/CredentialElement.java b/nearby/framework/java/android/nearby/CredentialElement.java
index d2049d1..7a43b01 100644
--- a/nearby/framework/java/android/nearby/CredentialElement.java
+++ b/nearby/framework/java/android/nearby/CredentialElement.java
@@ -17,12 +17,16 @@
 package android.nearby;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import com.android.internal.util.Preconditions;
 
+import java.util.Arrays;
+import java.util.Objects;
+
 /**
  * Represents an element in {@link PresenceCredential}.
  *
@@ -33,12 +37,9 @@
     private final String mKey;
     private final byte[] mValue;
 
-    /**
-     * Constructs a {@link CredentialElement}.
-     */
+    /** Constructs a {@link CredentialElement}. */
     public CredentialElement(@NonNull String key, @NonNull byte[] value) {
-        Preconditions.checkState(key != null && value != null,
-                "neither key or value can be null");
+        Preconditions.checkState(key != null && value != null, "neither key or value can be null");
         mKey = key;
         mValue = value;
     }
@@ -58,7 +59,7 @@
                 public CredentialElement[] newArray(int size) {
                     return new CredentialElement[size];
                 }
-    };
+            };
 
     @Override
     public int describeContents() {
@@ -72,19 +73,29 @@
         dest.writeByteArray(mValue);
     }
 
-    /**
-     * Returns the key of the credential element.
-     */
+    /** Returns the key of the credential element. */
     @NonNull
     public String getKey() {
         return mKey;
     }
 
-    /**
-     * Returns the value of the credential element.
-     */
+    /** Returns the value of the credential element. */
     @NonNull
     public byte[] getValue() {
         return mValue;
     }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (obj instanceof CredentialElement) {
+            CredentialElement that = (CredentialElement) obj;
+            return mKey.equals(that.mKey) && Arrays.equals(mValue, that.mValue);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mKey.hashCode(), Arrays.hashCode(mValue));
+    }
 }
diff --git a/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java b/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java
index 694e15b..1ad3571 100644
--- a/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java
+++ b/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java
@@ -29,21 +29,18 @@
 
 /**
  * A data class representing scan result from Nearby Service. Scan result can come from multiple
- * mediums like BLE, Wi-Fi Aware, and etc.
- * A scan result  consists of
- * An encapsulation of various parameters for requesting nearby scans.
+ * mediums like BLE, Wi-Fi Aware, and etc. A scan result consists of An encapsulation of various
+ * parameters for requesting nearby scans.
  *
  * <p>All scan results generated through {@link NearbyManager} are guaranteed to have a valid
- * medium, identifier, timestamp (both UTC time and elapsed real-time since boot), and accuracy.
- * All other parameters are optional.
+ * medium, identifier, timestamp (both UTC time and elapsed real-time since boot), and accuracy. All
+ * other parameters are optional.
  *
  * @hide
  */
 public final class NearbyDeviceParcelable implements Parcelable {
 
-    /**
-     * Used to read a NearbyDeviceParcelable from a Parcel.
-     */
+    /** Used to read a NearbyDeviceParcelable from a Parcel. */
     @NonNull
     public static final Creator<NearbyDeviceParcelable> CREATOR =
             new Creator<NearbyDeviceParcelable>() {
@@ -54,8 +51,13 @@
                         builder.setName(in.readString());
                     }
                     builder.setMedium(in.readInt());
-                    builder.setRssi(in.readInt());
                     builder.setTxPower(in.readInt());
+                    builder.setRssi(in.readInt());
+                    builder.setAction(in.readInt());
+                    builder.setPublicCredential(
+                            in.readParcelable(
+                                    PublicCredential.class.getClassLoader(),
+                                    PublicCredential.class));
                     if (in.readInt() == 1) {
                         builder.setFastPairModelId(in.readString());
                     }
@@ -78,36 +80,40 @@
             };
 
     @ScanRequest.ScanType int mScanType;
-    @Nullable
-    private final String mName;
-    @NearbyDevice.Medium
-    private final int mMedium;
-    private final int mRssi;
+    @Nullable private final String mName;
+    @NearbyDevice.Medium private final int mMedium;
     private final int mTxPower;
+    private final int mRssi;
+    private final int mAction;
+    private final PublicCredential mPublicCredential;
+    @Nullable private final String mBluetoothAddress;
+    @Nullable private final String mFastPairModelId;
+    @Nullable private final byte[] mData;
 
-    @Nullable
-    private final String mBluetoothAddress;
-    @Nullable
-    private final String mFastPairModelId;
-    @Nullable
-    private final byte[] mData;
-
-    private NearbyDeviceParcelable(@ScanRequest.ScanType int scanType, @Nullable String name,
-            int medium, int rssi, int txPower, @Nullable String fastPairModelId,
-            @Nullable String bluetoothAddress, @Nullable byte[] data) {
+    private NearbyDeviceParcelable(
+            @ScanRequest.ScanType int scanType,
+            @Nullable String name,
+            int medium,
+            int TxPower,
+            int rssi,
+            int action,
+            PublicCredential publicCredential,
+            @Nullable String fastPairModelId,
+            @Nullable String bluetoothAddress,
+            @Nullable byte[] data) {
         mScanType = scanType;
         mName = name;
         mMedium = medium;
+        mTxPower = TxPower;
         mRssi = rssi;
-        mTxPower = txPower;
+        mAction = action;
+        mPublicCredential = publicCredential;
         mFastPairModelId = fastPairModelId;
         mBluetoothAddress = bluetoothAddress;
         mData = data;
     }
 
-    /**
-     * No special parcel contents.
-     */
+    /** No special parcel contents. */
     @Override
     public int describeContents() {
         return 0;
@@ -119,7 +125,6 @@
      * @param dest The Parcel in which the object should be written.
      * @param flags Additional flags about how the object should be written.
      */
-
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(mName == null ? 0 : 1);
@@ -127,8 +132,10 @@
             dest.writeString(mName);
         }
         dest.writeInt(mMedium);
-        dest.writeInt(mRssi);
         dest.writeInt(mTxPower);
+        dest.writeInt(mRssi);
+        dest.writeInt(mAction);
+        dest.writeParcelable(mPublicCredential, flags);
         dest.writeInt(mFastPairModelId == null ? 0 : 1);
         if (mFastPairModelId != null) {
             dest.writeString(mFastPairModelId);
@@ -144,19 +151,26 @@
         }
     }
 
-    /**
-     * Returns a string representation of this ScanRequest.
-     */
+    /** Returns a string representation of this ScanRequest. */
     @Override
     public String toString() {
         return "NearbyDeviceParcelable["
-                + "name=" + mName
-                + ", medium=" + NearbyDevice.mediumToString(mMedium)
-                + ", rssi=" + mRssi
-                + ", txPower=" + mTxPower
-                + ", bluetoothAddress=" + mBluetoothAddress
-                + ", fastPairModelId=" + mFastPairModelId
-                + ", data=" + Arrays.toString(mData)
+                + "name="
+                + mName
+                + ", medium="
+                + NearbyDevice.mediumToString(mMedium)
+                + ", txPower="
+                + mTxPower
+                + ", rssi="
+                + mRssi
+                + ", action="
+                + mAction
+                + ", bluetoothAddress="
+                + mBluetoothAddress
+                + ", fastPairModelId="
+                + mFastPairModelId
+                + ", data="
+                + Arrays.toString(mData)
                 + "]";
     }
 
@@ -164,10 +178,13 @@
     public boolean equals(Object other) {
         if (other instanceof NearbyDeviceParcelable) {
             NearbyDeviceParcelable otherNearbyDeviceParcelable = (NearbyDeviceParcelable) other;
-            return  Objects.equals(mName, otherNearbyDeviceParcelable.mName)
+            return Objects.equals(mName, otherNearbyDeviceParcelable.mName)
                     && (mMedium == otherNearbyDeviceParcelable.mMedium)
-                    && (mRssi == otherNearbyDeviceParcelable.mRssi)
                     && (mTxPower == otherNearbyDeviceParcelable.mTxPower)
+                    && (mRssi == otherNearbyDeviceParcelable.mRssi)
+                    && (mAction == otherNearbyDeviceParcelable.mAction)
+                    && (Objects.equals(
+                            mPublicCredential, otherNearbyDeviceParcelable.mPublicCredential))
                     && (Objects.equals(
                             mBluetoothAddress, otherNearbyDeviceParcelable.mBluetoothAddress))
                     && (Objects.equals(
@@ -180,8 +197,14 @@
     @Override
     public int hashCode() {
         return Objects.hash(
-                mName, mMedium, mRssi, mTxPower, mBluetoothAddress,
-                mFastPairModelId, Arrays.hashCode(mData));
+                mName,
+                mMedium,
+                mRssi,
+                mAction,
+                mPublicCredential.hashCode(),
+                mBluetoothAddress,
+                mFastPairModelId,
+                Arrays.hashCode(mData));
     }
 
     /**
@@ -194,9 +217,7 @@
         return mScanType;
     }
 
-    /**
-     * Gets the name of the NearbyDeviceParcelable. Returns {@code null} If there is no name.
-     */
+    /** Gets the name of the NearbyDeviceParcelable. Returns {@code null} If there is no name. */
     @Nullable
     public String getName() {
         return mName;
@@ -212,21 +233,39 @@
     }
 
     /**
-     * Gets the received signal strength in dBm.
+     * Gets the transmission power in dBm.
+     *
+     * @hide
      */
     @IntRange(from = -127, to = 126)
+    public int getTxPower() {
+        return mTxPower;
+    }
+
+    /** Gets the received signal strength in dBm. */
+    @IntRange(from = -127, to = 126)
     public int getRssi() {
         return mRssi;
     }
 
     /**
-     * Gets the transmit power in dBm. A value of
-     * android.bluetooth.le.ScanResult#TX_POWER_NOT_PRESENT
-     * indicates that the TX power is not present.
+     * Gets the Action.
+     *
+     * @hide
      */
     @IntRange(from = -127, to = 126)
-    public int getTxPower() {
-        return mTxPower;
+    public int getAction() {
+        return mAction;
+    }
+
+    /**
+     * Gets the public credential.
+     *
+     * @hide
+     */
+    @NonNull
+    public PublicCredential getPublicCredential() {
+        return mPublicCredential;
     }
 
     /**
@@ -247,31 +286,24 @@
         return mBluetoothAddress;
     }
 
-    /**
-     * Gets the raw data from the scanning. Returns {@code null} if there is no extra data.
-     */
+    /** Gets the raw data from the scanning. Returns {@code null} if there is no extra data. */
     @Nullable
     public byte[] getData() {
         return mData;
     }
 
-    /**
-     * Builder class for {@link NearbyDeviceParcelable}.
-     */
+    /** Builder class for {@link NearbyDeviceParcelable}. */
     public static final class Builder {
-        @Nullable
-        private String mName;
-        @NearbyDevice.Medium
-        private int mMedium;
+        @Nullable private String mName;
+        @NearbyDevice.Medium private int mMedium;
+        private int mTxPower;
         private int mRssi;
-        private int mTxPower = ScanResult.TX_POWER_NOT_PRESENT;
+        private int mAction;
+        private PublicCredential mPublicCredential;
         @ScanRequest.ScanType int mScanType;
-        @Nullable
-        private String mFastPairModelId;
-        @Nullable
-        private String mBluetoothAddress;
-        @Nullable
-        private byte[] mData;
+        @Nullable private String mFastPairModelId;
+        @Nullable private String mBluetoothAddress;
+        @Nullable private byte[] mData;
 
         /**
          * Sets the scan type of the NearbyDeviceParcelable.
@@ -306,6 +338,18 @@
         }
 
         /**
+         * Sets the transmission power of the discovered device.
+         *
+         * @param txPower The transmission power in dBm.
+         * @hide
+         */
+        @NonNull
+        public Builder setTxPower(int txPower) {
+            mTxPower = txPower;
+            return this;
+        }
+
+        /**
          * Sets the RSSI between scanned device and the discovered device.
          *
          * @param rssi The received signal strength in dBm.
@@ -317,13 +361,26 @@
         }
 
         /**
-         * Sets the txPower.
+         * Sets the action from the discovered device.
          *
-         * @param txPower The transmit power in dBm
+         * @param action The action of the discovered device.
+         * @hide
          */
         @NonNull
-        public Builder setTxPower(@IntRange(from = -127, to = 126) int txPower) {
-            mTxPower = txPower;
+        public Builder setAction(int action) {
+            mAction = action;
+            return this;
+        }
+
+        /**
+         * Sets the public credential of the discovered device.
+         *
+         * @param publicCredential The public credential.
+         * @hide
+         */
+        @NonNull
+        public Builder setPublicCredential(@NonNull PublicCredential publicCredential) {
+            mPublicCredential = publicCredential;
             return this;
         }
 
@@ -352,8 +409,8 @@
         /**
          * Sets the scanned raw data.
          *
-         * @param data Data the scan.
-         * For example, {@link ScanRecord#getServiceData()} if scanned by Bluetooth.
+         * @param data Data the scan. For example, {@link ScanRecord#getServiceData()} if scanned by
+         *     Bluetooth.
          */
         @NonNull
         public Builder setData(@Nullable byte[] data) {
@@ -361,13 +418,20 @@
             return this;
         }
 
-        /**
-         * Builds a ScanResult.
-         */
+        /** Builds a ScanResult. */
         @NonNull
         public NearbyDeviceParcelable build() {
-            return new NearbyDeviceParcelable(mScanType, mName, mMedium, mRssi, mTxPower,
-                    mFastPairModelId, mBluetoothAddress, mData);
+            return new NearbyDeviceParcelable(
+                    mScanType,
+                    mName,
+                    mMedium,
+                    mTxPower,
+                    mRssi,
+                    mAction,
+                    mPublicCredential,
+                    mFastPairModelId,
+                    mBluetoothAddress,
+                    mData);
         }
     }
 }
diff --git a/nearby/framework/java/android/nearby/PresenceCredential.java b/nearby/framework/java/android/nearby/PresenceCredential.java
index 7ad6ae9..1e851d6 100644
--- a/nearby/framework/java/android/nearby/PresenceCredential.java
+++ b/nearby/framework/java/android/nearby/PresenceCredential.java
@@ -24,7 +24,9 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Represents a credential for Nearby Presence.
@@ -33,50 +35,43 @@
  */
 @SystemApi
 public abstract class PresenceCredential {
-    /**
-     * Private credential type.
-     */
+    /** Private credential type. */
     public static final int CREDENTIAL_TYPE_PRIVATE = 0;
 
-    /**
-     * Public credential type.
-     */
+    /** Public credential type. */
     public static final int CREDENTIAL_TYPE_PUBLIC = 1;
 
-    /** @hide **/
+    /**
+     * @hide *
+     */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({CREDENTIAL_TYPE_PUBLIC, CREDENTIAL_TYPE_PRIVATE})
-    public @interface CredentialType {
-    }
+    public @interface CredentialType {}
 
-    /**
-     * Unknown identity type.
-     */
+    /** Unknown identity type. */
     public static final int IDENTITY_TYPE_UNKNOWN = 0;
 
-    /**
-     * Private identity type.
-     */
+    /** Private identity type. */
     public static final int IDENTITY_TYPE_PRIVATE = 1;
-    /**
-     * Provisioned identity type.
-     */
+    /** Provisioned identity type. */
     public static final int IDENTITY_TYPE_PROVISIONED = 2;
-    /**
-     * Trusted identity type.
-     */
+    /** Trusted identity type. */
     public static final int IDENTITY_TYPE_TRUSTED = 3;
-    /**
-     * Public identity type.
-     */
+    /** Public identity type. */
     public static final int IDENTITY_TYPE_PUBLIC = 4;
 
-    /** @hide **/
+    /**
+     * @hide *
+     */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({IDENTITY_TYPE_UNKNOWN, IDENTITY_TYPE_PRIVATE, IDENTITY_TYPE_PROVISIONED,
-            IDENTITY_TYPE_TRUSTED, IDENTITY_TYPE_PUBLIC})
-    public @interface IdentityType {
-    }
+    @IntDef({
+        IDENTITY_TYPE_UNKNOWN,
+        IDENTITY_TYPE_PRIVATE,
+        IDENTITY_TYPE_PROVISIONED,
+        IDENTITY_TYPE_TRUSTED,
+        IDENTITY_TYPE_PUBLIC
+    })
+    public @interface IdentityType {}
 
     private final @CredentialType int mType;
     private final @IdentityType int mIdentityType;
@@ -84,8 +79,12 @@
     private final byte[] mAuthenticityKey;
     private final List<CredentialElement> mCredentialElements;
 
-    PresenceCredential(@CredentialType int type, @IdentityType int identityType,
-            byte[] secretId, byte[] authenticityKey, List<CredentialElement> credentialElements) {
+    PresenceCredential(
+            @CredentialType int type,
+            @IdentityType int identityType,
+            byte[] secretId,
+            byte[] authenticityKey,
+            List<CredentialElement> credentialElements) {
         mType = type;
         mIdentityType = identityType;
         mSecretId = secretId;
@@ -101,48 +100,63 @@
         mAuthenticityKey = new byte[in.readInt()];
         in.readByteArray(mAuthenticityKey);
         mCredentialElements = new ArrayList<>();
-        in.readList(mCredentialElements, CredentialElement.class.getClassLoader(),
+        in.readList(
+                mCredentialElements,
+                CredentialElement.class.getClassLoader(),
                 CredentialElement.class);
     }
 
-    /**
-     * Returns the type of the credential.
-     */
+    /** Returns the type of the credential. */
     public @CredentialType int getType() {
         return mType;
     }
 
-    /**
-     * Returns the identity type of the credential.
-     */
+    /** Returns the identity type of the credential. */
     public @IdentityType int getIdentityType() {
         return mIdentityType;
     }
 
-    /**
-     * Returns the secret id of the credential.
-     */
+    /** Returns the secret id of the credential. */
     @NonNull
     public byte[] getSecretId() {
         return mSecretId;
     }
 
-    /**
-     * Returns the authenticity key of the credential.
-     */
+    /** Returns the authenticity key of the credential. */
     @NonNull
     public byte[] getAuthenticityKey() {
         return mAuthenticityKey;
     }
 
-    /**
-     * Returns the elements of the credential.
-     */
+    /** Returns the elements of the credential. */
     @NonNull
     public List<CredentialElement> getCredentialElements() {
         return mCredentialElements;
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof PresenceCredential) {
+            PresenceCredential that = (PresenceCredential) obj;
+            return mType == that.mType
+                    && mIdentityType == that.mIdentityType
+                    && Arrays.equals(mSecretId, that.mSecretId)
+                    && Arrays.equals(mAuthenticityKey, that.mAuthenticityKey)
+                    && mCredentialElements.equals(that.mCredentialElements);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mType,
+                mIdentityType,
+                Arrays.hashCode(mSecretId),
+                Arrays.hashCode(mAuthenticityKey),
+                mCredentialElements.hashCode());
+    }
+
     /**
      * Writes the presence credential to the parcel.
      *
diff --git a/nearby/framework/java/android/nearby/PublicCredential.java b/nearby/framework/java/android/nearby/PublicCredential.java
index 8aac323..5998d19 100644
--- a/nearby/framework/java/android/nearby/PublicCredential.java
+++ b/nearby/framework/java/android/nearby/PublicCredential.java
@@ -17,6 +17,7 @@
 package android.nearby;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -24,6 +25,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
@@ -35,25 +37,31 @@
 @SystemApi
 public final class PublicCredential extends PresenceCredential implements Parcelable {
     @NonNull
-    public static final Creator<PublicCredential> CREATOR = new Creator<PublicCredential>() {
-        @Override
-        public PublicCredential createFromParcel(Parcel in) {
-            in.readInt(); // Skip the type as it's used by parent class only.
-            return createFromParcelBody(in);
-        }
+    public static final Creator<PublicCredential> CREATOR =
+            new Creator<PublicCredential>() {
+                @Override
+                public PublicCredential createFromParcel(Parcel in) {
+                    in.readInt(); // Skip the type as it's used by parent class only.
+                    return createFromParcelBody(in);
+                }
 
-        @Override
-        public PublicCredential[] newArray(int size) {
-            return new PublicCredential[size];
-        }
-    };
+                @Override
+                public PublicCredential[] newArray(int size) {
+                    return new PublicCredential[size];
+                }
+            };
 
     private final byte[] mPublicKey;
     private final byte[] mEncryptedMetadata;
     private final byte[] mEncryptedMetadataKeyTag;
 
-    private PublicCredential(int identityType, byte[] secretId, byte[] authenticityKey,
-            List<CredentialElement> credentialElements, byte[] publicKey, byte[] encryptedMetadata,
+    private PublicCredential(
+            int identityType,
+            byte[] secretId,
+            byte[] authenticityKey,
+            List<CredentialElement> credentialElements,
+            byte[] publicKey,
+            byte[] encryptedMetadata,
             byte[] metadataEncryptionKeyTag) {
         super(CREDENTIAL_TYPE_PUBLIC, identityType, secretId, authenticityKey, credentialElements);
         mPublicKey = publicKey;
@@ -75,25 +83,19 @@
         return new PublicCredential(in);
     }
 
-    /**
-     * Returns the public key associated with this credential.
-     */
+    /** Returns the public key associated with this credential. */
     @NonNull
     public byte[] getPublicKey() {
         return mPublicKey;
     }
 
-    /**
-     * Returns the encrypted metadata associated with this credential.
-     */
+    /** Returns the encrypted metadata associated with this credential. */
     @NonNull
     public byte[] getEncryptedMetadata() {
         return mEncryptedMetadata;
     }
 
-    /**
-     * Returns the metadata encryption key tag associated with this credential.
-     */
+    /** Returns the metadata encryption key tag associated with this credential. */
     @NonNull
     public byte[] getEncryptedMetadataKeyTag() {
         return mEncryptedMetadataKeyTag;
@@ -105,6 +107,27 @@
     }
 
     @Override
+    public boolean equals(@Nullable Object obj) {
+        if (obj instanceof PublicCredential) {
+            PublicCredential that = (PublicCredential) obj;
+            return super.equals(obj)
+                    && Arrays.equals(mPublicKey, that.mPublicKey)
+                    && Arrays.equals(mEncryptedMetadata, that.mEncryptedMetadata)
+                    && Arrays.equals(mEncryptedMetadataKeyTag, that.mEncryptedMetadataKeyTag);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                super.hashCode(),
+                Arrays.hashCode(mPublicKey),
+                Arrays.hashCode(mEncryptedMetadata),
+                Arrays.hashCode(mEncryptedMetadataKeyTag));
+    }
+
+    @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         dest.writeInt(mPublicKey.length);
@@ -115,9 +138,7 @@
         dest.writeByteArray(mEncryptedMetadataKeyTag);
     }
 
-    /**
-     * Builder class for {@link PresenceCredential}.
-     */
+    /** Builder class for {@link PresenceCredential}. */
     public static final class Builder {
         private final List<CredentialElement> mCredentialElements;
 
@@ -128,17 +149,21 @@
         private final byte[] mEncryptedMetadata;
         private final byte[] mEncryptedMetadataKeyTag;
 
-        public Builder(@NonNull byte[] secretId, @NonNull byte[] authenticityKey,
-                @NonNull byte[] publicKey, @NonNull byte[] encryptedMetadata,
+        public Builder(
+                @NonNull byte[] secretId,
+                @NonNull byte[] authenticityKey,
+                @NonNull byte[] publicKey,
+                @NonNull byte[] encryptedMetadata,
                 @NonNull byte[] encryptedMetadataKeyTag) {
-            Preconditions.checkState(secretId != null && secretId.length > 0,
-                    "secret id cannot be empty");
-            Preconditions.checkState(authenticityKey != null && authenticityKey.length > 0,
+            Preconditions.checkState(
+                    secretId != null && secretId.length > 0, "secret id cannot be empty");
+            Preconditions.checkState(
+                    authenticityKey != null && authenticityKey.length > 0,
                     "authenticity key cannot be empty");
             Preconditions.checkState(
-                    publicKey != null && publicKey.length > 0,
-                    "publicKey cannot be empty");
-            Preconditions.checkState(encryptedMetadata != null && encryptedMetadata.length > 0,
+                    publicKey != null && publicKey.length > 0, "publicKey cannot be empty");
+            Preconditions.checkState(
+                    encryptedMetadata != null && encryptedMetadata.length > 0,
                     "encryptedMetadata cannot be empty");
             Preconditions.checkState(
                     encryptedMetadataKeyTag != null && encryptedMetadataKeyTag.length > 0,
@@ -152,18 +177,14 @@
             mCredentialElements = new ArrayList<>();
         }
 
-        /**
-         * Sets the identity type for the presence credential.
-         */
+        /** Sets the identity type for the presence credential. */
         @NonNull
         public Builder setIdentityType(@IdentityType int identityType) {
             mIdentityType = identityType;
             return this;
         }
 
-        /**
-         * Adds an element to the credential.
-         */
+        /** Adds an element to the credential. */
         @NonNull
         public Builder addCredentialElement(@NonNull CredentialElement credentialElement) {
             Objects.requireNonNull(credentialElement);
@@ -171,14 +192,17 @@
             return this;
         }
 
-        /**
-         * Builds the {@link PresenceCredential}.
-         */
+        /** Builds the {@link PresenceCredential}. */
         @NonNull
         public PublicCredential build() {
-            return new PublicCredential(mIdentityType, mSecretId, mAuthenticityKey,
-                    mCredentialElements, mPublicKey, mEncryptedMetadata, mEncryptedMetadataKeyTag);
+            return new PublicCredential(
+                    mIdentityType,
+                    mSecretId,
+                    mAuthenticityKey,
+                    mCredentialElements,
+                    mPublicKey,
+                    mEncryptedMetadata,
+                    mEncryptedMetadataKeyTag);
         }
-
     }
 }
diff --git a/nearby/service/java/com/android/server/nearby/NearbyService.java b/nearby/service/java/com/android/server/nearby/NearbyService.java
index d721575..e3e5b5d 100644
--- a/nearby/service/java/com/android/server/nearby/NearbyService.java
+++ b/nearby/service/java/com/android/server/nearby/NearbyService.java
@@ -39,16 +39,11 @@
 import com.android.server.nearby.fastpair.FastPairManager;
 import com.android.server.nearby.injector.ContextHubManagerAdapter;
 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;
 
-import java.util.concurrent.Executors;
-
-import service.proto.Blefilter;
-
 /** Service implementing nearby functionality. */
 public class NearbyService extends INearbyManager.Stub {
     public static final String TAG = "NearbyService";
@@ -84,19 +79,7 @@
         mBroadcastProviderManager = new BroadcastProviderManager(context, mSystemInjector);
         final LocatorContextWrapper lcw = new LocatorContextWrapper(context, null);
         mFastPairManager = new FastPairManager(lcw);
-        mPresenceManager =
-                new PresenceManager(
-                        mContext,
-                        (results) -> {
-                            // TODO(b/221082271): hooked with API codes.
-                            for (Blefilter.BleFilterResult result : results.getResultList()) {
-                                Log.i(
-                                        TAG,
-                                        String.format(
-                                                "received filter result with id: %d",
-                                                result.getId()));
-                            }
-                        });
+        mPresenceManager = new PresenceManager(lcw);
     }
 
     @Override
@@ -144,10 +127,9 @@
                         mBluetoothReceiver,
                         new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
                 mFastPairManager.initiate();
+                // Initialize ContextManager for CHRE scan.
                 mSystemInjector.initializeContextHubManagerAdapter();
-                mPresenceManager.initiate(
-                        new ChreCommunication(
-                                mSystemInjector, Executors.newSingleThreadExecutor()));
+                mPresenceManager.initiate();
                 break;
         }
     }
@@ -155,7 +137,7 @@
     private static final class SystemInjector implements Injector {
         private final Context mContext;
         @Nullable private BluetoothAdapter mBluetoothAdapter;
-        @Nullable private  ContextHubManagerAdapter mContextHubManagerAdapter;
+        @Nullable private ContextHubManagerAdapter mContextHubManagerAdapter;
 
         SystemInjector(Context context) {
             mContext = context;
@@ -168,6 +150,7 @@
         }
 
         @Override
+        @Nullable
         public ContextHubManagerAdapter getContextHubManagerAdapter() {
             return mContextHubManagerAdapter;
         }
@@ -193,6 +176,5 @@
             }
             mContextHubManagerAdapter = new ContextHubManagerAdapter(manager);
         }
-
     }
 }
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 e48074d..80ad88d 100644
--- a/nearby/service/java/com/android/server/nearby/presence/PresenceDiscoveryResult.java
+++ b/nearby/service/java/com/android/server/nearby/presence/PresenceDiscoveryResult.java
@@ -17,6 +17,7 @@
 package com.android.server.nearby.presence;
 
 import android.nearby.NearbyDevice;
+import android.nearby.NearbyDeviceParcelable;
 import android.nearby.PresenceDevice;
 import android.nearby.PresenceScanFilter;
 import android.nearby.PublicCredential;
@@ -24,16 +25,17 @@
 import java.util.ArrayList;
 import java.util.List;
 
-/**
- * Represents a Presence discovery result.
- */
+/** Represents a Presence discovery result. */
 public class PresenceDiscoveryResult {
 
-    /**
-     * Creates a {@link PresenceDiscoveryResult} from the scan data.
-     */
-    public static PresenceDiscoveryResult fromScanData(byte[] scanData, int rssi) {
-        return new PresenceDiscoveryResult.Builder().setRssi(rssi).build();
+    /** Creates a {@link PresenceDiscoveryResult} from the scan data. */
+    public static PresenceDiscoveryResult fromDevice(NearbyDeviceParcelable device) {
+        return new PresenceDiscoveryResult.Builder()
+                .setTxPower(device.getTxPower())
+                .setRssi(device.getRssi())
+                .addPresenceAction(device.getAction())
+                .setPublicCredential(device.getPublicCredential())
+                .build();
     }
 
     private final int mTxPower;
@@ -42,8 +44,12 @@
     private final List<Integer> mPresenceActions;
     private final PublicCredential mPublicCredential;
 
-    private PresenceDiscoveryResult(int txPower, int rssi, byte[] salt,
-            List<Integer> presenceActions, PublicCredential publicCredential) {
+    private PresenceDiscoveryResult(
+            int txPower,
+            int rssi,
+            byte[] salt,
+            List<Integer> presenceActions,
+            PublicCredential publicCredential) {
         mTxPower = txPower;
         mRssi = rssi;
         mSalt = salt;
@@ -51,9 +57,7 @@
         mPublicCredential = publicCredential;
     }
 
-    /**
-     * Returns whether the discovery result matches the scan filter.
-     */
+    /** Returns whether the discovery result matches the scan filter. */
     public boolean matches(PresenceScanFilter scanFilter) {
         return pathLossMatches(scanFilter.getMaxPathLoss())
                 && actionMatches(scanFilter.getPresenceActions())
@@ -75,9 +79,7 @@
         return credentials.contains(mPublicCredential);
     }
 
-    /**
-     * Converts a presence device from the discovery result.
-     */
+    /** Converts a presence device from the discovery result. */
     public PresenceDevice toPresenceDevice() {
         return new PresenceDevice.Builder(
                 // Use the public credential hash as the device Id.
@@ -86,12 +88,11 @@
                 mPublicCredential.getSecretId(),
                 mPublicCredential.getEncryptedMetadata())
                 .setRssi(mRssi)
-                .addMedium(NearbyDevice.Medium.BLE).build();
+                .addMedium(NearbyDevice.Medium.BLE)
+                .build();
     }
 
-    /**
-     * Builder for {@link PresenceDiscoveryResult}.
-     */
+    /** Builder for {@link PresenceDiscoveryResult}. */
     public static class Builder {
         private int mTxPower;
         private int mRssi;
@@ -104,52 +105,40 @@
             mPresenceActions = new ArrayList<>();
         }
 
-        /**
-         * Sets the calibrated tx power for the discovery result.
-         */
+        /** Sets the calibrated tx power for the discovery result. */
         public Builder setTxPower(int txPower) {
             mTxPower = txPower;
             return this;
         }
 
-        /**
-         * Sets the rssi for the discovery result.
-         */
+        /** Sets the rssi for the discovery result. */
         public Builder setRssi(int rssi) {
             mRssi = rssi;
             return this;
         }
 
-        /**
-         * Sets the salt for the discovery result.
-         */
+        /** Sets the salt for the discovery result. */
         public Builder setSalt(byte[] salt) {
             mSalt = salt;
             return this;
         }
 
-        /**
-         * Sets the public credential for the discovery result.
-         */
+        /** Sets the public credential for the discovery result. */
         public Builder setPublicCredential(PublicCredential publicCredential) {
             mPublicCredential = publicCredential;
             return this;
         }
 
-        /**
-         * Adds presence action of the discovery result.
-         */
+        /** Adds presence action of the discovery result. */
         public Builder addPresenceAction(int presenceAction) {
             mPresenceActions.add(presenceAction);
             return this;
         }
 
-        /**
-         * Builds a {@link PresenceDiscoveryResult}.
-         */
+        /** Builds a {@link PresenceDiscoveryResult}. */
         public PresenceDiscoveryResult build() {
-            return new PresenceDiscoveryResult(mTxPower, mRssi, mSalt, mPresenceActions,
-                    mPublicCredential);
+            return new PresenceDiscoveryResult(
+                    mTxPower, mRssi, mSalt, mPresenceActions, mPublicCredential);
         }
     }
 }
diff --git a/nearby/service/java/com/android/server/nearby/presence/PresenceManager.java b/nearby/service/java/com/android/server/nearby/presence/PresenceManager.java
index 66d4864..382c47a 100644
--- a/nearby/service/java/com/android/server/nearby/presence/PresenceManager.java
+++ b/nearby/service/java/com/android/server/nearby/presence/PresenceManager.java
@@ -16,152 +16,121 @@
 
 package com.android.server.nearby.presence;
 
+import static com.android.server.nearby.NearbyService.TAG;
+
+import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.hardware.location.NanoAppMessage;
+import android.nearby.NearbyDevice;
+import android.nearby.NearbyManager;
+import android.nearby.PresenceScanFilter;
+import android.nearby.PublicCredential;
+import android.nearby.ScanCallback;
+import android.nearby.ScanRequest;
 import android.util.Log;
 
-import com.android.internal.annotations.VisibleForTesting;
+import androidx.annotation.NonNull;
 
-import com.google.protobuf.ByteString;
-import com.google.protobuf.InvalidProtocolBufferException;
+import com.android.server.nearby.common.locator.Locator;
+import com.android.server.nearby.common.locator.LocatorContextWrapper;
 
-import java.util.Collections;
-
-import service.proto.Blefilter;
+import java.util.Locale;
+import java.util.concurrent.Executors;
 
 /** PresenceManager is the class initiated in nearby service to handle presence related work. */
 public class PresenceManager {
-    /** Callback that receives filter results from CHRE Nanoapp. */
-    public interface PresenceCallback {
-        /** Called when {@link BleFilterResults} has been received. */
-        void onFilterResults(Blefilter.BleFilterResults filterResults);
-    }
 
-    private static final String TAG = "PresenceService";
-    // Nanoapp ID reserved for Nearby Presence.
-    /** @hide */
-    @VisibleForTesting
-    public static final long NANOAPP_ID = 0x476f6f676c001031L;
-    /** @hide */
-    @VisibleForTesting
-    public static final int NANOAPP_MESSAGE_TYPE_FILTER = 3;
-    /** @hide */
-    @VisibleForTesting
-    public static final int NANOAPP_MESSAGE_TYPE_FILTER_RESULT = 4;
-    private final Context mContext;
-    private final PresenceCallback mPresenceCallback;
-    private final ChreCallback mChreCallback;
-    private ChreCommunication mChreCommunication;
-
-    private Blefilter.BleFilters mFilters = null;
-    private boolean mChreStarted = false;
-
+    final LocatorContextWrapper mLocatorContextWrapper;
+    final Locator mLocator;
     private final IntentFilter mIntentFilter;
 
+    private final ScanCallback mScanCallback =
+            new ScanCallback() {
+                @Override
+                public void onDiscovered(@NonNull NearbyDevice device) {
+                    Log.i(TAG, "[PresenceManager] discovered Device.");
+                }
+
+                @Override
+                public void onUpdated(@NonNull NearbyDevice device) {}
+
+                @Override
+                public void onLost(@NonNull NearbyDevice device) {}
+            };
+
     private final BroadcastReceiver mScreenBroadcastReceiver =
             new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
+                    NearbyManager manager = getNearbyManager();
+                    if (manager == null) {
+                        Log.e(TAG, "Nearby Manager is null");
+                        return;
+                    }
                     if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
-                        // TODO(b/221082271): removed this faked data once hooked with API codes.
-                        Log.d(TAG, "Update Presence CHRE filter");
-                        ByteString mac_addr = ByteString.copyFrom(new byte[] {1, 2, 3, 4, 5, 6});
-                        Blefilter.BleFilter filter =
-                                Blefilter.BleFilter.newBuilder()
-                                        .setId(0)
-                                        .setUuid(0xFCF1)
-                                        .setIntent(1)
-                                        .setMacAddress(mac_addr)
+                        Log.d(TAG, "Start CHRE scan.");
+                        byte[] secreteId = {1, 0, 0, 0};
+                        byte[] authenticityKey = {2, 0, 0, 0};
+                        byte[] publicKey = {3, 0, 0, 0};
+                        byte[] encryptedMetaData = {4, 0, 0, 0};
+                        byte[] encryptedMetaDataTag = {5, 0, 0, 0};
+                        PublicCredential publicCredential =
+                                new PublicCredential.Builder(
+                                                secreteId,
+                                                authenticityKey,
+                                                publicKey,
+                                                encryptedMetaData,
+                                                encryptedMetaDataTag)
                                         .build();
-                        Blefilter.BleFilters filters =
-                                Blefilter.BleFilters.newBuilder().addFilter(filter).build();
-                        updateFilters(filters);
+                        PresenceScanFilter presenceScanFilter =
+                                new PresenceScanFilter.Builder()
+                                        .setMaxPathLoss(3)
+                                        .addCredential(publicCredential)
+                                        .addPresenceAction(1)
+                                        .build();
+                        ScanRequest scanRequest =
+                                new ScanRequest.Builder()
+                                        .setScanType(ScanRequest.SCAN_TYPE_NEARBY_PRESENCE)
+                                        .addScanFilter(presenceScanFilter)
+                                        .build();
+                        Log.i(
+                                TAG,
+                                String.format(
+                                        Locale.getDefault(),
+                                        "[PresenceManager] Start Presence scan with request: %s",
+                                        scanRequest.toString()));
+                        manager.startScan(
+                                scanRequest, Executors.newSingleThreadExecutor(), mScanCallback);
+                    } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+                        Log.d(TAG, "Stop CHRE scan.");
+                        manager.stopScan(mScanCallback);
                     }
                 }
             };
 
-    public PresenceManager(Context context, PresenceCallback presenceCallback) {
-        mContext = context;
-        mPresenceCallback = presenceCallback;
-        mChreCallback = new ChreCallback();
+    public PresenceManager(LocatorContextWrapper contextWrapper) {
+        mLocatorContextWrapper = contextWrapper;
+        mLocator = mLocatorContextWrapper.getLocator();
         mIntentFilter = new IntentFilter();
     }
 
+    /** Null when the Nearby Service is not available. */
+    @Nullable
+    private NearbyManager getNearbyManager() {
+        return (NearbyManager)
+                mLocatorContextWrapper
+                        .getApplicationContext()
+                        .getSystemService(Context.NEARBY_SERVICE);
+    }
+
     /** Function called when nearby service start. */
-    public void initiate(ChreCommunication chreCommunication) {
-        mChreCommunication = chreCommunication;
-        mChreCommunication.start(mChreCallback, Collections.singleton(NANOAPP_ID));
-
+    public void initiate() {
         mIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
-        mContext.registerReceiver(mScreenBroadcastReceiver, mIntentFilter);
-    }
-
-    /** Updates the fitlers in Context Hub. */
-    public synchronized void updateFilters(Blefilter.BleFilters filters) {
-        mFilters = filters;
-        if (mChreStarted) {
-            sendFilters(mFilters);
-            mFilters = null;
-        }
-    }
-
-    private void sendFilters(Blefilter.BleFilters filters) {
-        NanoAppMessage message =
-                NanoAppMessage.createMessageToNanoApp(
-                        NANOAPP_ID, NANOAPP_MESSAGE_TYPE_FILTER, filters.toByteArray());
-        if (!mChreCommunication.sendMessageToNanoApp(message)) {
-            Log.e(TAG, "Failed to send filters to CHRE.");
-        }
-    }
-
-    private class ChreCallback implements ChreCommunication.ContextHubCommsCallback {
-
-        @Override
-        public void started(boolean success) {
-            if (success) {
-                synchronized (PresenceManager.this) {
-                    Log.i(TAG, "CHRE communication started");
-                    mChreStarted = true;
-                    if (mFilters != null) {
-                        sendFilters(mFilters);
-                        mFilters = null;
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void onHubReset() {
-            // TODO(b/221082271): hooked with upper level codes.
-            Log.i(TAG, "CHRE reset.");
-        }
-
-        @Override
-        public void onNanoAppRestart(long nanoAppId) {
-            // TODO(b/221082271): hooked with upper level codes.
-            Log.i(TAG, String.format("CHRE NanoApp %d restart.", nanoAppId));
-        }
-
-        @Override
-        public void onMessageFromNanoApp(NanoAppMessage message) {
-            if (message.getNanoAppId() != NANOAPP_ID) {
-                Log.e(TAG, "Received message from unknown nano app.");
-                return;
-            }
-            if (message.getMessageType() == NANOAPP_MESSAGE_TYPE_FILTER_RESULT) {
-                try {
-                    Blefilter.BleFilterResults results =
-                            Blefilter.BleFilterResults.parseFrom(message.getMessageBody());
-                    mPresenceCallback.onFilterResults(results);
-                } catch (InvalidProtocolBufferException e) {
-                    Log.e(
-                            TAG,
-                            String.format("Failed to decode the filter result %s", e.toString()));
-                }
-            }
-        }
+        mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+        mLocatorContextWrapper
+                .getContext()
+                .registerReceiver(mScreenBroadcastReceiver, mIntentFilter);
     }
 }
diff --git a/nearby/service/java/com/android/server/nearby/provider/AbstractDiscoveryProvider.java b/nearby/service/java/com/android/server/nearby/provider/AbstractDiscoveryProvider.java
index 7cc859c..f136695 100644
--- a/nearby/service/java/com/android/server/nearby/provider/AbstractDiscoveryProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/AbstractDiscoveryProvider.java
@@ -20,11 +20,13 @@
 
 import android.content.Context;
 import android.nearby.NearbyDeviceParcelable;
+import android.nearby.ScanFilter;
 import android.nearby.ScanRequest;
 import android.util.Log;
 
 import androidx.annotation.Nullable;
 
+import java.util.List;
 import java.util.concurrent.Executor;
 
 /**
@@ -38,10 +40,9 @@
     protected final DiscoveryProviderController mController;
     protected final Executor mExecutor;
     protected Listener mListener;
+    protected List<ScanFilter> mScanFilters;
 
-    /**
-     * Interface for listening to discovery providers.
-     */
+    /** Interface for listening to discovery providers. */
     public interface Listener {
         /**
          * Called when a provider has a new nearby device available. May be invoked from any thread.
@@ -60,20 +61,20 @@
      * can now be expected. Always implies that the provider request is set to the empty request.
      * Always invoked on the provider executor.
      */
-    protected void onStart() { }
+    protected void onStart() {}
 
     /**
      * Callback invoked when the provider is stopped, and signals that no further callback
      * invocations will occur (until a further call to {@link #onStart()}. Always invoked on the
      * provider executor.
      */
-    protected void onStop() { }
+    protected void onStop() {}
 
     /**
      * Callback invoked to inform the provider of a new provider request which replaces any prior
      * provider request. Always invoked on the provider executor.
      */
-    protected void invalidateScanMode() { }
+    protected void invalidateScanMode() {}
 
     /**
      * Retrieves the controller for this discovery provider. Should never be invoked by subclasses,
@@ -134,5 +135,10 @@
         public int getProviderScanMode() {
             return mScanMode;
         }
+
+        @Override
+        public void setProviderScanFilters(List<ScanFilter> filters) {
+            mScanFilters = filters;
+        }
     }
 }
diff --git a/nearby/service/java/com/android/server/nearby/presence/ChreCommunication.java b/nearby/service/java/com/android/server/nearby/provider/ChreCommunication.java
similarity index 91%
rename from nearby/service/java/com/android/server/nearby/presence/ChreCommunication.java
rename to nearby/service/java/com/android/server/nearby/provider/ChreCommunication.java
index fc9863e..607c579 100644
--- a/nearby/service/java/com/android/server/nearby/presence/ChreCommunication.java
+++ b/nearby/service/java/com/android/server/nearby/provider/ChreCommunication.java
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.nearby.presence;
+package com.android.server.nearby.provider;
+
+import static com.android.server.nearby.NearbyService.TAG;
 
 import android.annotation.Nullable;
 import android.hardware.location.ContextHubClient;
@@ -60,8 +62,7 @@
         void onMessageFromNanoApp(NanoAppMessage message);
     }
 
-    public static final String TAG = "PresenceService";
-    @Nullable private final ContextHubManagerAdapter mManager;
+    private final Injector mInjector;
     private final Executor mExecutor;
 
     private boolean mStarted = false;
@@ -69,10 +70,14 @@
     @Nullable private ContextHubClient mContextHubClient;
 
     public ChreCommunication(Injector injector, Executor executor) {
-        this.mManager = injector.getContextHubManagerAdapter();
+        mInjector = injector;
         mExecutor = executor;
     }
 
+    public boolean available() {
+        return mInjector.getContextHubManagerAdapter() != null;
+    }
+
     /**
      * Starts communication with the contexthub. This will invoke {@link
      * ContextHubCommsCallback#start(boolean)} on completion.
@@ -81,18 +86,17 @@
      *     contexthub.
      */
     public synchronized void start(ContextHubCommsCallback callback, Set<Long> nanoAppIds) {
-        if (this.mManager == null) {
+        ContextHubManagerAdapter manager = mInjector.getContextHubManagerAdapter();
+        if (manager == null) {
             Log.e(TAG, "ContexHub not available in this device");
             return;
         } else {
             Log.i(TAG, "Start ChreCommunication");
         }
         Preconditions.checkNotNull(callback);
-        if (nanoAppIds.isEmpty() || mManager == null) {
-            callback.started(false);
-            return;
-        }
+        Preconditions.checkArgument(!nanoAppIds.isEmpty());
         if (mStarted) {
+            Log.i(TAG, "ChreCommunication already started");
             this.mCallback.started(true);
             return;
         }
@@ -102,17 +106,18 @@
         mStarted = true;
         this.mCallback = callback;
 
-        List<ContextHubInfo> contextHubs = mManager.getContextHubs();
+        List<ContextHubInfo> contextHubs = manager.getContextHubs();
 
         // Make a copy of the list so we can modify it during our async callbacks (in case the code
         // is still iterating)
         List<ContextHubInfo> validContextHubs = new ArrayList<>(contextHubs);
 
         for (ContextHubInfo info : contextHubs) {
-            ContextHubTransaction<List<NanoAppState>> transaction = mManager.queryNanoApps(info);
+            ContextHubTransaction<List<NanoAppState>> transaction = manager.queryNanoApps(info);
             Log.i(TAG, "After query Nano Apps ");
             transaction.setOnCompleteListener(
-                    new OnQueryCompleteListener(info, validContextHubs, nanoAppIds), mExecutor);
+                    new OnQueryCompleteListener(info, validContextHubs, nanoAppIds, manager),
+                    mExecutor);
         }
     }
 
@@ -202,14 +207,17 @@
         private final ContextHubInfo mQueriedContextHub;
         private final List<ContextHubInfo> mContextHubs;
         private final Set<Long> mNanoAppIds;
+        private final ContextHubManagerAdapter mManager;
 
         OnQueryCompleteListener(
                 ContextHubInfo queriedContextHub,
                 List<ContextHubInfo> contextHubs,
-                Set<Long> nanoAppIds) {
+                Set<Long> nanoAppIds,
+                ContextHubManagerAdapter manager) {
             this.mQueriedContextHub = queriedContextHub;
             this.mContextHubs = contextHubs;
             this.mNanoAppIds = nanoAppIds;
+            this.mManager = manager;
         }
 
         @Override
diff --git a/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java b/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java
new file mode 100644
index 0000000..a70ef13
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java
@@ -0,0 +1,194 @@
+/*
+ * 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.nearby.ScanRequest.SCAN_TYPE_NEARBY_PRESENCE;
+
+import static com.android.server.nearby.NearbyService.TAG;
+
+import android.content.Context;
+import android.hardware.location.NanoAppMessage;
+import android.nearby.NearbyDevice;
+import android.nearby.NearbyDeviceParcelable;
+import android.nearby.PresenceScanFilter;
+import android.nearby.PublicCredential;
+import android.nearby.ScanFilter;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import service.proto.Blefilter;
+
+import java.util.Collections;
+import java.util.concurrent.Executor;
+
+/** Discovery provider that uses CHRE Nearby Nanoapp to do scanning. */
+public class ChreDiscoveryProvider extends AbstractDiscoveryProvider {
+    // Nanoapp ID reserved for Nearby Presence.
+    /** @hide */
+    @VisibleForTesting public static final long NANOAPP_ID = 0x476f6f676c001031L;
+    /** @hide */
+    @VisibleForTesting public static final int NANOAPP_MESSAGE_TYPE_FILTER = 3;
+    /** @hide */
+    @VisibleForTesting public static final int NANOAPP_MESSAGE_TYPE_FILTER_RESULT = 4;
+
+    private static final int PRESENCE_UUID = 0xFCF1;
+
+    private ChreCommunication mChreCommunication;
+    private ChreCallback mChreCallback;
+    private boolean mChreStarted = false;
+    private Blefilter.BleFilters mFilters = null;
+    private int mFilterId;
+
+    public ChreDiscoveryProvider(
+            Context context, ChreCommunication chreCommunication, Executor executor) {
+        super(context, executor);
+        mChreCommunication = chreCommunication;
+        mChreCallback = new ChreCallback();
+        mFilterId = 0;
+    }
+
+    @Override
+    protected void onStart() {
+        Log.d(TAG, "Start CHRE scan");
+        mChreCommunication.start(mChreCallback, Collections.singleton(NANOAPP_ID));
+        updateFilters();
+    }
+
+    @Override
+    protected void onStop() {
+        mChreStarted = false;
+        mChreCommunication.stop();
+    }
+
+    @Override
+    protected void invalidateScanMode() {
+        onStop();
+        onStart();
+    }
+
+    public boolean available() {
+        return mChreCommunication.available();
+    }
+
+    private synchronized void updateFilters() {
+        if (mScanFilters == null) {
+            Log.e(TAG, "ScanFilters not set.");
+            return;
+        }
+        Blefilter.BleFilters.Builder filtersBuilder = Blefilter.BleFilters.newBuilder();
+        for (ScanFilter scanFilter : mScanFilters) {
+            PresenceScanFilter presenceScanFilter = (PresenceScanFilter) scanFilter;
+            Blefilter.BleFilter filter =
+                    Blefilter.BleFilter.newBuilder()
+                            .setId(mFilterId)
+                            .setUuid(PRESENCE_UUID)
+                            .setIntent(presenceScanFilter.getPresenceActions().get(0))
+                            .build();
+            filtersBuilder.addFilter(filter);
+            mFilterId++;
+        }
+        mFilters = filtersBuilder.build();
+        if (mChreStarted) {
+            sendFilters(mFilters);
+            mFilters = null;
+        }
+    }
+
+    private void sendFilters(Blefilter.BleFilters filters) {
+        NanoAppMessage message =
+                NanoAppMessage.createMessageToNanoApp(
+                        NANOAPP_ID, NANOAPP_MESSAGE_TYPE_FILTER, filters.toByteArray());
+        if (!mChreCommunication.sendMessageToNanoApp(message)) {
+            Log.e(TAG, "Failed to send filters to CHRE.");
+        }
+    }
+
+    private class ChreCallback implements ChreCommunication.ContextHubCommsCallback {
+
+        @Override
+        public void started(boolean success) {
+            if (success) {
+                synchronized (ChreDiscoveryProvider.this) {
+                    Log.i(TAG, "CHRE communication started");
+                    mChreStarted = true;
+                    if (mFilters != null) {
+                        sendFilters(mFilters);
+                        mFilters = null;
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onHubReset() {
+            // TODO(b/221082271): hooked with upper level codes.
+            Log.i(TAG, "CHRE reset.");
+        }
+
+        @Override
+        public void onNanoAppRestart(long nanoAppId) {
+            // TODO(b/221082271): hooked with upper level codes.
+            Log.i(TAG, String.format("CHRE NanoApp %d restart.", nanoAppId));
+        }
+
+        @Override
+        public void onMessageFromNanoApp(NanoAppMessage message) {
+            if (message.getNanoAppId() != NANOAPP_ID) {
+                Log.e(TAG, "Received message from unknown nano app.");
+                return;
+            }
+            if (mListener == null) {
+                Log.e(TAG, "the listener is not set in ChreDiscoveryProvider.");
+                return;
+            }
+            if (message.getMessageType() == NANOAPP_MESSAGE_TYPE_FILTER_RESULT) {
+                try {
+                    Blefilter.BleFilterResults results =
+                            Blefilter.BleFilterResults.parseFrom(message.getMessageBody());
+                    for (Blefilter.BleFilterResult filterResult : results.getResultList()) {
+                        Blefilter.PublicCredential credential = filterResult.getPublicCredential();
+                        PublicCredential publicCredential =
+                                new PublicCredential.Builder(
+                                                credential.getSecretId().toByteArray(),
+                                                credential.getAuthenticityKey().toByteArray(),
+                                                credential.getPublicKey().toByteArray(),
+                                                credential.getEncryptedMetadata().toByteArray(),
+                                                credential.getEncryptedMetadataTag().toByteArray())
+                                        .build();
+                        NearbyDeviceParcelable device =
+                                new NearbyDeviceParcelable.Builder()
+                                        .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+                                        .setMedium(NearbyDevice.Medium.BLE)
+                                        .setTxPower(filterResult.getTxPower())
+                                        .setRssi(filterResult.getRssi())
+                                        .setAction(filterResult.getIntent())
+                                        .setPublicCredential(publicCredential)
+                                        .build();
+                        mExecutor.execute(() -> mListener.onNearbyDeviceDiscovered(device));
+                    }
+                } catch (InvalidProtocolBufferException e) {
+                    Log.e(
+                            TAG,
+                            String.format("Failed to decode the filter result %s", e.toString()));
+                }
+            }
+        }
+    }
+}
diff --git a/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderController.java b/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderController.java
index 469f623..fa1a874 100644
--- a/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderController.java
+++ b/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderController.java
@@ -17,42 +17,43 @@
 package com.android.server.nearby.provider;
 
 import android.annotation.Nullable;
+import android.nearby.ScanFilter;
 import android.nearby.ScanRequest;
 
-/**
- * Interface for controlling discovery providers.
- */
+import java.util.List;
+
+/** Interface for controlling discovery providers. */
 interface DiscoveryProviderController {
 
     /**
-     * Sets the listener which can expect to receive all state updates from after this point.
-     * May be invoked at any time.
+     * Sets the listener which can expect to receive all state updates from after this point. May be
+     * invoked at any time.
      */
     void setListener(@Nullable AbstractDiscoveryProvider.Listener listener);
 
-    /**
-     * Returns true if in the started state.
-     */
+    /** Returns true if in the started state. */
     boolean isStarted();
 
     /**
-     * Starts the discovery provider. Must be invoked before any other method (except
-     * {@link #setListener(AbstractDiscoveryProvider.Listener)} (Listener)}).
+     * Starts the discovery provider. Must be invoked before any other method (except {@link
+     * #setListener(AbstractDiscoveryProvider.Listener)} (Listener)}).
      */
     void start();
 
     /**
      * Stops the discovery provider. No other methods may be invoked after this method (except
-     * {@link #setListener(AbstractDiscoveryProvider.Listener)} (Listener)}), until {@link #start()} is called again.
+     * {@link #setListener(AbstractDiscoveryProvider.Listener)} (Listener)}), until {@link #start()}
+     * is called again.
      */
     void stop();
 
-    /**
-     * Sets the desired scan mode.
-     */
+    /** Sets the desired scan mode. */
     void setProviderScanMode(@ScanRequest.ScanMode int scanMode);
 
     /** Gets the controller scan mode. */
     @ScanRequest.ScanMode
     int getProviderScanMode();
+
+    /** Sets the scan filters. */
+    void setProviderScanFilters(List<ScanFilter> filters);
 }
diff --git a/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java b/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java
index 7ff3110..af1c0b5 100644
--- a/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java
+++ b/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java
@@ -20,6 +20,7 @@
 
 import static com.android.server.nearby.NearbyService.TAG;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.nearby.IScanListener;
 import android.nearby.NearbyDeviceParcelable;
@@ -35,20 +36,22 @@
 import com.android.server.nearby.metrics.NearbyMetrics;
 import com.android.server.nearby.presence.PresenceDiscoveryResult;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
 import java.util.stream.Collectors;
 
-/**
- * Manages all aspects of discovery providers.
- */
+/** Manages all aspects of discovery providers. */
 public class DiscoveryProviderManager implements AbstractDiscoveryProvider.Listener {
 
     protected final Object mLock = new Object();
     private final Context mContext;
     private final BleDiscoveryProvider mBleDiscoveryProvider;
+    @Nullable private final ChreDiscoveryProvider mChreDiscoveryProvider;
     private @ScanRequest.ScanMode int mScanMode;
 
     @GuardedBy("mLock")
@@ -65,18 +68,24 @@
                 }
                 if (nearbyDevice.getScanType() == SCAN_TYPE_NEARBY_PRESENCE) {
                     List<ScanFilter> presenceFilters =
-                            record.getScanRequest().getScanFilters().stream().filter(
-                                    scanFilter -> scanFilter.getType()
-                                            == SCAN_TYPE_NEARBY_PRESENCE).collect(
-                                    Collectors.toList());
+                            record.getScanRequest().getScanFilters().stream()
+                                    .filter(
+                                            scanFilter ->
+                                                    scanFilter.getType()
+                                                            == SCAN_TYPE_NEARBY_PRESENCE)
+                                    .collect(Collectors.toList());
+                    Log.i(
+                            TAG,
+                            String.format("match with filters size: %d", presenceFilters.size()));
                     if (!presenceFilterMatches(nearbyDevice, presenceFilters)) {
                         continue;
                     }
                 }
                 try {
-                    record.getScanListener().onDiscovered(
-                            PrivacyFilter.filter(record.getScanRequest().getScanType(),
-                                    nearbyDevice));
+                    record.getScanListener()
+                            .onDiscovered(
+                                    PrivacyFilter.filter(
+                                            record.getScanRequest().getScanType(), nearbyDevice));
                     NearbyMetrics.logScanDeviceDiscovered(
                             record.hashCode(), record.getScanRequest(), nearbyDevice);
                 } catch (RemoteException e) {
@@ -89,6 +98,10 @@
     public DiscoveryProviderManager(Context context, Injector injector) {
         mContext = context;
         mBleDiscoveryProvider = new BleDiscoveryProvider(mContext, injector);
+        Executor executor = Executors.newSingleThreadExecutor();
+        mChreDiscoveryProvider =
+                new ChreDiscoveryProvider(
+                        mContext, new ChreCommunication(injector, executor), executor);
         mScanTypeScanListenerRecordMap = new HashMap<>();
     }
 
@@ -96,23 +109,24 @@
      * Registers the listener in the manager and starts scan according to the requested scan mode.
      */
     public boolean registerScanListener(ScanRequest scanRequest, IScanListener listener) {
+        Log.i(TAG, "DiscoveryProviderManager registerScanListener");
         synchronized (mLock) {
             IBinder listenerBinder = listener.asBinder();
             if (mScanTypeScanListenerRecordMap.containsKey(listener.asBinder())) {
-                ScanRequest savedScanRequest = mScanTypeScanListenerRecordMap
-                        .get(listenerBinder).getScanRequest();
+                ScanRequest savedScanRequest =
+                        mScanTypeScanListenerRecordMap.get(listenerBinder).getScanRequest();
                 if (scanRequest.equals(savedScanRequest)) {
                     Log.d(TAG, "Already registered the scanRequest: " + scanRequest);
                     return true;
                 }
             }
+            ScanListenerRecord scanListenerRecord = new ScanListenerRecord(scanRequest, listener);
+            mScanTypeScanListenerRecordMap.put(listenerBinder, scanListenerRecord);
 
             if (!startProviders(scanRequest)) {
                 return false;
             }
 
-            ScanListenerRecord scanListenerRecord = new ScanListenerRecord(scanRequest, listener);
-            mScanTypeScanListenerRecordMap.put(listenerBinder, scanListenerRecord);
             NearbyMetrics.logScanStarted(scanListenerRecord.hashCode(), scanRequest);
             if (mScanMode < scanRequest.getScanMode()) {
                 mScanMode = scanRequest.getScanMode();
@@ -129,21 +143,23 @@
         IBinder listenerBinder = listener.asBinder();
         synchronized (mLock) {
             if (!mScanTypeScanListenerRecordMap.containsKey(listenerBinder)) {
-                Log.w(TAG,
+                Log.w(
+                        TAG,
                         "Cannot unregister the scanRequest because the request is never "
                                 + "registered.");
                 return;
             }
 
-            ScanListenerRecord removedRecord = mScanTypeScanListenerRecordMap
-                    .remove(listenerBinder);
-            NearbyMetrics.logScanStopped(
-                    removedRecord.hashCode(), removedRecord.getScanRequest());
+            ScanListenerRecord removedRecord =
+                    mScanTypeScanListenerRecordMap.remove(listenerBinder);
+            NearbyMetrics.logScanStopped(removedRecord.hashCode(), removedRecord.getScanRequest());
             if (mScanTypeScanListenerRecordMap.isEmpty()) {
                 stopProviders();
                 return;
             }
 
+            // TODO(b/221082271): updates the scan with reduced filters.
+
             // Removes current highest scan mode requested and sets the next highest scan mode.
             if (removedRecord.getScanRequest().getScanMode() == mScanMode) {
                 @ScanRequest.ScanMode int highestScanModeRequested = ScanRequest.SCAN_MODE_NO_POWER;
@@ -166,8 +182,13 @@
     // starts successfully.
     private boolean startProviders(ScanRequest scanRequest) {
         if (scanRequest.isBleEnabled()) {
-            startBleProvider(scanRequest);
-            return true;
+            if (mChreDiscoveryProvider.available()) {
+                startChreProvider();
+                return true;
+            } else {
+                startBleProvider(scanRequest);
+                return true;
+            }
         }
         return false;
     }
@@ -181,31 +202,57 @@
         }
     }
 
+    private void startChreProvider() {
+        Log.d(TAG, "DiscoveryProviderManager starts CHRE scanning.");
+        synchronized (mLock) {
+            mChreDiscoveryProvider.getController().setListener(this);
+            List<ScanFilter> scanFilters = new ArrayList();
+            for (IBinder listenerBinder : mScanTypeScanListenerRecordMap.keySet()) {
+                ScanListenerRecord record = mScanTypeScanListenerRecordMap.get(listenerBinder);
+                List<ScanFilter> presenceFilters =
+                        record.getScanRequest().getScanFilters().stream()
+                                .filter(
+                                        scanFilter ->
+                                                scanFilter.getType() == SCAN_TYPE_NEARBY_PRESENCE)
+                                .collect(Collectors.toList());
+                scanFilters.addAll(presenceFilters);
+            }
+            mChreDiscoveryProvider.getController().setProviderScanFilters(scanFilters);
+            mChreDiscoveryProvider.getController().setProviderScanMode(mScanMode);
+            mChreDiscoveryProvider.getController().start();
+        }
+    }
+
     private void stopProviders() {
         stopBleProvider();
+        stopChreProvider();
     }
 
     private void stopBleProvider() {
         mBleDiscoveryProvider.getController().stop();
     }
 
-    private void invalidateProviderScanMode() {
-        if (!mBleDiscoveryProvider.getController().isStarted()) {
-            Log.d(TAG,
-                    "Skip invalidating BleDiscoveryProvider scan mode because the provider not "
-                            + "started.");
-            return;
-        }
-        mBleDiscoveryProvider.getController().setProviderScanMode(mScanMode);
+    private void stopChreProvider() {
+        mChreDiscoveryProvider.getController().stop();
     }
 
-    private static boolean presenceFilterMatches(NearbyDeviceParcelable device,
-            List<ScanFilter> scanFilters) {
+    private void invalidateProviderScanMode() {
+        if (mBleDiscoveryProvider.getController().isStarted()) {
+            mBleDiscoveryProvider.getController().setProviderScanMode(mScanMode);
+        } else {
+            Log.d(
+                    TAG,
+                    "Skip invalidating BleDiscoveryProvider scan mode because the provider not "
+                            + "started.");
+        }
+    }
+
+    private static boolean presenceFilterMatches(
+            NearbyDeviceParcelable device, List<ScanFilter> scanFilters) {
         if (scanFilters.isEmpty()) {
             return true;
         }
-        PresenceDiscoveryResult discoveryResult = PresenceDiscoveryResult.fromScanData(
-                device.getData(), device.getRssi());
+        PresenceDiscoveryResult discoveryResult = PresenceDiscoveryResult.fromDevice(device);
         for (ScanFilter scanFilter : scanFilters) {
             PresenceScanFilter presenceScanFilter = (PresenceScanFilter) scanFilter;
             if (discoveryResult.matches(presenceScanFilter)) {
@@ -221,7 +268,6 @@
 
         private final IScanListener mScanListener;
 
-
         ScanListenerRecord(ScanRequest scanRequest, IScanListener iScanListener) {
             mScanListener = iScanListener;
             mScanRequest = scanRequest;
diff --git a/nearby/service/proto/src/presence/blefilter.proto b/nearby/service/proto/src/presence/blefilter.proto
index da56522..9f75d34 100644
--- a/nearby/service/proto/src/presence/blefilter.proto
+++ b/nearby/service/proto/src/presence/blefilter.proto
@@ -47,6 +47,14 @@
   optional bytes metadata_encryption_key_tag = 2;
 }
 
+message PublicCredential {
+  optional bytes secret_id = 1;
+  optional bytes authenticity_key = 2;
+  optional bytes public_key = 3;
+  optional bytes encrypted_metadata = 4;
+  optional bytes encrypted_metadata_tag = 5;
+}
+
 message BleFilter {
   optional uint32 id = 1;  // Required, unique id of this filter.
   // Maximum delay to notify the client after an event occurs.
@@ -73,8 +81,11 @@
 // FilterResult is returned to host when a BLE event matches a Filter.
 message BleFilterResult {
   optional uint32 id = 1;  // id of the matched Filter.
-  // TODO(b/193756395): replace with BLE event proto.
-  optional bytes raw_data = 2;
+  optional uint32 tx_power = 2;
+  optional uint32 rssi = 3;
+  optional uint32 intent = 4;
+  optional bytes bluetooth_address = 5;
+  optional PublicCredential public_credential = 6;
 }
 
 message BleFilterResults {
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java
index 87fc4cd..b9ab95f 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java
@@ -20,6 +20,7 @@
 
 import android.nearby.NearbyDevice;
 import android.nearby.NearbyDeviceParcelable;
+import android.nearby.PublicCredential;
 import android.os.Build;
 import android.os.Parcel;
 
@@ -46,37 +47,50 @@
 
     @Before
     public void setUp() {
-        mBuilder =  new NearbyDeviceParcelable.Builder()
-                .setName("testDevice")
-                .setMedium(NearbyDevice.Medium.BLE)
-                .setRssi(RSSI)
-                .setTxPower(-90)
-                .setFastPairModelId(FAST_PAIR_MODEL_ID)
-                .setBluetoothAddress(BLUETOOTH_ADDRESS)
-                .setData(SCAN_DATA);
+        mBuilder =
+                new NearbyDeviceParcelable.Builder()
+                        .setName("testDevice")
+                        .setMedium(NearbyDevice.Medium.BLE)
+                        .setRssi(RSSI)
+                        .setFastPairModelId(FAST_PAIR_MODEL_ID)
+                        .setBluetoothAddress(BLUETOOTH_ADDRESS)
+                        .setData(SCAN_DATA);
     }
 
-
     /** Verify toString returns expected string. */
     @Test
-    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    @SdkSuppress(minSdkVersion = 33, codeName = "T")
     public void testToString() {
+        PublicCredential publicCredential =
+                new PublicCredential.Builder(
+                                new byte[] {1},
+                                new byte[] {2},
+                                new byte[] {3},
+                                new byte[] {4},
+                                new byte[] {5})
+                        .build();
         NearbyDeviceParcelable nearbyDeviceParcelable =
-                mBuilder.setFastPairModelId(null).setData(null).build();
+                mBuilder.setFastPairModelId(null)
+                        .setData(null)
+                        .setPublicCredential(publicCredential)
+                        .build();
 
-        assertThat(nearbyDeviceParcelable.toString()).isEqualTo(
-                "NearbyDeviceParcelable[name=testDevice, medium=BLE, rssi=-60, txPower=-90, "
-                        + "bluetoothAddress="
-                        + BLUETOOTH_ADDRESS + ", fastPairModelId=null, data=null]");
+        assertThat(nearbyDeviceParcelable.toString())
+                .isEqualTo(
+                        "NearbyDeviceParcelable[name=testDevice, medium=BLE, txPower=0, rssi=-60,"
+                                + " action=0, bluetoothAddress="
+                                + BLUETOOTH_ADDRESS
+                                + ", fastPairModelId=null, data=null]");
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    @SdkSuppress(minSdkVersion = 33, codeName = "T")
     public void test_defaultNullFields() {
-        NearbyDeviceParcelable nearbyDeviceParcelable = new NearbyDeviceParcelable.Builder()
-                .setMedium(NearbyDevice.Medium.BLE)
-                .setRssi(RSSI)
-                .build();
+        NearbyDeviceParcelable nearbyDeviceParcelable =
+                new NearbyDeviceParcelable.Builder()
+                        .setMedium(NearbyDevice.Medium.BLE)
+                        .setRssi(RSSI)
+                        .build();
 
         assertThat(nearbyDeviceParcelable.getName()).isNull();
         assertThat(nearbyDeviceParcelable.getFastPairModelId()).isNull();
@@ -88,15 +102,15 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    @SdkSuppress(minSdkVersion = 33, codeName = "T")
     public void testWriteParcel() {
-        NearbyDeviceParcelable nearbyDeviceParcelable =  mBuilder.build();
+        NearbyDeviceParcelable nearbyDeviceParcelable = mBuilder.build();
 
         Parcel parcel = Parcel.obtain();
         nearbyDeviceParcelable.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
-        NearbyDeviceParcelable actualNearbyDevice = NearbyDeviceParcelable.CREATOR.createFromParcel(
-                parcel);
+        NearbyDeviceParcelable actualNearbyDevice =
+                NearbyDeviceParcelable.CREATOR.createFromParcel(parcel);
         parcel.recycle();
 
         assertThat(actualNearbyDevice.getRssi()).isEqualTo(RSSI);
@@ -106,32 +120,30 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    @SdkSuppress(minSdkVersion = 33, codeName = "T")
     public void testWriteParcel_nullModelId() {
-        NearbyDeviceParcelable nearbyDeviceParcelable =
-                mBuilder.setFastPairModelId(null).build();
+        NearbyDeviceParcelable nearbyDeviceParcelable = mBuilder.setFastPairModelId(null).build();
 
         Parcel parcel = Parcel.obtain();
         nearbyDeviceParcelable.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
-        NearbyDeviceParcelable actualNearbyDevice = NearbyDeviceParcelable.CREATOR.createFromParcel(
-                parcel);
+        NearbyDeviceParcelable actualNearbyDevice =
+                NearbyDeviceParcelable.CREATOR.createFromParcel(parcel);
         parcel.recycle();
 
         assertThat(actualNearbyDevice.getFastPairModelId()).isNull();
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    @SdkSuppress(minSdkVersion = 33, codeName = "T")
     public void testWriteParcel_nullBluetoothAddress() {
-        NearbyDeviceParcelable nearbyDeviceParcelable =
-                mBuilder.setBluetoothAddress(null).build();
+        NearbyDeviceParcelable nearbyDeviceParcelable = mBuilder.setBluetoothAddress(null).build();
 
         Parcel parcel = Parcel.obtain();
         nearbyDeviceParcelable.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
-        NearbyDeviceParcelable actualNearbyDevice = NearbyDeviceParcelable.CREATOR.createFromParcel(
-                parcel);
+        NearbyDeviceParcelable actualNearbyDevice =
+                NearbyDeviceParcelable.CREATOR.createFromParcel(parcel);
         parcel.recycle();
 
         assertThat(actualNearbyDevice.getBluetoothAddress()).isNull();
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 10b3cf2..f05f65f 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/PrivateCredentialTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/PrivateCredentialTest.java
@@ -60,7 +60,7 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    @SdkSuppress(minSdkVersion = 33, codeName = "T")
     public void testBuilder() {
         PrivateCredential credential = mBuilder.build();
 
@@ -77,7 +77,7 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    @SdkSuppress(minSdkVersion = 33, codeName = "T")
     public void testWriteParcel() {
         PrivateCredential credential = mBuilder.build();
 
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 b4a34cc..11bbacc 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/PublicCredentialTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/PublicCredentialTest.java
@@ -37,29 +37,32 @@
 
 import java.util.Arrays;
 
-/**
- * Tests for {@link PresenceCredential}.
- */
+/** Tests for {@link PresenceCredential}. */
 @RunWith(AndroidJUnit4.class)
 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
 public class PublicCredentialTest {
 
-    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 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 String KEY = "KEY";
-    private static final byte[] VALUE = new byte[]{1, 2, 3, 4, 5};
+    private static final byte[] VALUE = new byte[] {1, 2, 3, 4, 5};
 
     private PublicCredential.Builder mBuilder;
 
     @Before
     public void setUp() {
-        mBuilder = new PublicCredential.Builder(SECRETE_ID, AUTHENTICITY_KEY, PUBLIC_KEY,
-                ENCRYPTED_METADATA, METADATA_ENCRYPTION_KEY_TAG)
-                .addCredentialElement(new CredentialElement(KEY, VALUE))
-                .setIdentityType(IDENTITY_TYPE_PRIVATE);
+        mBuilder =
+                new PublicCredential.Builder(
+                                SECRETE_ID,
+                                AUTHENTICITY_KEY,
+                                PUBLIC_KEY,
+                                ENCRYPTED_METADATA,
+                                METADATA_ENCRYPTION_KEY_TAG)
+                        .addCredentialElement(new CredentialElement(KEY, VALUE))
+                        .setIdentityType(IDENTITY_TYPE_PRIVATE);
     }
 
     @Test
@@ -74,8 +77,11 @@
         assertThat(Arrays.equals(credential.getAuthenticityKey(), AUTHENTICITY_KEY)).isTrue();
         assertThat(Arrays.equals(credential.getPublicKey(), PUBLIC_KEY)).isTrue();
         assertThat(Arrays.equals(credential.getEncryptedMetadata(), ENCRYPTED_METADATA)).isTrue();
-        assertThat(Arrays.equals(credential.getEncryptedMetadataKeyTag(),
-                METADATA_ENCRYPTION_KEY_TAG)).isTrue();
+        assertThat(
+                        Arrays.equals(
+                                credential.getEncryptedMetadataKeyTag(),
+                                METADATA_ENCRYPTION_KEY_TAG))
+                .isTrue();
     }
 
     @Test
@@ -86,19 +92,73 @@
         Parcel parcel = Parcel.obtain();
         credential.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
-        PublicCredential credentialFromParcel = PublicCredential.CREATOR.createFromParcel(
-                parcel);
+        PublicCredential credentialFromParcel = PublicCredential.CREATOR.createFromParcel(parcel);
         parcel.recycle();
 
         assertThat(credentialFromParcel.getType()).isEqualTo(CREDENTIAL_TYPE_PUBLIC);
         assertThat(credentialFromParcel.getIdentityType()).isEqualTo(IDENTITY_TYPE_PRIVATE);
         assertThat(Arrays.equals(credentialFromParcel.getSecretId(), SECRETE_ID)).isTrue();
-        assertThat(Arrays.equals(credentialFromParcel.getAuthenticityKey(),
-                AUTHENTICITY_KEY)).isTrue();
+        assertThat(Arrays.equals(credentialFromParcel.getAuthenticityKey(), AUTHENTICITY_KEY))
+                .isTrue();
         assertThat(Arrays.equals(credentialFromParcel.getPublicKey(), PUBLIC_KEY)).isTrue();
-        assertThat(Arrays.equals(credentialFromParcel.getEncryptedMetadata(),
-                ENCRYPTED_METADATA)).isTrue();
-        assertThat(Arrays.equals(credentialFromParcel.getEncryptedMetadataKeyTag(),
-                METADATA_ENCRYPTION_KEY_TAG)).isTrue();
+        assertThat(Arrays.equals(credentialFromParcel.getEncryptedMetadata(), ENCRYPTED_METADATA))
+                .isTrue();
+        assertThat(
+                        Arrays.equals(
+                                credentialFromParcel.getEncryptedMetadataKeyTag(),
+                                METADATA_ENCRYPTION_KEY_TAG))
+                .isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testEquals() {
+        PublicCredential credentialOne =
+                new PublicCredential.Builder(
+                                SECRETE_ID,
+                                AUTHENTICITY_KEY,
+                                PUBLIC_KEY,
+                                ENCRYPTED_METADATA,
+                                METADATA_ENCRYPTION_KEY_TAG)
+                        .addCredentialElement(new CredentialElement(KEY, VALUE))
+                        .setIdentityType(IDENTITY_TYPE_PRIVATE)
+                        .build();
+
+        PublicCredential credentialTwo =
+                new PublicCredential.Builder(
+                                SECRETE_ID,
+                                AUTHENTICITY_KEY,
+                                PUBLIC_KEY,
+                                ENCRYPTED_METADATA,
+                                METADATA_ENCRYPTION_KEY_TAG)
+                        .addCredentialElement(new CredentialElement(KEY, VALUE))
+                        .setIdentityType(IDENTITY_TYPE_PRIVATE)
+                        .build();
+        assertThat(credentialOne.equals((Object) credentialTwo)).isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testUnEquals() {
+        byte[] idOne = new byte[] {1, 2, 3, 4};
+        byte[] idTwo = new byte[] {4, 5, 6, 7};
+        PublicCredential credentialOne =
+                new PublicCredential.Builder(
+                                idOne,
+                                AUTHENTICITY_KEY,
+                                PUBLIC_KEY,
+                                ENCRYPTED_METADATA,
+                                METADATA_ENCRYPTION_KEY_TAG)
+                        .build();
+
+        PublicCredential credentialTwo =
+                new PublicCredential.Builder(
+                                idTwo,
+                                AUTHENTICITY_KEY,
+                                PUBLIC_KEY,
+                                ENCRYPTED_METADATA,
+                                METADATA_ENCRYPTION_KEY_TAG)
+                        .build();
+        assertThat(credentialOne.equals((Object) credentialTwo)).isFalse();
     }
 }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/metrics/NearbyMetricsTest.java b/nearby/tests/unit/src/com/android/server/nearby/metrics/NearbyMetricsTest.java
index 252bb90..c4d1ee2 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/metrics/NearbyMetricsTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/metrics/NearbyMetricsTest.java
@@ -20,6 +20,7 @@
 import static android.nearby.ScanRequest.SCAN_TYPE_NEARBY_SHARE;
 
 import android.nearby.NearbyDeviceParcelable;
+import android.nearby.PublicCredential;
 import android.nearby.ScanRequest;
 import android.os.WorkSource;
 
@@ -42,39 +43,50 @@
     private static final int RSSI = -60;
     private static final String FAST_PAIR_MODEL_ID = "1234";
     private static final String BLUETOOTH_ADDRESS = "00:11:22:33:FF:EE";
-    private static final byte[] SCAN_DATA = new byte[]{1, 2, 3, 4};
+    private static final byte[] SCAN_DATA = new byte[] {1, 2, 3, 4};
+    private static final PublicCredential PUBLIC_CREDENTIAL =
+            new PublicCredential.Builder(
+                            new byte[] {1},
+                            new byte[] {2},
+                            new byte[] {3},
+                            new byte[] {4},
+                            new byte[] {5})
+                    .build();
 
     private final WorkSource mWorkSource = new WorkSource(WORK_SOURCE_UID);
     private final WorkSource mEmptyWorkSource = new WorkSource();
 
-    private final ScanRequest.Builder mScanRequestBuilder = new ScanRequest.Builder()
-            .setScanMode(SCAN_MODE_BALANCED)
-            .setScanType(SCAN_TYPE_NEARBY_SHARE);
-    private final ScanRequest mScanRequest = mScanRequestBuilder
-            .setWorkSource(mWorkSource)
-            .build();
-    private final ScanRequest mScanRequestWithEmptyWorkSource = mScanRequestBuilder
-            .setWorkSource(mEmptyWorkSource)
-            .build();
+    private final ScanRequest.Builder mScanRequestBuilder =
+            new ScanRequest.Builder()
+                    .setScanMode(SCAN_MODE_BALANCED)
+                    .setScanType(SCAN_TYPE_NEARBY_SHARE);
+    private final ScanRequest mScanRequest = mScanRequestBuilder.setWorkSource(mWorkSource).build();
+    private final ScanRequest mScanRequestWithEmptyWorkSource =
+            mScanRequestBuilder.setWorkSource(mEmptyWorkSource).build();
 
     private final NearbyDeviceParcelable mNearbyDevice =
             new NearbyDeviceParcelable.Builder()
                     .setName(DEVICE_NAME)
                     .setMedium(SCAN_MEDIUM)
+                    .setTxPower(1)
                     .setRssi(RSSI)
+                    .setAction(1)
+                    .setPublicCredential(PUBLIC_CREDENTIAL)
                     .setFastPairModelId(FAST_PAIR_MODEL_ID)
                     .setBluetoothAddress(BLUETOOTH_ADDRESS)
-                    .setData(SCAN_DATA).build();
+                    .setData(SCAN_DATA)
+                    .build();
 
     private MockitoSession mSession;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mSession = ExtendedMockito.mockitoSession()
-                .strictness(Strictness.LENIENT)
-                .mockStatic(NearbyStatsLog.class)
-                .startMocking();
+        mSession =
+                ExtendedMockito.mockitoSession()
+                        .strictness(Strictness.LENIENT)
+                        .mockStatic(NearbyStatsLog.class)
+                        .startMocking();
     }
 
     @After
@@ -164,8 +176,8 @@
 
     @Test
     public void testLogScanDeviceDiscovered_emptyWorkSource() {
-        NearbyMetrics.logScanDeviceDiscovered(SESSION_ID, mScanRequestWithEmptyWorkSource,
-                mNearbyDevice);
+        NearbyMetrics.logScanDeviceDiscovered(
+                SESSION_ID, mScanRequestWithEmptyWorkSource, mNearbyDevice);
         ExtendedMockito.verify(() -> NearbyStatsLog.write(
                 NearbyStatsLog.NEARBY_DEVICE_SCAN_STATE_CHANGED,
                 -1,
diff --git a/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java
index f9676e3..d32e325 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java
@@ -16,93 +16,24 @@
 
 package com.android.server.nearby.presence;
 
-import static com.android.server.nearby.presence.PresenceManager.NANOAPP_ID;
-import static com.android.server.nearby.presence.PresenceManager.NANOAPP_MESSAGE_TYPE_FILTER_RESULT;
 
-import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.hardware.location.NanoAppMessage;
 
 import androidx.test.filters.SdkSuppress;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.Collections;
-
-import service.proto.Blefilter;
-
 public class PresenceManagerTest {
-    @Mock private Context mContext;
-    @Mock private PresenceManager.PresenceCallback mPresenceCallback;
-    @Mock private ChreCommunication mChreCommunication;
-
-    @Captor ArgumentCaptor<ChreCommunication.ContextHubCommsCallback> mChreCallbackCaptor;
-    @Captor ArgumentCaptor<NanoAppMessage> mNanoAppMessageCaptor;
-
     private PresenceManager mPresenceManager;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mPresenceManager = new PresenceManager(mContext, mPresenceCallback);
     }
 
     @Test
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
-    public void testInit() {
-        mPresenceManager.initiate(mChreCommunication);
-        verify(mChreCommunication).start(any(), eq(Collections.singleton(NANOAPP_ID)));
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = 32, codeName = "T")
-    public void testOnFilterResults() {
-        Blefilter.BleFilterResults results = Blefilter.BleFilterResults.newBuilder().build();
-        NanoAppMessage chre_message =
-                NanoAppMessage.createMessageToNanoApp(
-                        NANOAPP_ID, NANOAPP_MESSAGE_TYPE_FILTER_RESULT, results.toByteArray());
-        mPresenceManager.initiate(mChreCommunication);
-        verify(mChreCommunication).start(mChreCallbackCaptor.capture(), any());
-        mChreCallbackCaptor.getValue().onMessageFromNanoApp(chre_message);
-        verify(mPresenceCallback).onFilterResults(eq(results));
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = 32, codeName = "T")
-    public void testUpdateFiltersBeforeChreStarted() {
-        Blefilter.BleFilters filters = Blefilter.BleFilters.newBuilder().build();
-        mPresenceManager.updateFilters(filters);
-        verify(mChreCommunication, never()).sendMessageToNanoApp(any());
-        mPresenceManager.initiate(mChreCommunication);
-        verify(mChreCommunication).start(mChreCallbackCaptor.capture(), any());
-        mChreCallbackCaptor.getValue().started(true);
-        verify(mChreCommunication, times(1)).sendMessageToNanoApp(mNanoAppMessageCaptor.capture());
-        assertThat(mNanoAppMessageCaptor.getValue().getMessageBody())
-                .isEqualTo(filters.toByteArray());
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = 32, codeName = "T")
-    public void testUpdateFiltersAfterChreStarted() {
-        mPresenceManager.initiate(mChreCommunication);
-        verify(mChreCommunication).start(mChreCallbackCaptor.capture(), any());
-        mChreCallbackCaptor.getValue().started(true);
-        Blefilter.BleFilters filters = Blefilter.BleFilters.newBuilder().build();
-        mPresenceManager.updateFilters(filters);
-        verify(mChreCommunication, times(1)).sendMessageToNanoApp(mNanoAppMessageCaptor.capture());
-        assertThat(mNanoAppMessageCaptor.getValue().getMessageBody())
-                .isEqualTo(filters.toByteArray());
-    }
+    public void testInit() {}
 }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/presence/ChreCommunicationTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/ChreCommunicationTest.java
similarity index 85%
rename from nearby/tests/unit/src/com/android/server/nearby/presence/ChreCommunicationTest.java
rename to nearby/tests/unit/src/com/android/server/nearby/provider/ChreCommunicationTest.java
index b221f46..1b29b52 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/presence/ChreCommunicationTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/ChreCommunicationTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.nearby.presence;
+package com.android.server.nearby.provider;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
@@ -65,10 +65,11 @@
         when(mTransactionResponse.getContents())
                 .thenReturn(
                         Collections.singletonList(
-                                new NanoAppState(PresenceManager.NANOAPP_ID, 1, true)));
+                                new NanoAppState(ChreDiscoveryProvider.NANOAPP_ID, 1, true)));
 
         mChreCommunication = new ChreCommunication(mInjector, new InlineExecutor());
-        mChreCommunication.start(mChreCallback, Collections.singleton(PresenceManager.NANOAPP_ID));
+        mChreCommunication.start(
+                mChreCallback, Collections.singleton(ChreDiscoveryProvider.NANOAPP_ID));
 
         verify(mTransaction).setOnCompleteListener(mOnQueryCompleteListenerCaptor.capture(), any());
         mOnQueryCompleteListenerCaptor.getValue().onComplete(mTransaction, mTransactionResponse);
@@ -89,8 +90,8 @@
     public void testSendMessageToNanApp() {
         NanoAppMessage message =
                 NanoAppMessage.createMessageToNanoApp(
-                        PresenceManager.NANOAPP_ID,
-                        PresenceManager.NANOAPP_MESSAGE_TYPE_FILTER,
+                        ChreDiscoveryProvider.NANOAPP_ID,
+                        ChreDiscoveryProvider.NANOAPP_MESSAGE_TYPE_FILTER,
                         new byte[] {1, 2, 3});
         mChreCommunication.sendMessageToNanoApp(message);
         verify(mClient).sendMessageToNanoApp(eq(message));
@@ -100,8 +101,8 @@
     public void testOnMessageFromNanoApp() {
         NanoAppMessage message =
                 NanoAppMessage.createMessageToNanoApp(
-                        PresenceManager.NANOAPP_ID,
-                        PresenceManager.NANOAPP_MESSAGE_TYPE_FILTER_RESULT,
+                        ChreDiscoveryProvider.NANOAPP_ID,
+                        ChreDiscoveryProvider.NANOAPP_MESSAGE_TYPE_FILTER_RESULT,
                         new byte[] {1, 2, 3});
         mChreCommunication.onMessageFromNanoApp(mClient, message);
         verify(mChreCallback).onMessageFromNanoApp(eq(message));
@@ -115,8 +116,8 @@
 
     @Test
     public void testOnNanoAppLoaded() {
-        mChreCommunication.onNanoAppLoaded(mClient, PresenceManager.NANOAPP_ID);
-        verify(mChreCallback).onNanoAppRestart(eq(PresenceManager.NANOAPP_ID));
+        mChreCommunication.onNanoAppLoaded(mClient, ChreDiscoveryProvider.NANOAPP_ID);
+        verify(mChreCallback).onNanoAppRestart(eq(ChreDiscoveryProvider.NANOAPP_ID));
     }
 
     private static class InlineExecutor implements Executor {
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/ChreDiscoveryProviderTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/ChreDiscoveryProviderTest.java
new file mode 100644
index 0000000..7c0dd92
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/ChreDiscoveryProviderTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.hardware.location.NanoAppMessage;
+import android.nearby.ScanFilter;
+
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.google.protobuf.ByteString;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import service.proto.Blefilter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+public class ChreDiscoveryProviderTest {
+    @Mock AbstractDiscoveryProvider.Listener mListener;
+    @Mock ChreCommunication mChreCommunication;
+
+    @Captor ArgumentCaptor<ChreCommunication.ContextHubCommsCallback> mChreCallbackCaptor;
+
+    private ChreDiscoveryProvider mChreDiscoveryProvider;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        mChreDiscoveryProvider =
+                new ChreDiscoveryProvider(context, mChreCommunication, new InLineExecutor());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testOnStart() {
+        List<ScanFilter> scanFilters = new ArrayList<>();
+        mChreDiscoveryProvider.getController().setProviderScanFilters(scanFilters);
+        mChreDiscoveryProvider.onStart();
+        verify(mChreCommunication).start(mChreCallbackCaptor.capture(), any());
+        mChreCallbackCaptor.getValue().started(true);
+        verify(mChreCommunication).sendMessageToNanoApp(any());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testOnNearbyDeviceDiscovered() {
+        Blefilter.PublicCredential credential =
+                Blefilter.PublicCredential.newBuilder()
+                        .setSecretId(ByteString.copyFrom(new byte[] {1}))
+                        .setAuthenticityKey(ByteString.copyFrom(new byte[2]))
+                        .setPublicKey(ByteString.copyFrom(new byte[3]))
+                        .setEncryptedMetadata(ByteString.copyFrom(new byte[4]))
+                        .setEncryptedMetadataTag(ByteString.copyFrom(new byte[5]))
+                        .build();
+        Blefilter.BleFilterResult result =
+                Blefilter.BleFilterResult.newBuilder()
+                        .setTxPower(2)
+                        .setRssi(1)
+                        .setPublicCredential(credential)
+                        .build();
+        Blefilter.BleFilterResults results =
+                Blefilter.BleFilterResults.newBuilder().addResult(result).build();
+        NanoAppMessage chre_message =
+                NanoAppMessage.createMessageToNanoApp(
+                        ChreDiscoveryProvider.NANOAPP_ID,
+                        ChreDiscoveryProvider.NANOAPP_MESSAGE_TYPE_FILTER_RESULT,
+                        results.toByteArray());
+        mChreDiscoveryProvider.getController().setListener(mListener);
+        mChreDiscoveryProvider.onStart();
+        verify(mChreCommunication).start(mChreCallbackCaptor.capture(), any());
+        mChreCallbackCaptor.getValue().onMessageFromNanoApp(chre_message);
+        verify(mListener).onNearbyDeviceDiscovered(any());
+    }
+
+    private static class InLineExecutor implements Executor {
+        @Override
+        public void execute(Runnable command) {
+            command.run();
+        }
+    }
+}