Merge "Add Reflect, reflectionException."
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/AccountKeyGenerator.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/AccountKeyGenerator.java
new file mode 100644
index 0000000..28a9c33
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/AccountKeyGenerator.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2021 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.common.bluetooth.fastpair;
+
+import static com.android.server.nearby.common.bluetooth.fastpair.AesEcbSingleBlockEncryption.generateKey;
+
+import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.AccountKeyCharacteristic;
+
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * This is to generate account key with fast-pair style.
+ */
+public final class AccountKeyGenerator {
+
+    // Generate a key where the first byte is always defined as the type, 0x04. This maintains 15
+    // bytes of entropy in the key while also allowing providers to verify that they have received
+    // a properly formatted key and decrypted it correctly, minimizing the risk of replay attacks.
+
+    /**
+     * Creates account key.
+     */
+    public static byte[] createAccountKey() throws NoSuchAlgorithmException {
+        byte[] accountKey = generateKey();
+        accountKey[0] = AccountKeyCharacteristic.TYPE;
+        return accountKey;
+    }
+
+    private AccountKeyGenerator() {
+    }
+}
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothClassicPairer.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothClassicPairer.java
new file mode 100644
index 0000000..6c467d3
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothClassicPairer.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2021 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.common.bluetooth.fastpair;
+
+import static android.bluetooth.BluetoothDevice.BOND_BONDED;
+import static android.bluetooth.BluetoothDevice.BOND_BONDING;
+import static android.bluetooth.BluetoothDevice.BOND_NONE;
+import static android.bluetooth.BluetoothDevice.ERROR;
+import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
+
+import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothAddress.maskBluetoothAddress;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.annotation.WorkerThread;
+
+import com.google.common.base.Strings;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Pairs to Bluetooth classic devices with passkey confirmation.
+ */
+// TODO(b/202524672): Add class unit test.
+public class BluetoothClassicPairer {
+
+    private static final String TAG = BluetoothClassicPairer.class.getSimpleName();
+    /**
+     * Hidden, see {@link BluetoothDevice}.
+     */
+    private static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON";
+
+    private final Context mContext;
+    private final BluetoothDevice mDevice;
+    private final Preferences mPreferences;
+    private final PasskeyConfirmationHandler mPasskeyConfirmationHandler;
+
+    public BluetoothClassicPairer(
+            Context context,
+            BluetoothDevice device,
+            Preferences preferences,
+            PasskeyConfirmationHandler passkeyConfirmationHandler) {
+        this.mContext = context;
+        this.mDevice = device;
+        this.mPreferences = preferences;
+        this.mPasskeyConfirmationHandler = passkeyConfirmationHandler;
+    }
+
+    /**
+     * Pairs with the device. Throws a {@link PairingException} if any error occurs.
+     */
+    @WorkerThread
+    public void pair() throws PairingException {
+        Log.i(TAG, "BluetoothClassicPairer, createBond with " + maskBluetoothAddress(mDevice)
+                + ", type=" + mDevice.getType());
+        try (BondedReceiver bondedReceiver = new BondedReceiver()) {
+            if (mDevice.createBond()) {
+                bondedReceiver.await(mPreferences.getCreateBondTimeoutSeconds(), SECONDS);
+            } else {
+                throw new PairingException(
+                        "BluetoothClassicPairer, createBond got immediate error");
+            }
+        } catch (TimeoutException | InterruptedException | ExecutionException e) {
+            throw new PairingException("BluetoothClassicPairer, createBond failed", e);
+        }
+    }
+
+    protected boolean isPaired() {
+        return mDevice.getBondState() == BOND_BONDED;
+    }
+
+    /**
+     * Receiver that closes after bonding has completed.
+     */
+    private class BondedReceiver extends DeviceIntentReceiver {
+
+        private BondedReceiver() {
+            super(
+                    mContext,
+                    mPreferences,
+                    mDevice,
+                    BluetoothDevice.ACTION_PAIRING_REQUEST,
+                    BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        }
+
+        /**
+         * Called with ACTION_PAIRING_REQUEST and ACTION_BOND_STATE_CHANGED about the interesting
+         * device (see {@link DeviceIntentReceiver}).
+         *
+         * <p>The ACTION_PAIRING_REQUEST intent provides the passkey which will be sent to the
+         * {@link PasskeyConfirmationHandler} for showing the UI, and the ACTION_BOND_STATE_CHANGED
+         * will provide the result of the bonding.
+         */
+        @Override
+        protected void onReceiveDeviceIntent(Intent intent) {
+            String intentAction = intent.getAction();
+            BluetoothDevice remoteDevice = intent.getParcelableExtra(EXTRA_DEVICE);
+            if (Strings.isNullOrEmpty(intentAction)
+                    || remoteDevice == null
+                    || !remoteDevice.getAddress().equals(mDevice.getAddress())) {
+                Log.w(TAG,
+                        "BluetoothClassicPairer, receives " + intentAction
+                                + " from unexpected device " + maskBluetoothAddress(remoteDevice));
+                return;
+            }
+            switch (intentAction) {
+                case BluetoothDevice.ACTION_PAIRING_REQUEST:
+                    handlePairingRequest(
+                            remoteDevice,
+                            intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, ERROR),
+                            intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, ERROR));
+                    break;
+                case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
+                    handleBondStateChanged(
+                            intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, ERROR),
+                            intent.getIntExtra(EXTRA_REASON, ERROR));
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private void handlePairingRequest(BluetoothDevice device, int variant, int passkey) {
+            Log.i(TAG,
+                    "BluetoothClassicPairer, pairing request, " + device + ", " + variant + ", "
+                            + passkey);
+            // Prevent Bluetooth Settings from getting the pairing request and showing its own UI.
+            abortBroadcast();
+            mPasskeyConfirmationHandler.onPasskeyConfirmation(device, passkey);
+        }
+
+        private void handleBondStateChanged(int bondState, int reason) {
+            Log.i(TAG,
+                    "BluetoothClassicPairer, bond state changed to " + bondState + ", reason="
+                            + reason);
+            switch (bondState) {
+                case BOND_BONDING:
+                    // Don't close!
+                    return;
+                case BOND_BONDED:
+                    close();
+                    return;
+                case BOND_NONE:
+                default:
+                    closeWithError(
+                            new PairingException(
+                                    "BluetoothClassicPairer, createBond failed, reason:" + reason));
+            }
+        }
+    }
+
+    // Applies UsesPermission annotation will create circular dependency.
+    @SuppressLint("MissingPermission")
+    static void setPairingConfirmation(BluetoothDevice device, boolean confirm) {
+        Log.i(TAG, "BluetoothClassicPairer: setPairingConfirmation " + maskBluetoothAddress(device)
+                + ", confirm: " + confirm);
+        device.setPairingConfirmation(confirm);
+    }
+}
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Constants.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Constants.java
new file mode 100644
index 0000000..d3eb388
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Constants.java
@@ -0,0 +1,724 @@
+/*
+ * Copyright 2021 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.common.bluetooth.fastpair;
+
+import static android.bluetooth.BluetoothProfile.A2DP;
+import static android.bluetooth.BluetoothProfile.HEADSET;
+
+import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothUuids.to128BitUuid;
+import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothUuids.toFastPair128BitUuid;
+
+import static com.google.common.primitives.Bytes.concat;
+
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothHeadset;
+import android.util.Log;
+
+import com.android.server.nearby.common.bluetooth.BluetoothException;
+import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.primitives.Shorts;
+
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.util.Random;
+import java.util.UUID;
+
+/**
+ * Fast Pair and Transport Discovery Service constants.
+ *
+ * <p>Unless otherwise specified, these numbers come from
+ * {https://www.bluetooth.com/specifications/gatt}.
+ */
+public final class Constants {
+
+    /** A2DP sink service uuid. */
+    public static final short A2DP_SINK_SERVICE_UUID = 0x110B;
+
+    /** Headset service uuid. */
+    public static final short HEADSET_SERVICE_UUID = 0x1108;
+
+    /** Hands free sink service uuid. */
+    public static final short HANDS_FREE_SERVICE_UUID = 0x111E;
+
+    /** Bluetooth address length. */
+    public static final int BLUETOOTH_ADDRESS_LENGTH = 6;
+
+    private static final String TAG = Constants.class.getSimpleName();
+
+    /**
+     * Defined by https://developers.google.com/nearby/fast-pair/spec.
+     */
+    public static final class FastPairService {
+
+        /** Fast Pair service UUID. */
+        public static final UUID ID = to128BitUuid((short) 0xFE2C);
+
+        /**
+         * Characteristic to write verification bytes to during the key handshake.
+         */
+        public static final class KeyBasedPairingCharacteristic {
+
+            private static final short SHORT_UUID = 0x1234;
+
+            /**
+             * Gets the new 128-bit UUID of this characteristic.
+             *
+             * <p>Note: For GATT server only. GATT client should use {@link
+             * KeyBasedPairingCharacteristic#getId(BluetoothGattConnection)}.
+             */
+            public static final UUID CUSTOM_128_BIT_UUID = toFastPair128BitUuid(SHORT_UUID);
+
+            /**
+             * Gets the {@link UUID} of this characteristic.
+             *
+             * <p>This method is designed for being backward compatible with old version of UUID
+             * therefore needs the {@link BluetoothGattConnection} parameter to check the supported
+             * status of the Fast Pair provider.
+             */
+            public static UUID getId(BluetoothGattConnection gattConnection) {
+                return getSupportedUuid(gattConnection, SHORT_UUID);
+            }
+
+            /**
+             * Constants related to the decrypted request written to this characteristic.
+             */
+            public static final class Request {
+
+                /**
+                 * The size of this message.
+                 */
+                public static final int SIZE = 16;
+
+                /**
+                 * The index of this message for indicating the type byte.
+                 */
+                public static final int TYPE_INDEX = 0;
+
+                /**
+                 * The index of this message for indicating the flags byte.
+                 */
+                public static final int FLAGS_INDEX = 1;
+
+                /**
+                 * The index of this message for indicating the verification data start from.
+                 */
+                public static final int VERIFICATION_DATA_INDEX = 2;
+
+                /**
+                 * The length of verification data, it is Provider’s current BLE address or public
+                 * address.
+                 */
+                public static final int VERIFICATION_DATA_LENGTH = BLUETOOTH_ADDRESS_LENGTH;
+
+                /**
+                 * The index of this message for indicating the seeker's public address start from.
+                 */
+                public static final int SEEKER_PUBLIC_ADDRESS_INDEX = 8;
+
+                /**
+                 * The index of this message for indicating event group.
+                 */
+                public static final int EVENT_GROUP_INDEX = 8;
+
+                /**
+                 * The index of this message for indicating event code.
+                 */
+                public static final int EVENT_CODE_INDEX = 9;
+
+                /**
+                 * The index of this message for indicating the length of additional data of the
+                 * event.
+                 */
+                public static final int EVENT_ADDITIONAL_DATA_LENGTH_INDEX = 10;
+
+                /**
+                 * The index of this message for indicating the event additional data start from.
+                 */
+                public static final int EVENT_ADDITIONAL_DATA_INDEX = 11;
+
+                /**
+                 * The index of this message for indicating the additional data type used in the
+                 * following Additional Data characteristic.
+                 */
+                public static final int ADDITIONAL_DATA_TYPE_INDEX = 10;
+
+                /**
+                 * The type of this message for Key-based Pairing Request.
+                 */
+                public static final byte TYPE_KEY_BASED_PAIRING_REQUEST = 0x00;
+
+                /**
+                 * The bit indicating that the Fast Pair device should temporarily become
+                 * discoverable.
+                 */
+                public static final byte REQUEST_DISCOVERABLE = (byte) (1 << 7);
+
+                /**
+                 * The bit indicating that the requester (Seeker) has included their public address
+                 * in bytes [7,12] of the request, and the Provider should initiate bonding to that
+                 * address.
+                 */
+                public static final byte PROVIDER_INITIATES_BONDING = (byte) (1 << 6);
+
+                /**
+                 * The bit indicating that Seeker requests Provider shall return the existing name.
+                 */
+                public static final byte REQUEST_DEVICE_NAME = (byte) (1 << 5);
+
+                /**
+                 * The bit to request retroactive pairing.
+                 */
+                public static final byte REQUEST_RETROACTIVE_PAIR = (byte) (1 << 4);
+
+                /**
+                 * The type of this message for action over BLE.
+                 */
+                public static final byte TYPE_ACTION_OVER_BLE = 0x10;
+
+                private Request() {
+                }
+            }
+
+            /**
+             * Enumerates all flags of key-based pairing request.
+             */
+            // TODO(b/201673262): Convert enum to byte.
+            public enum KeyBasedPairingRequestFlag {
+                /**
+                 * The bit indicating that the Fast Pair device should temporarily become
+                 * discoverable.
+                 */
+                REQUEST_DISCOVERABLE((byte) (1 << 7)),
+                /**
+                 * The bit indicating that the requester (Seeker) has included their public address
+                 * in bytes [7,12] of the request, and the Provider should initiate bonding to that
+                 * address.
+                 */
+                PROVIDER_INITIATES_BONDING((byte) (1 << 6)),
+                /**
+                 * The bit indicating that Seeker requests Provider shall return the existing name.
+                 */
+                REQUEST_DEVICE_NAME((byte) (1 << 5)),
+                /**
+                 * The bit indicating that the Seeker request retroactive pairing.
+                 */
+                REQUEST_RETROACTIVE_PAIR((byte) (1 << 4));
+
+                private final byte mValue;
+
+                KeyBasedPairingRequestFlag(byte flag) {
+                    this.mValue = flag;
+                }
+
+                /** Returns value. */
+                public byte getValue() {
+                    return mValue;
+                }
+            }
+
+            /**
+             * Enumerates all flags of action over BLE request, see Fast Pair spec for details.
+             */
+            // TODO(b/201673262): Convert enum to byte.
+            public enum ActionOverBleFlag {
+                /**
+                 * The bit indicating that the handshaking is for Device Action.
+                 */
+                DEVICE_ACTION((byte) (1 << 7)),
+                /**
+                 * The bit indicating that this handshake will be followed by Additional Data
+                 * characteristic.
+                 */
+                ADDITIONAL_DATA_CHARACTERISTIC((byte) (1 << 6));
+
+                private final byte mValue;
+
+                ActionOverBleFlag(byte value) {
+                    this.mValue = value;
+                }
+
+                /** Retunns value. */
+                public byte getValue() {
+                    return mValue;
+                }
+            }
+
+            /**
+             * Constants related to the decrypted response sent back in a notify.
+             */
+            public static final class Response {
+
+                /**
+                 * The type of this message = Key-based Pairing Response.
+                 */
+                public static final byte TYPE = 0x01;
+
+                private Response() {
+                }
+            }
+
+            private KeyBasedPairingCharacteristic() {
+            }
+        }
+
+        /**
+         * Characteristic used during Key-based Pairing, to exchange the encrypted passkey.
+         */
+        public static final class PasskeyCharacteristic {
+
+            private static final short SHORT_UUID = 0x1235;
+
+            /**
+             * Gets the new 128-bit UUID of this characteristic.
+             *
+             * <p>Note: For GATT server only. GATT client should use {@link
+             * PasskeyCharacteristic#getId(BluetoothGattConnection)}.
+             */
+            public static final UUID CUSTOM_128_BIT_UUID = toFastPair128BitUuid(SHORT_UUID);
+
+            /**
+             * Gets the {@link UUID} of this characteristic.
+             *
+             * <p>This method is designed for being backward compatible with old version of UUID
+             * therefore
+             * needs the {@link BluetoothGattConnection} parameter to check the supported status of
+             * the Fast Pair provider.
+             */
+            public static UUID getId(BluetoothGattConnection gattConnection) {
+                return getSupportedUuid(gattConnection, SHORT_UUID);
+            }
+
+            /**
+             * The type of the Passkey Block message.
+             */
+            // TODO(b/201673262): Convert enum to byte.
+            public enum Type {
+                /**
+                 * Seeker's Passkey.
+                 */
+                SEEKER((byte) 0x02),
+                /**
+                 * Provider's Passkey.
+                 */
+                PROVIDER((byte) 0x03);
+
+                private final byte mValue;
+
+                Type(byte value) {
+                    this.mValue = value;
+                }
+            }
+
+            /**
+             * Constructs the encrypted value to write to the characteristic.
+             */
+            public static byte[] encrypt(Type type, byte[] secret, int passkey)
+                    throws GeneralSecurityException {
+                Preconditions.checkArgument(
+                        0 < passkey && passkey < /*2^24=*/ 16777216,
+                        "Passkey %s must be positive and fit in 3 bytes",
+                        passkey);
+                byte[] passkeyBytes =
+                        new byte[]{(byte) (passkey >>> 16), (byte) (passkey >>> 8), (byte) passkey};
+                byte[] salt =
+                        new byte[AesEcbSingleBlockEncryption.AES_BLOCK_LENGTH - 1
+                                - passkeyBytes.length];
+                new Random().nextBytes(salt);
+                return AesEcbSingleBlockEncryption.encrypt(
+                        secret, concat(new byte[]{type.mValue}, passkeyBytes, salt));
+            }
+
+            /**
+             * Extracts the passkey from the encrypted characteristic value.
+             */
+            public static int decrypt(Type type, byte[] secret, byte[] passkeyCharacteristicValue)
+                    throws GeneralSecurityException {
+                byte[] decrypted = AesEcbSingleBlockEncryption
+                        .decrypt(secret, passkeyCharacteristicValue);
+                if (decrypted[0] != type.mValue) {
+                    throw new GeneralSecurityException(
+                            "Wrong Passkey Block type (expected " + type.mValue + ", got "
+                                    + decrypted[0] + ")");
+                }
+                return ByteBuffer.allocate(4)
+                        .put((byte) 0)
+                        .put(decrypted, /*offset=*/ 1, /*length=*/ 3)
+                        .getInt(0);
+            }
+
+            private PasskeyCharacteristic() {
+            }
+        }
+
+        /**
+         * Characteristic to write to during the key exchange.
+         */
+        public static final class AccountKeyCharacteristic {
+
+            private static final short SHORT_UUID = 0x1236;
+
+            /**
+             * Gets the new 128-bit UUID of this characteristic.
+             *
+             * <p>Note: For GATT server only. GATT client should use {@link
+             * AccountKeyCharacteristic#getId(BluetoothGattConnection)}.
+             */
+            public static final UUID CUSTOM_128_BIT_UUID = toFastPair128BitUuid(SHORT_UUID);
+
+            /**
+             * Gets the {@link UUID} of this characteristic.
+             *
+             * <p>This method is designed for being backward compatible with old version of UUID
+             * therefore
+             * needs the {@link BluetoothGattConnection} parameter to check the supported status of
+             * the Fast Pair provider.
+             */
+            public static UUID getId(BluetoothGattConnection gattConnection) {
+                return getSupportedUuid(gattConnection, SHORT_UUID);
+            }
+
+            /**
+             * The type for this message, account key request.
+             */
+            public static final byte TYPE = 0x04;
+
+            private AccountKeyCharacteristic() {
+            }
+        }
+
+        /**
+         * Characteristic to write to and notify on for handling personalized name, see {@link
+         * NamingEncoder}.
+         */
+        public static final class NameCharacteristic {
+
+            private static final short SHORT_UUID = 0x1237;
+
+            /**
+             * Gets the new 128-bit UUID of this characteristic.
+             *
+             * <p>Note: For GATT server only. GATT client should use {@link
+             * NameCharacteristic#getId(BluetoothGattConnection)}.
+             */
+            public static final UUID CUSTOM_128_BIT_UUID = toFastPair128BitUuid(SHORT_UUID);
+
+            /**
+             * Gets the {@link UUID} of this characteristic.
+             *
+             * <p>This method is designed for being backward compatible with old version of UUID
+             * therefore
+             * needs the {@link BluetoothGattConnection} parameter to check the supported status of
+             * the Fast Pair provider.
+             */
+            public static UUID getId(BluetoothGattConnection gattConnection) {
+                return getSupportedUuid(gattConnection, SHORT_UUID);
+            }
+
+            private NameCharacteristic() {
+            }
+        }
+
+        /**
+         * Characteristic to write to and notify on for handling additional data, see
+         * https://developers.google.com/nearby/fast-pair/early-access/spec#AdditionalData
+         */
+        public static final class AdditionalDataCharacteristic {
+
+            private static final short SHORT_UUID = 0x1237;
+
+            public static final int DATA_ID_INDEX = 0;
+            public static final int DATA_LENGTH_INDEX = 1;
+            public static final int DATA_START_INDEX = 2;
+
+            /**
+             * Gets the new 128-bit UUID of this characteristic.
+             *
+             * <p>Note: For GATT server only. GATT client should use {@link
+             * AdditionalDataCharacteristic#getId(BluetoothGattConnection)}.
+             */
+            public static final UUID CUSTOM_128_BIT_UUID = toFastPair128BitUuid(SHORT_UUID);
+
+            /**
+             * Gets the {@link UUID} of this characteristic.
+             *
+             * <p>This method is designed for being backward compatible with old version of UUID
+             * therefore
+             * needs the {@link BluetoothGattConnection} parameter to check the supported status of
+             * the Fast Pair provider.
+             */
+            public static UUID getId(BluetoothGattConnection gattConnection) {
+                return getSupportedUuid(gattConnection, SHORT_UUID);
+            }
+
+            /**
+             * Enumerates all types of additional data.
+             */
+            // TODO(b/201673262): Convert enum to byte.
+            public enum AdditionalDataType {
+                /**
+                 * The value indicating that the type is for personalized name.
+                 */
+                PERSONALIZED_NAME((byte) 0x01),
+                UNKNOWN((byte) 0x00); // and all others.
+
+                private final byte mValue;
+
+                AdditionalDataType(byte value) {
+                    this.mValue = value;
+                }
+
+                public byte getValue() {
+                    return mValue;
+                }
+
+                /** Converts byte to enum. */
+                public static AdditionalDataType valueOf(byte value) {
+                    for (AdditionalDataType type : AdditionalDataType.values()) {
+                        if (type.getValue() == value) {
+                            return type;
+                        }
+                    }
+                    return UNKNOWN;
+                }
+            }
+
+            private AdditionalDataCharacteristic() {
+            }
+        }
+
+        /**
+         * Characteristic to control the beaconing feature (FastPair+Eddystone).
+         */
+        public static final class BeaconActionsCharacteristic {
+
+            private static final short SHORT_UUID = 0x1238;
+
+            /**
+             * Gets the new 128-bit UUID of this characteristic.
+             *
+             * <p>Note: For GATT server only. GATT client should use {@link
+             * BeaconActionsCharacteristic#getId(BluetoothGattConnection)}.
+             */
+            public static final UUID CUSTOM_128_BIT_UUID = toFastPair128BitUuid(SHORT_UUID);
+
+            /**
+             * Gets the {@link UUID} of this characteristic.
+             *
+             * <p>This method is designed for being backward compatible with old version of UUID
+             * therefore
+             * needs the {@link BluetoothGattConnection} parameter to check the supported status of
+             * the Fast Pair provider.
+             */
+            public static UUID getId(BluetoothGattConnection gattConnection) {
+                return getSupportedUuid(gattConnection, SHORT_UUID);
+            }
+
+            /**
+             * Enumerates all types of beacon actions.
+             */
+            // TODO(b/201673262): Convert enum to byte.
+            public enum BeaconActionType {
+                /**
+                 * The value indicating that the type is for personalized name.
+                 */
+                READ_BEACON_PARAMETERS((byte) 0x00),
+                READ_PROVISIONING_STATE((byte) 0x01),
+                SET_EPHEMERAL_IDENTITY_KEY((byte) 0x02),
+                CLEAR_EPHEMERAL_IDENTITY_KEY((byte) 0x03),
+                READ_EPHEMERAL_IDENTITY_KEY((byte) 0x04),
+                RING((byte) 0x05),
+                READ_RINGING_STATE((byte) 0x06),
+                UNKNOWN((byte) 0xFF); // and all others.
+
+                private final byte mValue;
+
+                BeaconActionType(byte value) {
+                    this.mValue = value;
+                }
+
+                public byte getValue() {
+                    return mValue;
+                }
+
+                /** Converts value to enum. */
+                public static BeaconActionType valueOf(byte value) {
+                    for (BeaconActionType type : BeaconActionType.values()) {
+                        if (type.getValue() == value) {
+                            return type;
+                        }
+                    }
+                    return UNKNOWN;
+                }
+            }
+
+            private BeaconActionsCharacteristic() {
+            }
+        }
+
+        /**
+         * Characteristic to read for checking firmware version. 0X2A26 is assigned number from
+         * bluetooth SIG website.
+         */
+        public static final class FirmwareVersionCharacteristic {
+
+            /** UUID for firmware version. */
+            public static final UUID ID = to128BitUuid((short) 0x2A26);
+
+            private FirmwareVersionCharacteristic() {
+            }
+        }
+
+        private FastPairService() {
+        }
+    }
+
+    /**
+     * Defined by the BR/EDR Handover Profile. Pre-release version here:
+     * {https://jfarfel.users.x20web.corp.google.com/Bluetooth%20Handover%20d09.pdf}
+     */
+    public interface TransportDiscoveryService {
+
+        UUID ID = to128BitUuid((short) 0x1824);
+
+        byte BLUETOOTH_SIG_ORGANIZATION_ID = 0x01;
+        byte SERVICE_UUIDS_16_BIT_LIST_TYPE = 0x01;
+        byte SERVICE_UUIDS_32_BIT_LIST_TYPE = 0x02;
+        byte SERVICE_UUIDS_128_BIT_LIST_TYPE = 0x03;
+
+        /**
+         * Writing to this allows you to activate the BR/EDR transport.
+         */
+        interface ControlPointCharacteristic {
+
+            UUID ID = to128BitUuid((short) 0x2ABC);
+            byte ACTIVATE_TRANSPORT_OP_CODE = 0x01;
+        }
+
+        /**
+         * Info necessary to pair (mostly the Bluetooth Address).
+         */
+        interface BrHandoverDataCharacteristic {
+
+            UUID ID = to128BitUuid((short) 0x2C01);
+
+            /**
+             * All bits are reserved for future use.
+             */
+            byte BR_EDR_FEATURES = 0x00;
+        }
+
+        /**
+         * This characteristic exists only to wrap the descriptor.
+         */
+        interface BluetoothSigDataCharacteristic {
+
+            UUID ID = to128BitUuid((short) 0x2C02);
+
+            /**
+             * The entire Transport Block data (e.g. supported Bluetooth services).
+             */
+            interface BrTransportBlockDataDescriptor {
+
+                UUID ID = to128BitUuid((short) 0x2C03);
+            }
+        }
+    }
+
+    public static final UUID CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR_UUID =
+            to128BitUuid((short) 0x2902);
+
+    /**
+     * Wrapper for Bluetooth profile
+     */
+    public static class Profile {
+
+        public final int type;
+        public final String name;
+        public final String connectionStateAction;
+
+        private Profile(int type, String name, String connectionStateAction) {
+            this.type = type;
+            this.name = name;
+            this.connectionStateAction = connectionStateAction;
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+    }
+
+    /**
+     * {@link BluetoothHeadset} is used for both Headset and HandsFree (HFP).
+     */
+    private static final Profile HEADSET_AND_HANDS_FREE_PROFILE =
+            new Profile(
+                    HEADSET, "HEADSET_AND_HANDS_FREE",
+                    BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+
+    /** Fast Pair supported profiles. */
+    public static final ImmutableMap<Short, Profile> PROFILES =
+            ImmutableMap.<Short, Profile>builder()
+                    .put(
+                            Constants.A2DP_SINK_SERVICE_UUID,
+                            new Profile(A2DP, "A2DP",
+                                    BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED))
+                    .put(Constants.HEADSET_SERVICE_UUID, HEADSET_AND_HANDS_FREE_PROFILE)
+                    .put(Constants.HANDS_FREE_SERVICE_UUID, HEADSET_AND_HANDS_FREE_PROFILE)
+                    .build();
+
+    static short[] getSupportedProfiles() {
+        return Shorts.toArray(PROFILES.keySet());
+    }
+
+    /**
+     * Helper method of getting 128-bit UUID for Fast Pair custom GATT characteristics.
+     *
+     * <p>This method is designed for being backward compatible with old version of UUID therefore
+     * needs the {@link BluetoothGattConnection} parameter to check the supported status of the Fast
+     * Pair provider.
+     *
+     * <p>Note: For new custom GATT characteristics, don't need to use this helper and please just
+     * call {@code toFastPair128BitUuid(shortUuid)} to get the UUID. Which also implies that callers
+     * don't need to provide {@link BluetoothGattConnection} to get the UUID anymore.
+     */
+    private static UUID getSupportedUuid(BluetoothGattConnection gattConnection, short shortUuid) {
+        // In worst case (new characteristic not found), this method's performance impact is about
+        // 6ms
+        // by using Pixel2 + JBL LIVE220. And the impact should be less and less along with more and
+        // more devices adopt the new characteristics.
+        try {
+            // Checks the new UUID first.
+            if (gattConnection
+                    .getCharacteristic(FastPairService.ID, toFastPair128BitUuid(shortUuid))
+                    != null) {
+                Log.d(TAG, "Uses new KeyBasedPairingCharacteristic.ID");
+                return toFastPair128BitUuid(shortUuid);
+            }
+        } catch (BluetoothException e) {
+            Log.d(TAG, "Uses old KeyBasedPairingCharacteristic.ID");
+        }
+        // Returns the old UUID for default.
+        return to128BitUuid(shortUuid);
+    }
+
+    private Constants() {
+    }
+}
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/DeviceIntentReceiver.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/DeviceIntentReceiver.java
new file mode 100644
index 0000000..5bcf10a
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/DeviceIntentReceiver.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2021 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.common.bluetooth.fastpair;
+
+import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothAddress.maskBluetoothAddress;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+/**
+ * Like {@link SimpleBroadcastReceiver}, but for intents about a certain {@link BluetoothDevice}.
+ */
+abstract class DeviceIntentReceiver extends SimpleBroadcastReceiver {
+
+    private static final String TAG = DeviceIntentReceiver.class.getSimpleName();
+
+    private final BluetoothDevice mDevice;
+
+    static DeviceIntentReceiver oneShotReceiver(
+            Context context, Preferences preferences, BluetoothDevice device, String... actions) {
+        return new DeviceIntentReceiver(context, preferences, device, actions) {
+            @Override
+            protected void onReceiveDeviceIntent(Intent intent) throws Exception {
+                close();
+            }
+        };
+    }
+
+    /**
+     * @param context The context to use to register / unregister the receiver.
+     * @param device The interesting device. We ignore intents about other devices.
+     * @param actions The actions to include in our intent filter.
+     */
+    protected DeviceIntentReceiver(
+            Context context, Preferences preferences, BluetoothDevice device, String... actions) {
+        super(context, preferences, actions);
+        this.mDevice = device;
+    }
+
+    /**
+     * Called with intents about the interesting device (see {@link #DeviceIntentReceiver}). Any
+     * exception thrown by this method will be delivered via {@link #await}.
+     */
+    protected abstract void onReceiveDeviceIntent(Intent intent) throws Exception;
+
+    // incompatible types in argument.
+    @Override
+    protected void onReceive(Intent intent) throws Exception {
+        BluetoothDevice intentDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+        if (mDevice == null || mDevice.equals(intentDevice)) {
+            onReceiveDeviceIntent(intent);
+        } else {
+            Log.v(TAG,
+                    "Ignoring intent for device=" + maskBluetoothAddress(intentDevice)
+                            + "(expected "
+                            + maskBluetoothAddress(mDevice) + ")");
+        }
+    }
+}
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Preferences.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Preferences.java
new file mode 100644
index 0000000..034c640
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Preferences.java
@@ -0,0 +1,779 @@
+/*
+ * Copyright 2021 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.common.bluetooth.fastpair;
+
+import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothUuids.get16BitUuid;
+
+import androidx.annotation.Nullable;
+
+import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.FirmwareVersionCharacteristic;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.primitives.Shorts;
+
+import java.nio.ByteOrder;
+
+/**
+ * Preferences that tweak the Fast Pairing process: timeouts, number of retries... All preferences
+ * have default values which should be reasonable for all clients.
+ */
+@AutoValue
+public abstract class Preferences {
+
+    /**
+     * Timeout for each GATT operation (not for the whole pairing process).
+     */
+    public abstract int getGattOperationTimeoutSeconds();
+
+    /** Timeout for Gatt connection operation. */
+    public abstract int getGattConnectionTimeoutSeconds();
+
+    /** Timeout for Bluetooth toggle. */
+    public abstract int getBluetoothToggleTimeoutSeconds();
+
+    /** Sleep time for Bluetooth toggle. */
+    public abstract int getBluetoothToggleSleepSeconds();
+
+    /** Timeout for classic discovery. */
+    public abstract int getClassicDiscoveryTimeoutSeconds();
+
+    /** Number of discovery attempts allowed. */
+    public abstract int getNumDiscoverAttempts();
+
+    /** Sleep time between discovery retry. */
+    public abstract int getDiscoveryRetrySleepSeconds();
+
+    /** Whether to ignore error incurred during discovery. */
+    public abstract boolean getIgnoreDiscoveryError();
+
+    /** Timeout for Sdp. */
+    public abstract int getSdpTimeoutSeconds();
+
+    /** Number of Sdp attempts allowed. */
+    public abstract int getNumSdpAttempts();
+
+    /** Number of create bond attempts allowed. */
+    public abstract int getNumCreateBondAttempts();
+
+    /** Number of connect attempts allowed. */
+    public abstract int getNumConnectAttempts();
+
+    /** Number of write account key attempts allowed. */
+    public abstract int getNumWriteAccountKeyAttempts();
+
+    /** Returns whether it is OK toggle bluetooth to retry upon failure.  */
+    public abstract boolean getToggleBluetoothOnFailure();
+
+    /** Whether to get Bluetooth state using polling. */
+    public abstract boolean getBluetoothStateUsesPolling();
+
+    /** Polling time when retrieving Bluetooth state. */
+    public abstract int getBluetoothStatePollingMillis();
+
+    /**
+     * The number of times to attempt a generic operation, before giving up.
+     */
+    public abstract int getNumAttempts();
+
+    /** Returns whether BrEdr handover is enabled. */
+    public abstract boolean getEnableBrEdrHandover();
+
+    /** Returns characteristic Id for Br Handover data. */
+    public abstract short getBrHandoverDataCharacteristicId();
+
+    /** Returns characteristic Id for Bluethoth Sig data. */
+    public abstract short getBluetoothSigDataCharacteristicId();
+
+    /** Returns characteristic Id for Firmware version. */
+    public abstract short getFirmwareVersionCharacteristicId();
+
+    /** Returns descripter Id for Br transport block data. */
+    public abstract short getBrTransportBlockDataDescriptorId();
+
+    /** Whether to wait for Uuids after bonding. */
+    public abstract boolean getWaitForUuidsAfterBonding();
+
+    /** Whether to get received Uuids and bonded events before close. */
+    public abstract boolean getReceiveUuidsAndBondedEventBeforeClose();
+
+    /** Timeout for remove bond operation. */
+    public abstract int getRemoveBondTimeoutSeconds();
+
+    /** Sleep time for remove bond operation. */
+    public abstract int getRemoveBondSleepMillis();
+
+    /**
+     * This almost always succeeds (or fails) in 2-10 seconds (Taimen running O -> Nexus 6P sim).
+     */
+    public abstract int getCreateBondTimeoutSeconds();
+
+    /** Timeout for creating bond with Hid devices. */
+    public abstract int getHidCreateBondTimeoutSeconds();
+
+    /** Timeout for get proxy operation. */
+    public abstract int getProxyTimeoutSeconds();
+
+    /** Whether to reject phone book access. */
+    public abstract boolean getRejectPhonebookAccess();
+
+    /** Whether to reject message access. */
+    public abstract boolean getRejectMessageAccess();
+
+    /** Whether to reject sim access. */
+    public abstract boolean getRejectSimAccess();
+
+    /** Sleep time for write account key operation. */
+    public abstract int getWriteAccountKeySleepMillis();
+
+    /** Whether to skip disconneting gatt before writing account key. */
+    public abstract boolean getSkipDisconnectingGattBeforeWritingAccountKey();
+
+    /** Whether to get more event log for quality improvement. */
+    public abstract boolean getMoreEventLogForQuality();
+
+    /** Whether to retry gatt connection and secrete handshake. */
+    public abstract boolean getRetryGattConnectionAndSecretHandshake();
+
+    /** Short Gatt connection timeoout. */
+    public abstract long getGattConnectShortTimeoutMs();
+
+    /** Long Gatt connection timeout. */
+    public abstract long getGattConnectLongTimeoutMs();
+
+    /** Short Timeout for Gatt connection, including retry. */
+    public abstract long getGattConnectShortTimeoutRetryMaxSpentTimeMs();
+
+    /** Timeout for address rotation, including retry. */
+    public abstract long getAddressRotateRetryMaxSpentTimeMs();
+
+    /** Returns pairing retry delay time. */
+    public abstract long getPairingRetryDelayMs();
+
+    /** Short timeout for secrete handshake. */
+    public abstract long getSecretHandshakeShortTimeoutMs();
+
+    /** Long timeout for secret handshake. */
+    public abstract long getSecretHandshakeLongTimeoutMs();
+
+    /** Short timeout for secret handshake, including retry. */
+    public abstract long getSecretHandshakeShortTimeoutRetryMaxSpentTimeMs();
+
+    /** Long timeout for secret handshake, including retry. */
+    public abstract long getSecretHandshakeLongTimeoutRetryMaxSpentTimeMs();
+
+    /** Number of secrete handshake retry allowed. */
+    public abstract long getSecretHandshakeRetryAttempts();
+
+    /** Timeout for secrete handshake and gatt connection, including retry. */
+    public abstract long getSecretHandshakeRetryGattConnectionMaxSpentTimeMs();
+
+    /** Timeout for signal lost handling, including retry. */
+    public abstract long getSignalLostRetryMaxSpentTimeMs();
+
+    /** Returns error for gatt connection and secrete handshake, without retry. */
+    public abstract ImmutableSet<Integer> getGattConnectionAndSecretHandshakeNoRetryGattError();
+
+    /** Whether to retry upon secrete handshake timeout. */
+    public abstract boolean getRetrySecretHandshakeTimeout();
+
+    /** Wehther to log user manual retry. */
+    public abstract boolean getLogUserManualRetry();
+
+    /** Returns number of pairing failure counts. */
+    public abstract int getPairFailureCounts();
+
+    /** Returns cached device address. */
+    public abstract String getCachedDeviceAddress();
+
+    /** Returns possible cached device address. */
+    public abstract String getPossibleCachedDeviceAddress();
+
+    /** Returns count of paired devices from the same model Id. */
+    public abstract int getSameModelIdPairedDeviceCount();
+
+    /** Whether the bonded device address is in the Cache . */
+    public abstract boolean getIsDeviceFinishCheckAddressFromCache();
+
+    /** Whether to log pairing info when cached model Id is hit. */
+    public abstract boolean getLogPairWithCachedModelId();
+
+    /** Whether to directly connnect to a profile of a device, whose model Id is in cache. */
+    public abstract boolean getDirectConnectProfileIfModelIdInCache();
+
+    /**
+     * Whether to auto-accept
+     * {@link android.bluetooth.BluetoothDevice#PAIRING_VARIANT_PASSKEY_CONFIRMATION}.
+     * Only the Fast Pair Simulator (which runs on an Android device) sends this. Since real
+     * Bluetooth headphones don't have displays, they use secure simple pairing (no pin code
+     * confirmation; we get no pairing request broadcast at all). So we may want to turn this off in
+     * prod.
+     */
+    public abstract boolean getAcceptPasskey();
+
+    /** Returns Uuids for supported profiles. */
+    @SuppressWarnings("mutable")
+    public abstract byte[] getSupportedProfileUuids();
+
+    /**
+     * If true, after the Key-based Pairing BLE handshake, we wait for the headphones to send a
+     * pairing request to us; if false, we send the request to them.
+     */
+    public abstract boolean getProviderInitiatesBondingIfSupported();
+
+    /**
+     * If true, the first step will be attempting to connect directly to our supported profiles when
+     * a device has previously been bonded. This will help with performance on subsequent bondings
+     * and help to increase reliability in some cases.
+     */
+    public abstract boolean getAttemptDirectConnectionWhenPreviouslyBonded();
+
+    /**
+     * If true, closed Gatt connections will be reopened when they are needed again. Otherwise, they
+     * will remain closed until they are explicitly reopened.
+     */
+    public abstract boolean getAutomaticallyReconnectGattWhenNeeded();
+
+    /**
+     * If true, we'll finish the pairing process after we've created a bond instead of after
+     * connecting a profile.
+     */
+    public abstract boolean getSkipConnectingProfiles();
+
+    /**
+     * If true, continues the pairing process if we've timed out due to not receiving UUIDs from the
+     * headset. We can still attempt to connect to A2DP afterwards. If false, Fast Pair will fail
+     * after this step since we're expecting to receive the UUIDs.
+     */
+    public abstract boolean getIgnoreUuidTimeoutAfterBonded();
+
+    /**
+     * If true, a specific transport type will be included in the create bond request, which will be
+     * used for dual mode devices. Otherwise, we'll use the platform defined default which is
+     * BluetoothDevice.TRANSPORT_AUTO. See {@link #getCreateBondTransportType()}.
+     */
+    public abstract boolean getSpecifyCreateBondTransportType();
+
+    /**
+     * The transport type to use when creating a bond when
+     * {@link #getSpecifyCreateBondTransportType()} is true. This should be one of
+     * BluetoothDevice.TRANSPORT_AUTO, BluetoothDevice.TRANSPORT_BREDR, or
+     * BluetoothDevice.TRANSPORT_LE.
+     */
+    public abstract int getCreateBondTransportType();
+
+    /** Whether to increase intent filter priority. */
+    public abstract boolean getIncreaseIntentFilterPriority();
+
+    /** Whether to evaluate performance. */
+    public abstract boolean getEvaluatePerformance();
+
+    /** Returns extra logging information. */
+    @Nullable
+    public abstract ExtraLoggingInformation getExtraLoggingInformation();
+
+    /** Whether to enable naming characteristic. */
+    public abstract boolean getEnableNamingCharacteristic();
+
+    /** Whether to enable firmware version characteristic. */
+    public abstract boolean getEnableFirmwareVersionCharacteristic();
+
+    /**
+     * If true, even Fast Pair identifies a provider have paired with the account, still writes the
+     * identified account key to the provider.
+     */
+    public abstract boolean getKeepSameAccountKeyWrite();
+
+    /**
+     * If true, run retroactive pairing.
+     */
+    public abstract boolean getIsRetroactivePairing();
+
+    /**
+     * If it's larger than 0, {@link android.bluetooth.BluetoothDevice#fetchUuidsWithSdp} would be
+     * triggered with number of attempts after device is bonded and no profiles were automatically
+     * discovered".
+     */
+    public abstract int getNumSdpAttemptsAfterBonded();
+
+    /**
+     * If true, supports HID device for fastpair.
+     */
+    public abstract boolean getSupportHidDevice();
+
+    /**
+     * If true, we'll enable the pairing behavior to handle the state transition from BOND_BONDED to
+     * BOND_BONDING when directly connecting profiles.
+     */
+    public abstract boolean getEnablePairingWhileDirectlyConnecting();
+
+    /**
+     * If true, we will accept the user confirmation when bonding with FastPair 1.0 devices.
+     */
+    public abstract boolean getAcceptConsentForFastPairOne();
+
+    /**
+     * If it's larger than 0, we will retry connecting GATT within the timeout.
+     */
+    public abstract int getGattConnectRetryTimeoutMillis();
+
+    /**
+     * If true, then uses the new custom GATT characteristics {go/fastpair-128bit-gatt}.
+     */
+    public abstract boolean getEnable128BitCustomGattCharacteristicsId();
+
+    /**
+     * If true, then sends the internal pair step or Exception to Validator by Intent.
+     */
+    public abstract boolean getEnableSendExceptionStepToValidator();
+
+    /**
+     * If true, then adds the additional data type in the handshake packet when action over BLE.
+     */
+    public abstract boolean getEnableAdditionalDataTypeWhenActionOverBle();
+
+    /**
+     * If true, then checks the bond state when skips connecting profiles in the pairing shortcut.
+     */
+    public abstract boolean getCheckBondStateWhenSkipConnectingProfiles();
+
+    /**
+     * If true, the passkey confirmation will be handled by the half-sheet UI.
+     */
+    public abstract boolean getHandlePasskeyConfirmationByUi();
+
+    /**
+     * If true, then use pair flow to show ui when pairing is finished without connecting profile.
+     */
+    public abstract boolean getEnablePairFlowShowUiWithoutProfileConnection();
+
+    /** Converts an instance to a builder. */
+    public abstract Builder toBuilder();
+
+    /** Constructs a builder. */
+    public static Builder builder() {
+        return new AutoValue_Preferences.Builder()
+                .setGattOperationTimeoutSeconds(3)
+                .setGattConnectionTimeoutSeconds(15)
+                .setBluetoothToggleTimeoutSeconds(10)
+                .setBluetoothToggleSleepSeconds(2)
+                .setClassicDiscoveryTimeoutSeconds(10)
+                .setNumDiscoverAttempts(3)
+                .setDiscoveryRetrySleepSeconds(1)
+                .setIgnoreDiscoveryError(false)
+                .setSdpTimeoutSeconds(10)
+                .setNumSdpAttempts(3)
+                .setNumCreateBondAttempts(3)
+                .setNumConnectAttempts(1)
+                .setNumWriteAccountKeyAttempts(3)
+                .setToggleBluetoothOnFailure(false)
+                .setBluetoothStateUsesPolling(true)
+                .setBluetoothStatePollingMillis(1000)
+                .setNumAttempts(2)
+                .setEnableBrEdrHandover(false)
+                .setBrHandoverDataCharacteristicId(get16BitUuid(
+                        Constants.TransportDiscoveryService.BrHandoverDataCharacteristic.ID))
+                .setBluetoothSigDataCharacteristicId(get16BitUuid(
+                        Constants.TransportDiscoveryService.BluetoothSigDataCharacteristic.ID))
+                .setFirmwareVersionCharacteristicId(get16BitUuid(FirmwareVersionCharacteristic.ID))
+                .setBrTransportBlockDataDescriptorId(
+                        get16BitUuid(
+                                Constants.TransportDiscoveryService.BluetoothSigDataCharacteristic
+                                        .BrTransportBlockDataDescriptor.ID))
+                .setWaitForUuidsAfterBonding(true)
+                .setReceiveUuidsAndBondedEventBeforeClose(true)
+                .setRemoveBondTimeoutSeconds(5)
+                .setRemoveBondSleepMillis(1000)
+                .setCreateBondTimeoutSeconds(15)
+                .setHidCreateBondTimeoutSeconds(40)
+                .setProxyTimeoutSeconds(2)
+                .setRejectPhonebookAccess(false)
+                .setRejectMessageAccess(false)
+                .setRejectSimAccess(false)
+                .setAcceptPasskey(true)
+                .setSupportedProfileUuids(Constants.getSupportedProfiles())
+                .setWriteAccountKeySleepMillis(2000)
+                .setProviderInitiatesBondingIfSupported(false)
+                .setAttemptDirectConnectionWhenPreviouslyBonded(false)
+                .setAutomaticallyReconnectGattWhenNeeded(false)
+                .setSkipDisconnectingGattBeforeWritingAccountKey(false)
+                .setSkipConnectingProfiles(false)
+                .setIgnoreUuidTimeoutAfterBonded(false)
+                .setSpecifyCreateBondTransportType(false)
+                .setCreateBondTransportType(0 /*BluetoothDevice.TRANSPORT_AUTO*/)
+                .setIncreaseIntentFilterPriority(true)
+                .setEvaluatePerformance(false)
+                .setKeepSameAccountKeyWrite(true)
+                .setEnableNamingCharacteristic(false)
+                .setEnableFirmwareVersionCharacteristic(false)
+                .setIsRetroactivePairing(false)
+                .setNumSdpAttemptsAfterBonded(1)
+                .setSupportHidDevice(false)
+                .setEnablePairingWhileDirectlyConnecting(true)
+                .setAcceptConsentForFastPairOne(true)
+                .setGattConnectRetryTimeoutMillis(0)
+                .setEnable128BitCustomGattCharacteristicsId(true)
+                .setEnableSendExceptionStepToValidator(true)
+                .setEnableAdditionalDataTypeWhenActionOverBle(true)
+                .setCheckBondStateWhenSkipConnectingProfiles(true)
+                .setHandlePasskeyConfirmationByUi(false)
+                .setMoreEventLogForQuality(true)
+                .setRetryGattConnectionAndSecretHandshake(true)
+                .setGattConnectShortTimeoutMs(7000)
+                .setGattConnectLongTimeoutMs(15000)
+                .setGattConnectShortTimeoutRetryMaxSpentTimeMs(10000)
+                .setAddressRotateRetryMaxSpentTimeMs(15000)
+                .setPairingRetryDelayMs(100)
+                .setSecretHandshakeShortTimeoutMs(3000)
+                .setSecretHandshakeLongTimeoutMs(10000)
+                .setSecretHandshakeShortTimeoutRetryMaxSpentTimeMs(5000)
+                .setSecretHandshakeLongTimeoutRetryMaxSpentTimeMs(7000)
+                .setSecretHandshakeRetryAttempts(3)
+                .setSecretHandshakeRetryGattConnectionMaxSpentTimeMs(15000)
+                .setSignalLostRetryMaxSpentTimeMs(15000)
+                .setGattConnectionAndSecretHandshakeNoRetryGattError(ImmutableSet.of())
+                .setRetrySecretHandshakeTimeout(false)
+                .setLogUserManualRetry(true)
+                .setPairFailureCounts(0)
+                .setEnablePairFlowShowUiWithoutProfileConnection(true)
+                .setPairFailureCounts(0)
+                .setLogPairWithCachedModelId(true)
+                .setDirectConnectProfileIfModelIdInCache(false)
+                .setCachedDeviceAddress("")
+                .setPossibleCachedDeviceAddress("")
+                .setSameModelIdPairedDeviceCount(0)
+                .setIsDeviceFinishCheckAddressFromCache(true);
+    }
+
+    /**
+     * Preferences builder.
+     */
+    @AutoValue.Builder
+    public abstract static class Builder {
+
+        /** Set gatt operation timeout. */
+        public abstract Builder setGattOperationTimeoutSeconds(int value);
+
+        /** Set gatt connection timeout. */
+        public abstract Builder setGattConnectionTimeoutSeconds(int value);
+
+        /** Set bluetooth toggle timeout. */
+        public abstract Builder setBluetoothToggleTimeoutSeconds(int value);
+
+        /** Set bluetooth toggle sleep time. */
+        public abstract Builder setBluetoothToggleSleepSeconds(int value);
+
+        /** Set classic discovery timeout. */
+        public abstract Builder setClassicDiscoveryTimeoutSeconds(int value);
+
+        /** Set number of discover attempts allowed. */
+        public abstract Builder setNumDiscoverAttempts(int value);
+
+        /** Set discovery retry sleep time. */
+        public abstract Builder setDiscoveryRetrySleepSeconds(int value);
+
+        /** Set whether to ignore discovery error. */
+        public abstract Builder setIgnoreDiscoveryError(boolean value);
+
+        /** Set sdp timeout. */
+        public abstract Builder setSdpTimeoutSeconds(int value);
+
+        /** Set number of sdp attempts allowed. */
+        public abstract Builder setNumSdpAttempts(int value);
+
+        /** Set number of allowed attempts to create bond. */
+        public abstract Builder setNumCreateBondAttempts(int value);
+
+        /** Set number of connect attempts allowed. */
+        public abstract Builder setNumConnectAttempts(int value);
+
+        /** Set number of write account key attempts allowed. */
+        public abstract Builder setNumWriteAccountKeyAttempts(int value);
+
+        /** Set whether to retry by bluetooth toggle on failure. */
+        public abstract Builder setToggleBluetoothOnFailure(boolean value);
+
+        /** Set whether to use polling to set bluetooth status. */
+        public abstract Builder setBluetoothStateUsesPolling(boolean value);
+
+        /** Set Bluetooth state polling timeout. */
+        public abstract Builder setBluetoothStatePollingMillis(int value);
+
+        /** Set number of attempts. */
+        public abstract Builder setNumAttempts(int value);
+
+        /** Set whether to enable BrEdr handover. */
+        public abstract Builder setEnableBrEdrHandover(boolean value);
+
+        /** Set Br handover data characteristic Id. */
+        public abstract Builder setBrHandoverDataCharacteristicId(short value);
+
+        /** Set Bluetooth Sig data characteristic Id. */
+        public abstract Builder setBluetoothSigDataCharacteristicId(short value);
+
+        /** Set Firmware version characteristic id. */
+        public abstract Builder setFirmwareVersionCharacteristicId(short value);
+
+        /** Set Br transport block data descriptor id. */
+        public abstract Builder setBrTransportBlockDataDescriptorId(short value);
+
+        /** Set whether to wait for Uuids after bonding. */
+        public abstract Builder setWaitForUuidsAfterBonding(boolean value);
+
+        /** Set whether to receive Uuids and bonded event before close. */
+        public abstract Builder setReceiveUuidsAndBondedEventBeforeClose(boolean value);
+
+        /** Set remove bond timeout. */
+        public abstract Builder setRemoveBondTimeoutSeconds(int value);
+
+        /** Set remove bound sleep time. */
+        public abstract Builder setRemoveBondSleepMillis(int value);
+
+        /** Set create bond timeout. */
+        public abstract Builder setCreateBondTimeoutSeconds(int value);
+
+        /** Set Hid create bond timeout. */
+        public abstract Builder setHidCreateBondTimeoutSeconds(int value);
+
+        /** Set proxy timeout. */
+        public abstract Builder setProxyTimeoutSeconds(int value);
+
+        /** Set whether to reject phone book access. */
+        public abstract Builder setRejectPhonebookAccess(boolean value);
+
+        /** Set whether to reject message access. */
+        public abstract Builder setRejectMessageAccess(boolean value);
+
+        /** Set whether to reject slim access. */
+        public abstract Builder setRejectSimAccess(boolean value);
+
+        /** Set whether to accept passkey. */
+        public abstract Builder setAcceptPasskey(boolean value);
+
+        /** Set supported profile Uuids. */
+        public abstract Builder setSupportedProfileUuids(byte[] value);
+
+        /** Set whether to collect more event log for quality. */
+        public abstract Builder setMoreEventLogForQuality(boolean value);
+
+        /** Set supported profile Uuids. */
+        public Builder setSupportedProfileUuids(short... uuids) {
+            return setSupportedProfileUuids(Bytes.toBytes(ByteOrder.BIG_ENDIAN, uuids));
+        }
+
+        /** Set write account key sleep time. */
+        public abstract Builder setWriteAccountKeySleepMillis(int value);
+
+        /** Set whether to do provider initialized bonding if supported. */
+        public abstract Builder setProviderInitiatesBondingIfSupported(boolean value);
+
+        /** Set whether to try direct connection when the device is previously bonded. */
+        public abstract Builder setAttemptDirectConnectionWhenPreviouslyBonded(boolean value);
+
+        /** Set whether to automatically reconnect gatt when needed. */
+        public abstract Builder setAutomaticallyReconnectGattWhenNeeded(boolean value);
+
+        /** Set whether to skip disconnecting gatt before writing account key. */
+        public abstract Builder setSkipDisconnectingGattBeforeWritingAccountKey(boolean value);
+
+        /** Set whether to skip connecting profiles. */
+        public abstract Builder setSkipConnectingProfiles(boolean value);
+
+        /** Set whether to ignore Uuid timeout after bonded. */
+        public abstract Builder setIgnoreUuidTimeoutAfterBonded(boolean value);
+
+        /** Set whether to include transport type in create bound request. */
+        public abstract Builder setSpecifyCreateBondTransportType(boolean value);
+
+        /** Set transport type used in create bond request. */
+        public abstract Builder setCreateBondTransportType(int value);
+
+        /** Set whether to increase intent filter priority. */
+        public abstract Builder setIncreaseIntentFilterPriority(boolean value);
+
+        /** Set whether to evaluate performance. */
+        public abstract Builder setEvaluatePerformance(boolean value);
+
+        /** Set extra logging info. */
+        public abstract Builder setExtraLoggingInformation(ExtraLoggingInformation value);
+
+        /** Set whether to enable naming characteristic. */
+        public abstract Builder setEnableNamingCharacteristic(boolean value);
+
+        /**
+         * Set whether to keep writing the account key to the provider,
+         * that has already paired with the account.
+         */
+        public abstract Builder setKeepSameAccountKeyWrite(boolean value);
+
+        /** Set whether to enable firmware version characteristic. */
+        public abstract Builder setEnableFirmwareVersionCharacteristic(boolean value);
+
+        /** Set whether it is retroactive pairing. */
+        public abstract Builder setIsRetroactivePairing(boolean value);
+
+        /** Set number of allowed sdp attempts after bonded. */
+        public abstract Builder setNumSdpAttemptsAfterBonded(int value);
+
+        /** Set whether to support Hid device. */
+        public abstract Builder setSupportHidDevice(boolean value);
+
+        /**
+          *  Set wehther to enable the pairing behavior to handle the state transition from
+          *  BOND_BONDED to BOND_BONDING when directly connecting profiles.
+          */
+        public abstract Builder setEnablePairingWhileDirectlyConnecting(boolean value);
+
+        /** Set whether to accept consent for fast pair one. */
+        public abstract Builder setAcceptConsentForFastPairOne(boolean value);
+
+        /** Set Gatt connect retry timeout. */
+        public abstract Builder setGattConnectRetryTimeoutMillis(int value);
+
+        /** Set whether to enable 128 bit custom gatt characteristic Id. */
+        public abstract Builder setEnable128BitCustomGattCharacteristicsId(boolean value);
+
+        /** Set whether to send exception step to validator. */
+        public abstract Builder setEnableSendExceptionStepToValidator(boolean value);
+
+        /** Set wehther to add the additional data type in the handshake when action over BLE. */
+        public abstract Builder setEnableAdditionalDataTypeWhenActionOverBle(boolean value);
+
+        /** Set whether to check bond state when skip connecting profiles. */
+        public abstract Builder setCheckBondStateWhenSkipConnectingProfiles(boolean value);
+
+        /** Set whether to handle passkey confirmation by UI. */
+        public abstract Builder setHandlePasskeyConfirmationByUi(boolean value);
+
+        /** Set wehther to retry gatt connection and secret handshake. */
+        public abstract Builder setRetryGattConnectionAndSecretHandshake(boolean value);
+
+        /** Set gatt connect short timeout. */
+        public abstract Builder setGattConnectShortTimeoutMs(long value);
+
+        /** Set gatt connect long timeout. */
+        public abstract Builder setGattConnectLongTimeoutMs(long value);
+
+        /** Set gatt connection short timoutout, including retry. */
+        public abstract Builder setGattConnectShortTimeoutRetryMaxSpentTimeMs(long value);
+
+        /** Set address rotate timeout, including retry. */
+        public abstract Builder setAddressRotateRetryMaxSpentTimeMs(long value);
+
+        /** Set pairing retry delay time. */
+        public abstract Builder setPairingRetryDelayMs(long value);
+
+        /** Set secret handshake short timeout. */
+        public abstract Builder setSecretHandshakeShortTimeoutMs(long value);
+
+        /** Set secret handshake long timeout. */
+        public abstract Builder setSecretHandshakeLongTimeoutMs(long value);
+
+        /** Set secret handshake short timeout retry max spent time. */
+        public abstract Builder setSecretHandshakeShortTimeoutRetryMaxSpentTimeMs(long value);
+
+        /** Set secret handshake long timeout retry max spent time. */
+        public abstract Builder setSecretHandshakeLongTimeoutRetryMaxSpentTimeMs(long value);
+
+        /** Set secret handshake retry attempts allowed. */
+        public abstract Builder setSecretHandshakeRetryAttempts(long value);
+
+        /** Set secret handshake retry gatt connection max spent time. */
+        public abstract Builder setSecretHandshakeRetryGattConnectionMaxSpentTimeMs(long value);
+
+        /** Set signal loss retry max spent time. */
+        public abstract Builder setSignalLostRetryMaxSpentTimeMs(long value);
+
+        /** Set gatt connection and secret handshake no retry gatt error. */
+        public abstract Builder setGattConnectionAndSecretHandshakeNoRetryGattError(
+                ImmutableSet<Integer> value);
+
+        /** Set retry secret handshake timeout. */
+        public abstract Builder setRetrySecretHandshakeTimeout(boolean value);
+
+        /** Set whether to log user manual retry. */
+        public abstract Builder setLogUserManualRetry(boolean value);
+
+        /** Set pair falure counts. */
+        public abstract Builder setPairFailureCounts(int counts);
+
+        /**
+         * Set whether to use pair flow to show ui when pairing is finished without connecting
+         * profile..
+         */
+        public abstract Builder setEnablePairFlowShowUiWithoutProfileConnection(boolean value);
+
+        /** Set whether to log pairing with cached module Id. */
+        public abstract Builder setLogPairWithCachedModelId(boolean value);
+
+        /** Set possible cached device address. */
+        public abstract Builder setPossibleCachedDeviceAddress(String value);
+
+        /** Set paired device count from the same module Id. */
+        public abstract Builder setSameModelIdPairedDeviceCount(int value);
+
+        /** Set whether the bonded device address is from cache. */
+        public abstract Builder setIsDeviceFinishCheckAddressFromCache(boolean value);
+
+        /** Set whether to directly connect profile if modelId is in cache. */
+        public abstract Builder setDirectConnectProfileIfModelIdInCache(boolean value);
+
+        /** Set cached device address. */
+        public abstract Builder setCachedDeviceAddress(String value);
+
+        /** Builds a Preferences instance. */
+        public abstract Preferences build();
+    }
+
+    /** Whether a given Uuid is supported. */
+    public boolean isSupportedProfile(short profileUuid) {
+        return Constants.PROFILES.containsKey(profileUuid)
+                && Shorts.contains(
+                Bytes.toShorts(ByteOrder.BIG_ENDIAN, getSupportedProfileUuids()), profileUuid);
+    }
+
+    /**
+     * Information that will be used for logging.
+     */
+    @AutoValue
+    public abstract static class ExtraLoggingInformation {
+
+        /** Returns model Id. */
+        public abstract String getModelId();
+
+        /** Converts an instance to a builder. */
+        public abstract Builder toBuilder();
+
+        /** Creates a builder for ExtraLoggingInformation. */
+        public static Builder builder() {
+            return new AutoValue_Preferences_ExtraLoggingInformation.Builder();
+        }
+
+        /**
+         * Extra logging information builder.
+         */
+        @AutoValue.Builder
+        public abstract static class Builder {
+
+            /** Set model ID. */
+            public abstract Builder setModelId(String modelId);
+
+            /** Builds extra logging information. */
+            public abstract ExtraLoggingInformation build();
+        }
+    }
+}
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/SimpleBroadcastReceiver.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/SimpleBroadcastReceiver.java
new file mode 100644
index 0000000..7f525a7
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/SimpleBroadcastReceiver.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2021 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.common.bluetooth.fastpair;
+
+import android.annotation.TargetApi;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Build.VERSION_CODES;
+import android.os.Handler;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.google.common.util.concurrent.SettableFuture;
+
+import java.util.Arrays;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Like {@link BroadcastReceiver}, but:
+ *
+ * <ul>
+ *   <li>Simpler to create and register, with a list of actions.
+ *   <li>Implements AutoCloseable. If used as a resource in try-with-resources (available on
+ *       KitKat+), unregisters itself automatically.
+ *   <li>Lets you block waiting for your state transition with {@link #await}.
+ * </ul>
+ */
+// AutoCloseable only available on KitKat+.
+@TargetApi(VERSION_CODES.KITKAT)
+public abstract class SimpleBroadcastReceiver extends BroadcastReceiver implements AutoCloseable {
+
+    private static final String TAG = SimpleBroadcastReceiver.class.getSimpleName();
+
+    /**
+     * Creates a one shot receiver.
+     */
+    public static SimpleBroadcastReceiver oneShotReceiver(
+            Context context, Preferences preferences, String... actions) {
+        return new SimpleBroadcastReceiver(context, preferences, actions) {
+            @Override
+            protected void onReceive(Intent intent) {
+                close();
+            }
+        };
+    }
+
+    private final Context mContext;
+    private final SettableFuture<Void> mIsClosedFuture = SettableFuture.create();
+    private long mAwaitExtendSecond;
+
+    // Nullness checker complains about 'this' being @UnderInitialization
+    @SuppressWarnings("nullness")
+    public SimpleBroadcastReceiver(
+            Context context, Preferences preferences, @Nullable Handler handler,
+            String... actions) {
+        Log.v(TAG, this + " listening for actions " + Arrays.toString(actions));
+        this.mContext = context;
+        IntentFilter intentFilter = new IntentFilter();
+        if (preferences.getIncreaseIntentFilterPriority()) {
+            intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+        }
+        for (String action : actions) {
+            intentFilter.addAction(action);
+        }
+        context.registerReceiver(this, intentFilter, /* broadcastPermission= */ null, handler);
+    }
+
+    public SimpleBroadcastReceiver(Context context, Preferences preferences, String... actions) {
+        this(context, preferences, /* handler= */ null, actions);
+    }
+
+    /**
+     * Any exception thrown by this method will be delivered via {@link #await}.
+     */
+    protected abstract void onReceive(Intent intent) throws Exception;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.v(TAG, "Got intent with action= " + intent.getAction());
+        try {
+            onReceive(intent);
+        } catch (Exception e) {
+            closeWithError(e);
+        }
+    }
+
+    @Override
+    public void close() {
+        closeWithError(null);
+    }
+
+    void closeWithError(@Nullable Exception e) {
+        try {
+            mContext.unregisterReceiver(this);
+        } catch (IllegalArgumentException ignored) {
+            // Ignore. Happens if you unregister twice.
+        }
+        if (e == null) {
+            mIsClosedFuture.set(null);
+        } else {
+            mIsClosedFuture.setException(e);
+        }
+    }
+
+    /**
+     * Extends the awaiting time.
+     */
+    public void extendAwaitSecond(int awaitExtendSecond) {
+        this.mAwaitExtendSecond = awaitExtendSecond;
+    }
+
+    /**
+     * Blocks until this receiver has closed (i.e. the state transition that this receiver is
+     * interested in has completed). Throws an exception on any error.
+     */
+    public void await(long timeout, TimeUnit timeUnit)
+            throws InterruptedException, ExecutionException, TimeoutException {
+        Log.v(TAG, this + " waiting on future for " + timeout + " " + timeUnit);
+        try {
+            mIsClosedFuture.get(timeout, timeUnit);
+        } catch (TimeoutException e) {
+            if (mAwaitExtendSecond <= 0) {
+                throw e;
+            }
+            Log.i(TAG, "Extend timeout for " + mAwaitExtendSecond + " seconds");
+            mIsClosedFuture.get(mAwaitExtendSecond, TimeUnit.SECONDS);
+        }
+    }
+}
diff --git a/nearby/tests/src/com/android/server/nearby/common/bluetooth/fastpair/AccountKeyGeneratorTest.java b/nearby/tests/src/com/android/server/nearby/common/bluetooth/fastpair/AccountKeyGeneratorTest.java
new file mode 100644
index 0000000..5084e7e
--- /dev/null
+++ b/nearby/tests/src/com/android/server/nearby/common/bluetooth/fastpair/AccountKeyGeneratorTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 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.common.bluetooth.fastpair;
+
+import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.AccountKeyCharacteristic;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Unit tests for {@link AccountKeyGenerator}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AccountKeyGeneratorTest {
+    @Test
+    public void createAccountKey() throws NoSuchAlgorithmException {
+        byte[] accountKey = AccountKeyGenerator.createAccountKey();
+
+        assertThat(accountKey).hasLength(16);
+        assertThat(accountKey[0]).isEqualTo(AccountKeyCharacteristic.TYPE);
+    }
+}
diff --git a/nearby/tests/src/com/android/server/nearby/common/bluetooth/fastpair/ConstantsTest.java b/nearby/tests/src/com/android/server/nearby/common/bluetooth/fastpair/ConstantsTest.java
new file mode 100644
index 0000000..a0933a9
--- /dev/null
+++ b/nearby/tests/src/com/android/server/nearby/common/bluetooth/fastpair/ConstantsTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 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.common.bluetooth.fastpair;
+
+import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothUuids.to128BitUuid;
+import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothUuids.toFastPair128BitUuid;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+
+import com.android.server.nearby.common.bluetooth.BluetoothException;
+import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic;
+import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection;
+
+import junit.framework.TestCase;
+
+import org.mockito.Mock;
+
+import java.util.UUID;
+
+/**
+ * Unit tests for {@link Constants}.
+ */
+public class ConstantsTest extends TestCase {
+
+    @Mock
+    private BluetoothGattConnection mMockGattConnection;
+
+    private static final UUID OLD_KEY_BASE_PAIRING_CHARACTERISTICS = to128BitUuid((short) 0x1234);
+
+    private static final UUID NEW_KEY_BASE_PAIRING_CHARACTERISTICS =
+            toFastPair128BitUuid((short) 0x1234);
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        initMocks(this);
+    }
+
+    public void test_getId_whenSupportNewCharacteristics() throws BluetoothException {
+        when(mMockGattConnection.getCharacteristic(any(UUID.class), any(UUID.class)))
+                .thenReturn(new BluetoothGattCharacteristic(NEW_KEY_BASE_PAIRING_CHARACTERISTICS, 0,
+                        0));
+
+        assertThat(KeyBasedPairingCharacteristic.getId(mMockGattConnection))
+                .isEqualTo(NEW_KEY_BASE_PAIRING_CHARACTERISTICS);
+    }
+
+    public void test_getId_whenNotSupportNewCharacteristics() throws BluetoothException {
+        // {@link BluetoothGattConnection#getCharacteristic(UUID, UUID)} throws {@link
+        // BluetoothException} if the characteristic not found .
+        when(mMockGattConnection.getCharacteristic(any(UUID.class), any(UUID.class)))
+                .thenThrow(new BluetoothException(""));
+
+        assertThat(KeyBasedPairingCharacteristic.getId(mMockGattConnection))
+                .isEqualTo(OLD_KEY_BASE_PAIRING_CHARACTERISTICS);
+    }
+}