Merge "Add Preference builder based on GMSCore Log."
diff --git a/nearby/framework/api/system-current.txt b/nearby/framework/api/system-current.txt
index d802177..fe3cd98 100644
--- a/nearby/framework/api/system-current.txt
+++ b/nearby/framework/api/system-current.txt
@@ -1 +1,37 @@
 // Signature format: 2.0
+package android.nearby {
+
+  public abstract class FastPairDataProviderBase {
+    ctor public FastPairDataProviderBase(@NonNull String);
+    method @Nullable public final android.os.IBinder getBinder();
+    method public abstract void onLoadFastPairDeviceMetadata(@NonNull android.nearby.FastPairDataProviderBase.FastPairDeviceMetadataRequest, @NonNull android.nearby.FastPairDataProviderBase.FastPairDeviceMetadataCallback);
+  }
+
+  public static class FastPairDataProviderBase.FastPairDeviceMetadata {
+  }
+
+  public static final class FastPairDataProviderBase.FastPairDeviceMetadata.Builder {
+    ctor public FastPairDataProviderBase.FastPairDeviceMetadata.Builder();
+    method @NonNull public android.nearby.FastPairDataProviderBase.FastPairDeviceMetadata build();
+    method @NonNull public android.nearby.FastPairDataProviderBase.FastPairDeviceMetadata.Builder setAntiSpoofPublicKey(@NonNull byte[]);
+    method @NonNull public android.nearby.FastPairDataProviderBase.FastPairDeviceMetadata.Builder setBleTxPower(int);
+    method @NonNull public android.nearby.FastPairDataProviderBase.FastPairDeviceMetadata.Builder setDeviceType(int);
+    method @NonNull public android.nearby.FastPairDataProviderBase.FastPairDeviceMetadata.Builder setImage(@NonNull byte[]);
+    method @NonNull public android.nearby.FastPairDataProviderBase.FastPairDeviceMetadata.Builder setImageUrl(@NonNull String);
+    method @NonNull public android.nearby.FastPairDataProviderBase.FastPairDeviceMetadata.Builder setIntentUri(@NonNull String);
+    method @NonNull public android.nearby.FastPairDataProviderBase.FastPairDeviceMetadata.Builder setTriggerDistance(float);
+    method @NonNull public android.nearby.FastPairDataProviderBase.FastPairDeviceMetadata.Builder setTrueWirelessImageUriLeftBud(@NonNull byte[]);
+    method @NonNull public android.nearby.FastPairDataProviderBase.FastPairDeviceMetadata.Builder setTrueWirelessImageUrlCase(@NonNull byte[]);
+    method @NonNull public android.nearby.FastPairDataProviderBase.FastPairDeviceMetadata.Builder setTrueWirelessImageUrlRightBud(@NonNull byte[]);
+  }
+
+  public static interface FastPairDataProviderBase.FastPairDeviceMetadataCallback {
+    method public void onFastPairDeviceMetadataReceived(@NonNull android.nearby.FastPairDataProviderBase.FastPairDeviceMetadata);
+  }
+
+  public static class FastPairDataProviderBase.FastPairDeviceMetadataRequest {
+    method @Nullable public byte[] getModelId();
+  }
+
+}
+
diff --git a/nearby/framework/java/android/nearby/FastPairDataProviderBase.java b/nearby/framework/java/android/nearby/FastPairDataProviderBase.java
new file mode 100644
index 0000000..36c3be0
--- /dev/null
+++ b/nearby/framework/java/android/nearby/FastPairDataProviderBase.java
@@ -0,0 +1,280 @@
+/*
+ * 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 android.nearby;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Base class for fast pair providers outside the system server.
+ *
+ * Fast pair providers should be wrapped in a non-exported service which returns the result of
+ * {@link #getBinder()} from the service's {@link android.app.Service#onBind(Intent)} method. The
+ * service should not be exported so that components other than the system server cannot bind to it.
+ * Alternatively, the service may be guarded by a permission that only system server can obtain.
+ *
+ * <p>Fast Pair providers are identified by their UID / package name.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class FastPairDataProviderBase {
+
+    private final IBinder mBinder;
+    private final String mTag;
+
+    public FastPairDataProviderBase(@NonNull String tag) {
+        mBinder = new Service();
+        mTag = tag;
+    }
+
+    /**
+     * Callback to be invoked when a device metadata is loaded.
+     */
+    public interface FastPairDeviceMetadataCallback {
+
+        /**
+         * Should be invoked once the meta data is loaded.
+         */
+        void onFastPairDeviceMetadataReceived(@NonNull FastPairDeviceMetadata metadata);
+    }
+
+    /**
+     * Fullfills the load device metadata request by using callback to send back the serialized
+     * device meta data of the given modelId.
+     */
+    public abstract void onLoadFastPairDeviceMetadata(
+            @NonNull FastPairDeviceMetadataRequest request,
+            @NonNull FastPairDeviceMetadataCallback callback);
+
+    /**
+     * Returns the IBinder instance that should be returned from the {@link
+     * android.app.Service#onBind(Intent)} method of the wrapping service.
+     */
+    public final @Nullable IBinder getBinder() {
+        return mBinder;
+    }
+
+    /**
+     * Class for building FastPairDeviceMetadata.
+     */
+    public static class FastPairDeviceMetadata {
+
+        private FastPairDeviceMetadataParcel mMetadataParcel;
+
+        private FastPairDeviceMetadata(FastPairDeviceMetadataParcel metadataParcel) {
+            this.mMetadataParcel = metadataParcel;
+        }
+
+        /**
+         * Builder used to create FastPairDeviceMetadata.
+         */
+        public static final class Builder {
+
+            private final FastPairDeviceMetadataParcel mBuilderParcel;
+
+            /**
+             * Default constructor of Builder.
+             */
+            public Builder() {
+                mBuilderParcel = new FastPairDeviceMetadataParcel();
+                mBuilderParcel.imageUrl = null;
+                mBuilderParcel.intentUri = null;
+                mBuilderParcel.antiSpoofPublicKey = null;
+                mBuilderParcel.bleTxPower = 0;
+                mBuilderParcel.triggerDistance = 0;
+                mBuilderParcel.image = null;
+                mBuilderParcel.deviceType = 0;  // DEVICE_TYPE_UNSPECIFIED
+                mBuilderParcel.trueWirelessImageUrlLeftBud = null;
+                mBuilderParcel.trueWirelessImageUrlRightBud = null;
+                mBuilderParcel.trueWirelessImageUrlCase = null;
+            }
+
+            /**
+             * Set ImageUlr.
+             */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setImageUrl(@NonNull String imageUrl) {
+                mBuilderParcel.imageUrl = imageUrl;
+                return this;
+            }
+
+            /**
+             * Set IntentUri.
+             */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setIntentUri(@NonNull String intentUri) {
+                mBuilderParcel.intentUri = intentUri;
+                return this;
+            }
+
+            /**
+             * Set AntiSpoof public key.
+             */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setAntiSpoofPublicKey(@NonNull byte[] antiSpoofPublicKey) {
+                mBuilderParcel.antiSpoofPublicKey = antiSpoofPublicKey;
+                return this;
+            }
+
+            /**
+             * Set ble transmission power.
+             */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setBleTxPower(int bleTxPower) {
+                mBuilderParcel.bleTxPower = bleTxPower;
+                return this;
+            }
+
+            /**
+             * Set trigger distance.
+             */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setTriggerDistance(float triggerDistance) {
+                mBuilderParcel.triggerDistance = triggerDistance;
+                return this;
+            }
+
+            /**
+             * Set image.
+             */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setImage(@NonNull byte[] image) {
+                mBuilderParcel.image = image;
+                return this;
+            }
+
+            /**
+             * Set device type.
+             */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setDeviceType(int deviceType) {
+                mBuilderParcel.deviceType = deviceType;
+                return this;
+            }
+
+            /**
+             * Set true wireless image url for left bud.
+             */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setTrueWirelessImageUriLeftBud(
+                    @NonNull byte[] trueWirelessImageUrlLeftBud) {
+                mBuilderParcel.trueWirelessImageUrlLeftBud = trueWirelessImageUrlLeftBud;
+                return this;
+            }
+
+            /**
+             * Set true wireless image url for right bud.
+             */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setTrueWirelessImageUrlRightBud(
+                    @NonNull byte[] trueWirelessImageUrlRightBud) {
+                mBuilderParcel.trueWirelessImageUrlRightBud = trueWirelessImageUrlRightBud;
+                return this;
+            }
+
+            /**
+             * Set true wireless image url for right bud.
+             */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setTrueWirelessImageUrlCase(@NonNull byte[] trueWirelessImageUrlCase) {
+                mBuilderParcel.trueWirelessImageUrlCase = trueWirelessImageUrlCase;
+                return this;
+            }
+
+            /**
+             * Build {@link FastPairDeviceMetadataRequest} with the currently set configuration.
+             */
+            @NonNull
+            public FastPairDeviceMetadata build() {
+                return new FastPairDeviceMetadata(mBuilderParcel);
+            }
+        }
+    }
+
+    /**
+     * Class for reading FastPairDeviceMetadataRequest.
+     */
+    public static class FastPairDeviceMetadataRequest {
+
+        private final FastPairDeviceMetadataRequestParcel mMetadataRequestParcel;
+
+        private FastPairDeviceMetadataRequest(
+                final FastPairDeviceMetadataRequestParcel metaDataRequestParcel) {
+            this.mMetadataRequestParcel = metaDataRequestParcel;
+        }
+
+        public @Nullable byte[] getModelId() {
+            return this.mMetadataRequestParcel.modelId;
+        }
+    }
+
+    /**
+     * Call back class that sends back data.
+     */
+    private final class Callback implements FastPairDeviceMetadataCallback {
+
+        private IFastPairDataCallback mCallback;
+
+        private Callback(IFastPairDataCallback callback) {
+            mCallback = callback;
+        }
+
+        /**
+         * Sends back the serialized device meta data.
+         */
+        @Override
+        public void onFastPairDeviceMetadataReceived(@NonNull FastPairDeviceMetadata metadata) {
+            try {
+                mCallback.onFastPairDeviceMetadataReceived(metadata.mMetadataParcel);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            } catch (RuntimeException e) {
+                Log.w(mTag, e);
+            }
+        }
+    }
+
+    private final class Service extends IFastPairDataProvider.Stub {
+
+        Service() {
+        }
+
+        @Override
+        public void loadFastPairDeviceMetadata(
+                @NonNull FastPairDeviceMetadataRequestParcel requestParcel,
+                IFastPairDataCallback callback) {
+            onLoadFastPairDeviceMetadata(new FastPairDeviceMetadataRequest(requestParcel),
+                    new Callback(callback));
+        }
+    }
+}
diff --git a/nearby/framework/java/android/nearby/FastPairDeviceMetadataParcel.aidl b/nearby/framework/java/android/nearby/FastPairDeviceMetadataParcel.aidl
new file mode 100644
index 0000000..6fafa66
--- /dev/null
+++ b/nearby/framework/java/android/nearby/FastPairDeviceMetadataParcel.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nearby;
+
+/**
+ * Configuration details for requesting tethering.
+ * @hide
+ */
+parcelable FastPairDeviceMetadataParcel {
+
+    // The image to show on the notification.
+    String imageUrl;
+
+    // The intent that will be launched via the notification.
+    String intentUri;
+
+    // Anti spoof public key;
+    byte[] antiSpoofPublicKey;
+
+    // The transmit power of the device's BLE chip.
+    int bleTxPower;
+
+    // The distance that the device must be within to show a notification.
+    // If no distance is set, we default to 0.6 meters. Only Nearby admins can
+    // change this.
+    float triggerDistance;
+
+    // The image icon that shows in the notification.
+    byte[] image;
+
+    int deviceType;
+
+    // The image for device with device type "true wireless".
+    byte[] trueWirelessImageUrlLeftBud;
+    byte[] trueWirelessImageUrlRightBud;
+    byte[] trueWirelessImageUrlCase;
+}
\ No newline at end of file
diff --git a/nearby/framework/java/android/nearby/FastPairDeviceMetadataRequestParcel.aidl b/nearby/framework/java/android/nearby/FastPairDeviceMetadataRequestParcel.aidl
new file mode 100644
index 0000000..2799e5d
--- /dev/null
+++ b/nearby/framework/java/android/nearby/FastPairDeviceMetadataRequestParcel.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nearby;
+
+/**
+ * Configuration details for requesting tethering.
+ * @hide
+ */
+parcelable FastPairDeviceMetadataRequestParcel {
+    byte[] modelId;
+}
\ No newline at end of file
diff --git a/nearby/framework/java/android/nearby/IFastPairDataCallback.aidl b/nearby/framework/java/android/nearby/IFastPairDataCallback.aidl
new file mode 100644
index 0000000..e4a0348
--- /dev/null
+++ b/nearby/framework/java/android/nearby/IFastPairDataCallback.aidl
@@ -0,0 +1,26 @@
+// 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 android.nearby;
+
+import android.nearby.FastPairDeviceMetadataParcel;
+
+/**
+  * Provides callback interface for OEMs to send FastPair data back.
+  *
+  * {@hide}
+  */
+interface IFastPairDataCallback {
+     void onFastPairDeviceMetadataReceived(in FastPairDeviceMetadataParcel metadata);
+ }
diff --git a/nearby/framework/java/android/nearby/IFastPairDataProvider.aidl b/nearby/framework/java/android/nearby/IFastPairDataProvider.aidl
new file mode 100644
index 0000000..c3c04d6
--- /dev/null
+++ b/nearby/framework/java/android/nearby/IFastPairDataProvider.aidl
@@ -0,0 +1,28 @@
+// 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 android.nearby;
+
+import android.nearby.FastPairDeviceMetadataRequestParcel;
+import android.nearby.IFastPairDataCallback;
+
+/**
+ * Interface for communicating with the fast pair providers.
+ *
+ * @hide
+ */
+oneway interface IFastPairDataProvider {
+    void loadFastPairDeviceMetadata(in FastPairDeviceMetadataRequestParcel modelId,
+        in IFastPairDataCallback callback);
+}
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
index d3eb388..cfecd2f 100644
--- 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
@@ -28,6 +28,8 @@
 import android.bluetooth.BluetoothHeadset;
 import android.util.Log;
 
