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