Add Constants.

Test: unit test

Bug: 200231384
Change-Id: Ia74494523c0e5e32d096a447cdd0c876c4f11488
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/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);
+    }
+}