+import androidx.annotation.IntDef;
+
 import com.android.server.nearby.common.bluetooth.BluetoothException;
 import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection;
 
@@ -35,6 +37,8 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.primitives.Shorts;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.security.GeneralSecurityException;
 import java.util.Random;
@@ -199,67 +203,57 @@
             /**
              * Enumerates all flags of key-based pairing request.
              */
-            // TODO(b/201673262): Convert enum to byte.
-            public enum KeyBasedPairingRequestFlag {
+            @Retention(RetentionPolicy.SOURCE)
+            @IntDef(
+                    value = {
+                            KeyBasedPairingRequestFlag.REQUEST_DISCOVERABLE,
+                            KeyBasedPairingRequestFlag.PROVIDER_INITIATES_BONDING,
+                            KeyBasedPairingRequestFlag.REQUEST_DEVICE_NAME,
+                            KeyBasedPairingRequestFlag.REQUEST_RETROACTIVE_PAIR,
+                    })
+            public @interface KeyBasedPairingRequestFlag {
                 /**
                  * The bit indicating that the Fast Pair device should temporarily become
                  * discoverable.
                  */
-                REQUEST_DISCOVERABLE((byte) (1 << 7)),
+                int 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)),
+                int PROVIDER_INITIATES_BONDING = (byte) (1 << 6);
                 /**
                  * The bit indicating that Seeker requests Provider shall return the existing name.
                  */
-                REQUEST_DEVICE_NAME((byte) (1 << 5)),
+                int 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;
-                }
+                int REQUEST_RETROACTIVE_PAIR = (byte) (1 << 4);
             }
 
             /**
              * Enumerates all flags of action over BLE request, see Fast Pair spec for details.
              */
-            // TODO(b/201673262): Convert enum to byte.
-            public enum ActionOverBleFlag {
+            @IntDef(
+                    value = {
+                            ActionOverBleFlag.DEVICE_ACTION,
+                            ActionOverBleFlag.ADDITIONAL_DATA_CHARACTERISTIC,
+                    })
+            public @interface ActionOverBleFlag {
                 /**
                  * The bit indicating that the handshaking is for Device Action.
                  */
-                DEVICE_ACTION((byte) (1 << 7)),
+                int 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;
-                }
+                int ADDITIONAL_DATA_CHARACTERISTIC = (byte) (1 << 6);
             }
 
+
             /**
              * Constants related to the decrypted response sent back in a notify.
              */
@@ -308,28 +302,26 @@
             /**
              * The type of the Passkey Block message.
              */
-            // TODO(b/201673262): Convert enum to byte.
-            public enum Type {
+            @IntDef(
+                    value = {
+                            Type.SEEKER,
+                            Type.PROVIDER,
+                    })
+            public @interface Type {
                 /**
                  * Seeker's Passkey.
                  */
-                SEEKER((byte) 0x02),
+                int SEEKER = (byte) 0x02;
                 /**
                  * Provider's Passkey.
                  */
-                PROVIDER((byte) 0x03);
-
-                private final byte mValue;
-
-                Type(byte value) {
-                    this.mValue = value;
-                }
+                int PROVIDER = (byte) 0x03;
             }
 
             /**
              * Constructs the encrypted value to write to the characteristic.
              */
-            public static byte[] encrypt(Type type, byte[] secret, int passkey)
+            public static byte[] encrypt(@Type int type, byte[] secret, int passkey)
                     throws GeneralSecurityException {
                 Preconditions.checkArgument(
                         0 < passkey && passkey < /*2^24=*/ 16777216,
@@ -342,19 +334,20 @@
                                 - passkeyBytes.length];
                 new Random().nextBytes(salt);
                 return AesEcbSingleBlockEncryption.encrypt(
-                        secret, concat(new byte[]{type.mValue}, passkeyBytes, salt));
+                        secret, concat(new byte[]{(byte) type}, passkeyBytes, salt));
             }
 
             /**
              * Extracts the passkey from the encrypted characteristic value.
              */
-            public static int decrypt(Type type, byte[] secret, byte[] passkeyCharacteristicValue)
+            public static int decrypt(@Type int type, byte[] secret,
+                    byte[] passkeyCharacteristicValue)
                     throws GeneralSecurityException {
                 byte[] decrypted = AesEcbSingleBlockEncryption
                         .decrypt(secret, passkeyCharacteristicValue);
-                if (decrypted[0] != type.mValue) {
+                if (decrypted[0] != (byte) type) {
                     throw new GeneralSecurityException(
-                            "Wrong Passkey Block type (expected " + type.mValue + ", got "
+                            "Wrong Passkey Block type (expected " + type + ", got "
                                     + decrypted[0] + ")");
                 }
                 return ByteBuffer.allocate(4)
@@ -470,36 +463,18 @@
             /**
              * Enumerates all types of additional data.
              */
-            // TODO(b/201673262): Convert enum to byte.
-            public enum AdditionalDataType {
+            @Retention(RetentionPolicy.SOURCE)
+            @IntDef(
+                    value = {
+                            AdditionalDataType.PERSONALIZED_NAME,
+                            AdditionalDataType.UNKNOWN,
+                    })
+            public @interface 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() {
+                int PERSONALIZED_NAME = (byte) 0x01;
+                int UNKNOWN = (byte) 0x00; // and all others.
             }
         }
 
@@ -533,45 +508,49 @@
             /**
              * 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;
-                }
+            /** Fast Pair Bond State. */
+            @Retention(RetentionPolicy.SOURCE)
+            @IntDef(
+                    value = {
+                            BeaconActionType.READ_BEACON_PARAMETERS,
+                            BeaconActionType.READ_PROVISIONING_STATE,
+                            BeaconActionType.SET_EPHEMERAL_IDENTITY_KEY,
+                            BeaconActionType.CLEAR_EPHEMERAL_IDENTITY_KEY,
+                            BeaconActionType.READ_EPHEMERAL_IDENTITY_KEY,
+                            BeaconActionType.RING,
+                            BeaconActionType.READ_RINGING_STATE,
+                            BeaconActionType.UNKNOWN,
+                    })
+            public @interface BeaconActionType {
+                int READ_BEACON_PARAMETERS = (byte) 0x00;
+                int READ_PROVISIONING_STATE = (byte) 0x01;
+                int SET_EPHEMERAL_IDENTITY_KEY = (byte) 0x02;
+                int CLEAR_EPHEMERAL_IDENTITY_KEY = (byte) 0x03;
+                int READ_EPHEMERAL_IDENTITY_KEY = (byte) 0x04;
+                int RING = (byte) 0x05;
+                int READ_RINGING_STATE = (byte) 0x06;
+                int UNKNOWN = (byte) 0xFF; // and all others
             }
 
-            private BeaconActionsCharacteristic() {
+            /** Converts value to enum. */
+            public static @BeaconActionType int valueOf(byte value) {
+                switch(value) {
+                    case BeaconActionType.READ_BEACON_PARAMETERS:
+                    case BeaconActionType.READ_PROVISIONING_STATE:
+                    case BeaconActionType.SET_EPHEMERAL_IDENTITY_KEY:
+                    case BeaconActionType.CLEAR_EPHEMERAL_IDENTITY_KEY:
+                    case BeaconActionType.READ_EPHEMERAL_IDENTITY_KEY:
+                    case BeaconActionType.RING:
+                    case BeaconActionType.READ_RINGING_STATE:
+                    case BeaconActionType.UNKNOWN:
+                        return value;
+                    default:
+                        return BeaconActionType.UNKNOWN;
+                }
             }
         }
 
+
         /**
          * Characteristic to read for checking firmware version. 0X2A26 is assigned number from
          * bluetooth SIG website.
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnection.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnection.java
index b3be779..440d126 100644
--- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnection.java
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnection.java
@@ -45,6 +45,7 @@
 import android.util.Log;
 
 import androidx.annotation.GuardedBy;
+import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.annotation.WorkerThread;
@@ -80,6 +81,8 @@
 import com.google.common.base.Preconditions;
 import com.google.common.primitives.Shorts;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteOrder;
 import java.security.GeneralSecurityException;
 import java.security.NoSuchAlgorithmException;
@@ -216,32 +219,46 @@
                     },
                     REQUESTED_SERVICES_LTV);
 
-    // TODO(b/201673262): remove Java enum usage.
-    private enum ResultCode {
-        UNKNOWN((byte) 0xFF),
-        SUCCESS((byte) 0x00),
-        OP_CODE_NOT_SUPPORTED((byte) 0x01),
-        INVALID_PARAMETER((byte) 0x02),
-        UNSUPPORTED_ORGANIZATION_ID((byte) 0x03),
-        OPERATION_FAILED((byte) 0x04);
+    /**
+     * Operation Result Code.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            value = {
+                    ResultCode.UNKNOWN,
+                    ResultCode.SUCCESS,
+                    ResultCode.OP_CODE_NOT_SUPPORTED,
+                    ResultCode.INVALID_PARAMETER,
+                    ResultCode.UNSUPPORTED_ORGANIZATION_ID,
+                    ResultCode.OPERATION_FAILED,
+            })
 
-        private final byte mByteValue;
+    public @interface ResultCode {
 
-        ResultCode(byte byteValue) {
-            this.mByteValue = byteValue;
-        }
+        int UNKNOWN = (byte) 0xFF;
+        int SUCCESS = (byte) 0x00;
+        int OP_CODE_NOT_SUPPORTED = (byte) 0x01;
+        int INVALID_PARAMETER = (byte) 0x02;
+        int UNSUPPORTED_ORGANIZATION_ID = (byte) 0x03;
+        int OPERATION_FAILED = (byte) 0x04;
+    }
 
-        private static ResultCode fromTdsControlPointIndication(byte[] response) {
-            return response == null || response.length < 2 ? UNKNOWN : from(response[1]);
-        }
 
-        private static ResultCode from(byte byteValue) {
-            for (ResultCode resultCode : ResultCode.values()) {
-                if (resultCode.mByteValue == byteValue) {
-                    return resultCode;
-                }
-            }
-            return UNKNOWN;
+    private static @ResultCode int fromTdsControlPointIndication(byte[] response) {
+        return response == null || response.length < 2 ? ResultCode.UNKNOWN : from(response[1]);
+    }
+
+    private static @ResultCode int from(byte byteValue) {
+        switch (byteValue) {
+            case ResultCode.UNKNOWN:
+            case ResultCode.SUCCESS:
+            case ResultCode.OP_CODE_NOT_SUPPORTED:
+            case ResultCode.INVALID_PARAMETER:
+            case ResultCode.UNSUPPORTED_ORGANIZATION_ID:
+            case ResultCode.OPERATION_FAILED:
+                return byteValue;
+            default:
+                return ResultCode.UNKNOWN;
         }
     }
 
@@ -1492,7 +1509,7 @@
      * secret. The given key should be the account key.
      */
     private SharedSecret handshakeForActionOverBle(byte[] key,
-            AdditionalDataType additionalDataType)
+            @AdditionalDataType int additionalDataType)
             throws InterruptedException, ExecutionException, TimeoutException, BluetoothException,
             GeneralSecurityException, PairingException {
         HandshakeHandler handshakeHandler = prepareForHandshake();
@@ -1717,7 +1734,7 @@
         byte[] response =
                 changeObserver.waitForUpdate(
                         TimeUnit.SECONDS.toMillis(mPreferences.getGattOperationTimeoutSeconds()));
-        ResultCode resultCode = ResultCode.fromTdsControlPointIndication(response);
+        @ResultCode int resultCode = fromTdsControlPointIndication(response);
         if (resultCode != ResultCode.SUCCESS) {
             throw new TdsException(
                     BrEdrHandoverErrorCode.CONTROL_POINT_RESULT_CODE_NOT_SUCCESS,
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/HandshakeHandler.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/HandshakeHandler.java
index b1918b5..984133b 100644
--- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/HandshakeHandler.java
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/HandshakeHandler.java
@@ -393,8 +393,8 @@
             /**
              * Adds flags without changing other flags.
              */
-            public Builder addFlag(KeyBasedPairingRequestFlag flag) {
-                this.mFlags |= flag.getValue();
+            public Builder addFlag(@KeyBasedPairingRequestFlag int flag) {
+                this.mFlags |= (byte) flag;
                 return this;
             }
 
@@ -448,7 +448,7 @@
                             String.format(
                                     "type (%02X), flag (%02X)", rawMessage[TYPE_INDEX],
                                     rawMessage[FLAGS_INDEX]));
-            if ((mFlags & DEVICE_ACTION.getValue()) != 0) {
+            if ((mFlags & (byte) DEVICE_ACTION) != 0) {
                 rawMessage[EVENT_GROUP_INDEX] = mEventGroup;
                 rawMessage[EVENT_CODE_INDEX] = mEventCode;
 
@@ -470,7 +470,7 @@
                                 rawMessage[EVENT_CODE_INDEX],
                                 rawMessage[EVENT_ADDITIONAL_DATA_LENGTH_INDEX]));
             }
-            if ((mFlags & ADDITIONAL_DATA_CHARACTERISTIC.getValue()) != 0) {
+            if ((mFlags & (byte) ADDITIONAL_DATA_CHARACTERISTIC) != 0) {
                 rawMessage[ADDITIONAL_DATA_TYPE_INDEX] = mAdditionalDataType;
                 stringBuilder.append(
                         String.format(", data id(%02X)", rawMessage[ADDITIONAL_DATA_TYPE_INDEX]));
@@ -496,8 +496,8 @@
             /**
              * Adds flag without changing other flags.
              */
-            public Builder addFlag(ActionOverBleFlag flag) {
-                this.mFlags |= flag.getValue();
+            public Builder addFlag(@ActionOverBleFlag int flag) {
+                this.mFlags |= (byte) flag;
                 return this;
             }
 
@@ -505,7 +505,7 @@
              * Set event group and event code.
              */
             public Builder setEvent(int eventGroup, int eventCode) {
-                this.mFlags |= DEVICE_ACTION.getValue();
+                this.mFlags |= (byte) DEVICE_ACTION;
                 this.mEventGroup = (byte) (eventGroup & 0xFF);
                 this.mEventCode = (byte) (eventCode & 0xFF);
                 return this;
@@ -522,9 +522,9 @@
             /**
              * Set event additional data type.
              */
-            public Builder setAdditionalDataType(AdditionalDataType additionalDataType) {
-                this.mFlags |= ADDITIONAL_DATA_CHARACTERISTIC.getValue();
-                this.mAdditionalDataType = additionalDataType.getValue();
+            public Builder setAdditionalDataType(@AdditionalDataType int additionalDataType) {
+                this.mFlags |= (byte) ADDITIONAL_DATA_CHARACTERISTIC;
+                this.mAdditionalDataType = (byte) additionalDataType;
                 return this;
             }
 
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/PairingProgressListener.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/PairingProgressListener.java
index 8f8e498..270cb42 100644
--- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/PairingProgressListener.java
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/PairingProgressListener.java
@@ -16,24 +16,46 @@
 
 package com.android.server.nearby.common.bluetooth.fastpair;
 
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /** Callback interface for pairing progress. */
 public interface PairingProgressListener {
-    /** Enum for pairing events. */
-    enum PairingEvent {
-        START,
-        SUCCESS,
-        FAILED,
-        UNKNOWN;
 
-        public static PairingEvent fromOrdinal(int ordinal) {
-            PairingEvent[] values = PairingEvent.values();
-            if (ordinal < 0 || ordinal >= values.length) {
-                return UNKNOWN;
-            }
-            return values[ordinal];
+    /** Fast Pair Bond State. */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            value = {
+                    PairingEvent.START,
+                    PairingEvent.SUCCESS,
+                    PairingEvent.FAILED,
+                    PairingEvent.UNKNOWN,
+            })
+    public @interface PairingEvent {
+        int START = 0;
+        int SUCCESS = 1;
+        int FAILED = 2;
+        int UNKNOWN = 3;
+    }
+
+    /** Returns enum based on the ordinal index. */
+    static @PairingEvent int fromOrdinal(int ordinal) {
+        switch (ordinal) {
+            case 0:
+                return PairingEvent.START;
+            case 1:
+                return PairingEvent.SUCCESS;
+            case 2:
+                return PairingEvent.FAILED;
+            case 3:
+                return PairingEvent.UNKNOWN;
+            default:
+                return PairingEvent.UNKNOWN;
         }
     }
 
     /** Callback function upon pairing progress update. */
-    void onPairingProgressUpdating(PairingEvent event, String message);
+    void onPairingProgressUpdating(@PairingEvent int event, String message);
 }
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/gatt/BluetoothGattHelper.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/gatt/BluetoothGattHelper.java
index f17094d..04a9a58 100644
--- a/nearby/service/java/com/android/server/nearby/common/bluetooth/gatt/BluetoothGattHelper.java
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/gatt/BluetoothGattHelper.java
@@ -24,6 +24,7 @@
 import android.os.ParcelUuid;
 import android.util.Log;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.server.nearby.common.bluetooth.BluetoothException;
@@ -40,6 +41,8 @@
 
 import com.google.common.base.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.Locale;
 import java.util.Objects;
@@ -67,20 +70,37 @@
     /**
      * BT operation types that can be in flight.
      */
-    public enum OperationType {
-        SCAN,
-        CONNECT,
-        DISCOVER_SERVICES,
-        DISCOVER_SERVICES_INTERNAL,
-        NOTIFICATION_CHANGE,
-        READ_CHARACTERISTIC,
-        WRITE_CHARACTERISTIC,
-        READ_DESCRIPTOR,
-        WRITE_DESCRIPTOR,
-        READ_RSSI,
-        WRITE_RELIABLE,
-        CHANGE_MTU,
-        DISCONNECT
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            value = {
+                    OperationType.SCAN,
+                    OperationType.CONNECT,
+                    OperationType.DISCOVER_SERVICES,
+                    OperationType.DISCOVER_SERVICES_INTERNAL,
+                    OperationType.NOTIFICATION_CHANGE,
+                    OperationType.READ_CHARACTERISTIC,
+                    OperationType.WRITE_CHARACTERISTIC,
+                    OperationType.READ_DESCRIPTOR,
+                    OperationType.WRITE_DESCRIPTOR,
+                    OperationType.READ_RSSI,
+                    OperationType.WRITE_RELIABLE,
+                    OperationType.CHANGE_MTU,
+                    OperationType.DISCONNECT,
+            })
+    public @interface OperationType {
+        int SCAN = 0;
+        int CONNECT = 1;
+        int DISCOVER_SERVICES = 2;
+        int DISCOVER_SERVICES_INTERNAL = 3;
+        int NOTIFICATION_CHANGE = 4;
+        int READ_CHARACTERISTIC = 5;
+        int WRITE_CHARACTERISTIC = 6;
+        int READ_DESCRIPTOR = 7;
+        int WRITE_DESCRIPTOR = 8;
+        int READ_RSSI = 9;
+        int WRITE_RELIABLE = 10;
+        int CHANGE_MTU = 11;
+        int DISCONNECT = 12;
     }
 
     @VisibleForTesting
diff --git a/nearby/service/proto/src/fastpair/cache.proto b/nearby/service/proto/src/fastpair/cache.proto
index 4872ae6..bf80b58 100644
--- a/nearby/service/proto/src/fastpair/cache.proto
+++ b/nearby/service/proto/src/fastpair/cache.proto
@@ -1,6 +1,7 @@
 syntax = "proto3";
 package service.proto;
 import "src/fastpair/rpcs.proto";
+import "src/fastpair/fast_pair_string.proto";
 
 // db information for Fast Pair that gets from server.
 message ServerResponseDbItem {
@@ -17,5 +18,289 @@
 
   // Whether the item in the cache is expirable or not (when offline mode this
   // will be false).
-   bool expirable = 4;
+  bool expirable = 4;
+}
+
+
+// Client side scan result.
+message StoredScanResult {
+  // REQUIRED
+  // Unique ID generated based on scan result
+  string id = 1;
+
+  // REQUIRED
+  NearbyType type = 2;
+
+  // REQUIRED
+  // The most recent all upper case mac associated with this item.
+  // (Mac-to-DiscoveryItem is a many-to-many relationship)
+  string mac_address = 4;
+
+  // Beacon's RSSI value
+  int32 rssi = 10;
+
+  // Beacon's tx power
+  int32 tx_power = 11;
+
+  // The mac address encoded in beacon advertisement. Currently only used by
+  // chromecast.
+  string device_setup_mac = 12;
+
+  // Uptime of the device in minutes. Stops incrementing at 255.
+  int32 uptime_minutes = 13;
+
+  // REQUIRED
+  // Client timestamp when the beacon was first observed in BLE scan.
+  int64 first_observation_timestamp_millis = 14;
+
+  // REQUIRED
+  // Client timestamp when the beacon was last observed in BLE scan.
+  int64 last_observation_timestamp_millis = 15;
+
+  // Deprecated fields.
+  reserved 3, 5, 6, 7, 8, 9;
+}
+
+
+// Data for a DiscoveryItem created from server response and client scan result.
+// Only caching original data from scan result, server response, timestamps
+// and user actions. Do not save generated data in this object.
+// Next ID: 50
+message StoredDiscoveryItem {
+  enum State {
+    // Default unknown state.
+    STATE_UNKNOWN = 0;
+
+    // The item is normal.
+    STATE_ENABLED = 1;
+
+    // The item has been muted by user.
+    STATE_MUTED = 2;
+
+    // The item has been disabled by us (likely temporarily).
+    STATE_DISABLED_BY_SYSTEM = 3;
+  }
+
+  // The status of the item.
+  // TODO(b/204409421) remove enum
+  enum DebugMessageCategory {
+    // Default unknown state.
+    STATUS_UNKNOWN = 0;
+
+    // The item is valid and visible in notification.
+    STATUS_VALID_NOTIFICATION = 1;
+
+    // The item made it to list but not to notification.
+    STATUS_VALID_LIST_VIEW = 2;
+
+    // The item is filtered out on client. Never made it to list view.
+    STATUS_DISABLED_BY_CLIENT = 3;
+
+    // The item is filtered out by server. Never made it to client.
+    STATUS_DISABLED_BY_SERVER = 4;
+  }
+
+  enum ExperienceType {
+    EXPERIENCE_UNKNOWN = 0;
+    EXPERIENCE_GOOD = 1;
+    EXPERIENCE_BAD = 2;
+  }
+
+  // REQUIRED
+  // Offline item: unique ID generated on client.
+  // Online item: unique ID generated on server.
+  string id = 1;
+
+  // REQUIRED
+  NearbyType type = 2;
+
+  // REQUIRED
+  // The most recent all upper case mac associated with this item.
+  // (Mac-to-DiscoveryItem is a many-to-many relationship)
+  string mac_address = 4;
+
+  // REQUIRED
+  string action_url = 5;
+
+  // The bluetooth device name from advertisment
+  string device_name = 6;
+
+  // REQUIRED
+  // Item's title
+  string title = 7;
+
+  // Item's description.
+  string description = 8;
+
+  // The URL for display
+  string display_url = 9;
+
+  // REQUIRED
+  // Client timestamp when the beacon was last observed in BLE scan.
+  int64 last_observation_timestamp_millis = 10;
+
+  // REQUIRED
+  // Client timestamp when the beacon was first observed in BLE scan.
+  int64 first_observation_timestamp_millis = 11;
+
+  // REQUIRED
+  // Item's current state. e.g. if the item is blocked.
+  State state = 17;
+
+  // The resolved url type for the action_url.
+  ResolvedUrlType action_url_type = 19;
+
+  // The timestamp when the user is redirected to Play Store after clicking on
+  // the item.
+  int64 pending_app_install_timestamp_millis = 20;
+
+  // Beacon's RSSI value
+  int32 rssi = 22;
+
+  // Beacon's tx power
+  int32 tx_power = 23;
+
+  // Human readable name of the app designated to open the uri
+  // Used in the second line of the notification, "Open in {} app"
+  string app_name = 25;
+
+  // ID used for associating several DiscoveryItems.  These items may be
+  // visually displayed together.
+  string group_id = 26;
+
+  // The timestamp when the attachment was created on PBS server. In case there
+  // are duplicate
+  // items with the same scanId/groupID, only show the one with the latest
+  // timestamp.
+  int64 attachment_creation_sec = 28;
+
+  // Whether the attachment is created in debug namespace
+  DiscoveryAttachmentType attachment_type = 29;
+
+  // Package name of the App that owns this item.
+  string package_name = 30;
+
+  // The average star rating of the app.
+  float star_rating = 31;
+
+  // The "feature" graphic image url used for large sized list view entries.
+  string feature_graphic_url = 32;
+
+  // TriggerId identifies the trigger/beacon that is attached with a message.
+  // It's generated from server for online messages to synchronize formatting
+  // across client versions.
+  // Example:
+  // * BLE_UID: 3||deadbeef
+  // * BLE_URL: http://trigger.id
+  // See go/discovery-store-message-and-trigger-id for more details.
+  string trigger_id = 34;
+
+  // Bytes of item icon in PNG format displayed in Discovery item list.
+  bytes icon_png = 36;
+
+  // A FIFE URL of the item icon displayed in Discovery item list.
+  string icon_fife_url = 49;
+
+  // Message written to bugreport for 3P developers.(No sensitive info)
+  // null if the item is valid
+  string debug_message = 37;
+
+  // Weather the item is filtered out on server.
+  DebugMessageCategory debug_category = 38;
+
+  // Client timestamp when the trigger (e.g. beacon) was last lost (e.g. when
+  // Messages told us the beacon's no longer nearby).
+  int64 lost_millis = 41;
+
+  // The kind of expereince the user last had with this (e.g. if they dismissed
+  // the notification, that's bad; but if they tapped it, that's good).
+  ExperienceType last_user_experience = 42;
+
+  // The most recent BLE advertisement related to this item.
+  bytes ble_record_bytes = 43;
+
+  // An ID generated on the server to uniquely identify content.
+  string entity_id = 44;
+
+  // See equivalent field in NearbyItem.
+  bytes authentication_public_key_secp256r1 = 45;
+
+  // See equivalent field in NearbyItem.
+  FastPairInformation fast_pair_information = 46;
+
+  // Companion app detail.
+  CompanionAppDetails companion_detail = 47;
+
+  // Fast pair strings
+  FastPairStrings fast_pair_strings = 48;
+
+  // Deprecated fields.
+  reserved 3, 12, 13, 14, 15, 16, 18, 21, 24, 27, 33, 35, 39, 40;
+}
+enum ResolvedUrlType {
+  RESOLVED_URL_TYPE_UNKNOWN = 0;
+
+  // The url is resolved to a web page that is not a play store app.
+  // This can be considered as the default resolved type when it's
+  // not the other specific types.
+  WEBPAGE = 1;
+
+  // The url is resolved to the Google Play store app
+  // ie. play.google.com/store
+  APP = 2;
+}
+enum DiscoveryAttachmentType {
+  DISCOVERY_ATTACHMENT_TYPE_UNKNOWN = 0;
+
+  // The attachment is posted in the prod namespace (without "-debug")
+  DISCOVERY_ATTACHMENT_TYPE_NORMAL = 1;
+
+  // The attachment is posted in the debug namespace (with "-debug")
+  DISCOVERY_ATTACHMENT_TYPE_DEBUG = 2;
+}
+// Additional information relevant only for Fast Pair devices.
+message FastPairInformation {
+  // When true, Fast Pair will only create a bond with the device and not
+  // attempt to connect any profiles (for example, A2DP or HFP).
+  bool data_only_connection = 1;
+
+  // Additional images that are attached specifically for true wireless Fast
+  // Pair devices.
+  TrueWirelessHeadsetImages true_wireless_images = 3;
+
+  // When true, this device can support assistant function.
+  bool assistant_supported = 4;
+
+  // Features supported by the Fast Pair device.
+  repeated FastPairFeature features = 5;
+
+  // Optional, the name of the company producing this Fast Pair device.
+  string company_name = 6;
+
+  // Optional, the type of device.
+  DeviceType device_type = 7;
+
+  reserved 2;
+}
+
+
+enum NearbyType {
+  NEARBY_TYPE_UNKNOWN = 0;
+  // Proximity Beacon Service (PBS). This is the only type of nearbyItems which
+  // can be customized by 3p and therefore the intents passed should not be
+  // completely trusted. Deprecated already.
+  NEARBY_PROXIMITY_BEACON = 1;
+  // Physical Web URL beacon. Deprecated already.
+  NEARBY_PHYSICAL_WEB = 2;
+  // Chromecast beacon. Used on client-side only.
+  NEARBY_CHROMECAST = 3;
+  // Wear beacon. Used on client-side only.
+  NEARBY_WEAR = 4;
+  // A device (e.g. a Magic Pair device that needs to be set up). The special-
+  // case devices above (e.g. ChromeCast, Wear) might migrate to this type.
+  NEARBY_DEVICE = 6;
+  // Popular apps/urls based on user's current geo-location.
+  NEARBY_POPULAR_HERE = 7;
+
+  reserved 5;
 }
diff --git a/nearby/service/proto/src/fastpair/fast_pair_string.proto b/nearby/service/proto/src/fastpair/fast_pair_string.proto
new file mode 100644
index 0000000..6dfc19a
--- /dev/null
+++ b/nearby/service/proto/src/fastpair/fast_pair_string.proto
@@ -0,0 +1,65 @@
+syntax = "proto2";
+
+package service.proto;
+
+message FastPairStrings {
+  // Required for initial pairing, used when there is a Google account on the
+  // device
+  optional string tap_to_pair_with_account = 1;
+
+  // Required for initial pairing, used when there is no Google account on the
+  // device
+  optional string tap_to_pair_without_account = 2;
+
+  // Description for initial pairing
+  optional string initial_pairing_description = 3;
+
+  // Description after successfully paired the device with companion app
+  // installed
+  optional string pairing_finished_companion_app_installed = 4;
+
+  // Description after successfully paired the device with companion app not
+  // installed
+  optional string pairing_finished_companion_app_not_installed = 5;
+
+  // Description when phone found the device that associates with user's account
+  // before remind user to pair with new device.
+  optional string subsequent_pairing_description = 6;
+
+  // Description when fast pair finds the user paired with device manually
+  // reminds user to opt the device into cloud.
+  optional string retroactive_pairing_description = 7;
+
+  // Description when user click setup device while device is still pairing
+  optional string wait_app_launch_description = 8;
+
+  // Description when user fail to pair with device
+  optional string pairing_fail_description = 9;
+
+  // Title to ask the user to confirm the pin code.
+  optional string confirm_pin_title = 10;
+
+  // Description to ask the user to confirm the pin code.
+  optional string confirm_pin_description = 11;
+
+  // The title of the UI to ask the user to confirm to sync contacts.
+  optional string sync_contacts_title = 12;
+
+  // The description of the UI to ask the user to confirm to sync contacts.
+  optional string sync_contacts_description = 13;
+
+  // The title of the UI to ask the user to confirm to sync SMS.
+  optional string sync_sms_title = 14;
+
+  // The description of the UI to ask the user to confirm to sync SMS.
+  optional string sync_sms_description = 15;
+
+  // The description for half sheet to ask user to setup google assistant.
+  optional string assistant_half_sheet_description = 16;
+
+  // The description for notification to ask user to setup google assistant.
+  optional string assistant_notification_description = 17;
+
+  // Description of the connect device action on TV, when user is not logged in.
+  optional string fast_pair_tv_connect_device_no_account_description = 18;
+}
diff --git a/nearby/service/proto/src/fastpair/rpcs.proto b/nearby/service/proto/src/fastpair/rpcs.proto
index e1ab344..b684b48 100644
--- a/nearby/service/proto/src/fastpair/rpcs.proto
+++ b/nearby/service/proto/src/fastpair/rpcs.proto
@@ -64,8 +64,6 @@
   TrueWirelessHeadsetImages true_wireless_images = 15;
 
   // Fast Pair only - When true, this device can support assistant function.
-  // Only Nearby admins can change this.
-  // TODO(b/128545971): Transition this to a feature.
   bool assistant_supported = 16;
 
   // Output only.
@@ -77,8 +75,6 @@
 
   // Fast Pair only - When true, Fast Pair will only create a bond with the
   // device and not attempt to connect any profiles (for example, A2DP or HFP).
-  // Only Nearby admins can change this.
-  // TODO(b/128545971): Transition this to a feature.
   bool data_only_connection = 18;
 
   // Name of the company/brand that will be selling the product.
@@ -157,13 +153,13 @@
 // Additional images for True Wireless Fast Pair devices.
 message TrueWirelessHeadsetImages {
   // Image URL for the left bud.
-   string left_bud_url = 1;
+  string left_bud_url = 1;
 
   // Image URL for the right bud.
-   string right_bud_url = 2;
+  string right_bud_url = 2;
 
   // Image URL for the case.
-   string case_url = 3;
+  string case_url = 3;
 }
 
 // Represents the type of device that is being registered.
@@ -316,3 +312,4 @@
   // Description of the connect device action on TV, when user is not logged in.
   string fast_pair_tv_connect_device_no_account_description = 24;
 }
+