Add System Api impl for subsequent pairing.

Test: to add unit test
Bug: 204780849

Change-Id: I93e74f9aaf63f9ad95f5887cb8aa5bce4df25ae5
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
index 6166052..87045de 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
@@ -70,7 +70,8 @@
                     "On discovery model id" + Hex.bytesToStringLowercase(model));
             // Use api to get anti spoofing key from model id.
             Rpcs.GetObservedDeviceResponse response =
-                    FastPairDataProvider.getInstance().loadFastPairDeviceMetadata(model);
+                    FastPairDataProvider.getInstance()
+                            .loadFastPairAntispoofkeyDeviceMetadata(model);
             ByteString publicKey = response.getDevice().getAntiSpoofingKeyPair().getPublicKey();
             Locator.get(mContext, FastPairHalfSheetManager.class).showHalfSheet(
                     Cache.ScanFastPairStoreItem.newBuilder().setAddress(mBleAddress)
diff --git a/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java b/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
index 9bf2bef..d59e696 100644
--- a/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
@@ -21,6 +21,10 @@
 import android.content.Context;
 import android.nearby.FastPairDataProviderBase;
 import android.nearby.FastPairDevice;
+import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataRequestParcel;
+import android.nearby.aidl.FastPairEligibleAccountsRequestParcel;
+import android.nearby.aidl.FastPairManageAccountDeviceRequestParcel;
+import android.nearby.aidl.FastPairManageAccountRequestParcel;
 import android.util.Log;
 
 import androidx.annotation.WorkerThread;
@@ -28,9 +32,13 @@
 import com.android.server.nearby.common.bloomfilter.BloomFilter;
 import com.android.server.nearby.fastpair.footprint.FastPairUploadInfo;
 
+import java.util.List;
+
 import service.proto.Rpcs;
 
-/** FastPairDataProvider is a singleton that implements APIs to get FastPair data. */
+/**
+ * FastPairDataProvider is a singleton that implements APIs to get FastPair data.
+ */
 public class FastPairDataProvider {
 
     private static final String TAG = "FastPairDataProvider";
@@ -39,7 +47,9 @@
 
     private ProxyFastPairDataProvider mProxyFastPairDataProvider;
 
-    /** Initializes FastPairDataProvider singleton. */
+    /**
+     * Initializes FastPairDataProvider singleton.
+     */
     public static synchronized FastPairDataProvider init(Context context) {
 
         if (sInstance == null) {
@@ -68,28 +78,58 @@
         }
     }
 
-    /** loadFastPairDeviceMetadata. */
+    /**
+     * Loads FastPairAntispoofkeyDeviceMetadata.
+     *
+     * @throws IllegalStateException If ProxyFastPairDataProvider is not available.
+     */
     @WorkerThread
     @Nullable
-    public Rpcs.GetObservedDeviceResponse loadFastPairDeviceMetadata(byte[] modelId) {
+    public Rpcs.GetObservedDeviceResponse loadFastPairAntispoofkeyDeviceMetadata(byte[] modelId) {
         if (mProxyFastPairDataProvider != null) {
-            return mProxyFastPairDataProvider.loadFastPairDeviceMetadata(modelId);
+            FastPairAntispoofkeyDeviceMetadataRequestParcel requestParcel =
+                    new FastPairAntispoofkeyDeviceMetadataRequestParcel();
+            requestParcel.modelId = modelId;
+            return Utils.convertFastPairAntispoofkeyDeviceMetadataToGetObservedDeviceResponse(
+                    mProxyFastPairDataProvider
+                            .loadFastPairAntispoofkeyDeviceMetadata(requestParcel));
         }
         throw new IllegalStateException("No ProxyFastPairDataProvider yet constructed");
     }
 
     /**
-     * opt in default account to fast pair
+     * Enrolls an account to Fast Pair.
+     *
+     * @throws IllegalStateException If ProxyFastPairDataProvider is not available.
      */
     public void optIn(Account account) {
-
+        if (mProxyFastPairDataProvider != null) {
+            FastPairManageAccountRequestParcel requestParcel =
+                    new FastPairManageAccountRequestParcel();
+            requestParcel.account = account;
+            requestParcel.requestType = FastPairDataProviderBase.MANAGE_REQUEST_ADD;
+            mProxyFastPairDataProvider.manageFastPairAccount(requestParcel);
+        }
+        throw new IllegalStateException("No ProxyFastPairDataProvider yet constructed");
     }
 
     /**
-     * Upload the device to the footprint
+     * Uploads the device info to Fast Pair account.
+     *
+     * @throws IllegalStateException If ProxyFastPairDataProvider is not available.
      */
     public void upload(Account account, FastPairUploadInfo uploadInfo) {
-
+        if (mProxyFastPairDataProvider != null) {
+            FastPairManageAccountDeviceRequestParcel requestParcel =
+                    new FastPairManageAccountDeviceRequestParcel();
+            requestParcel.account = account;
+            requestParcel.requestType = FastPairDataProviderBase.MANAGE_REQUEST_ADD;
+            requestParcel.accountKeyDeviceMetadata =
+                    Utils.convertFastPairUploadInfoToFastPairAccountKeyDeviceMetadata(
+                            uploadInfo);
+            mProxyFastPairDataProvider.manageFastPairAccountDevice(requestParcel);
+        }
+        throw new IllegalStateException("No ProxyFastPairDataProvider yet constructed");
     }
 
     /**
@@ -99,4 +139,18 @@
         return new FastPairDevice.Builder().build();
     }
 
+    /**
+     * Get FastPair Eligible Accounts.
+     *
+     * @throws IllegalStateException If ProxyFastPairDataProvider is not available.
+     */
+    public List<Account> loadFastPairEligibleAccounts() {
+        if (mProxyFastPairDataProvider != null) {
+            FastPairEligibleAccountsRequestParcel requestParcel =
+                    new FastPairEligibleAccountsRequestParcel();
+            return Utils.convertFastPairEligibleAccountsToAccountList(
+                    mProxyFastPairDataProvider.loadFastPairEligibleAccounts(requestParcel));
+        }
+        throw new IllegalStateException("No ProxyFastPairDataProvider yet constructed");
+    }
 }
diff --git a/nearby/service/java/com/android/server/nearby/provider/ProxyFastPairDataProvider.java b/nearby/service/java/com/android/server/nearby/provider/ProxyFastPairDataProvider.java
index 7bd53f4..36b4478 100644
--- a/nearby/service/java/com/android/server/nearby/provider/ProxyFastPairDataProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/ProxyFastPairDataProvider.java
@@ -18,10 +18,20 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.nearby.aidl.FastPairAccountDevicesMetadataRequestParcel;
+import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel;
 import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataParcel;
 import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataRequestParcel;
+import android.nearby.aidl.FastPairEligibleAccountParcel;
+import android.nearby.aidl.FastPairEligibleAccountsRequestParcel;
+import android.nearby.aidl.FastPairManageAccountDeviceRequestParcel;
+import android.nearby.aidl.FastPairManageAccountRequestParcel;
+import android.nearby.aidl.IFastPairAccountDevicesMetadataCallback;
 import android.nearby.aidl.IFastPairAntispoofkeyDeviceMetadataCallback;
 import android.nearby.aidl.IFastPairDataProvider;
+import android.nearby.aidl.IFastPairEligibleAccountsCallback;
+import android.nearby.aidl.IFastPairManageAccountCallback;
+import android.nearby.aidl.IFastPairManageAccountDeviceCallback;
 import android.os.IBinder;
 import android.os.RemoteException;
 
@@ -36,13 +46,13 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
-import service.proto.Rpcs;
-
 /**
  * Proxy for IFastPairDataProvider implementations.
  */
 public class ProxyFastPairDataProvider implements ServiceListener<BoundServiceInfo> {
 
+    private static final int TIME_OUT_MILLIS = 10000;
+
     /**
      * Creates and registers this proxy. If no suitable service is available for the proxy, returns
      * null.
@@ -71,7 +81,9 @@
         return mServiceMonitor.checkServiceResolves();
     }
 
-    /** User service watch to connect to actually services implemented by OEMs. */
+    /**
+     * User service watch to connect to actually services implemented by OEMs.
+     */
     public void register() {
         mServiceMonitor.register();
     }
@@ -88,29 +100,151 @@
     public void onUnbind() {
     }
 
-    /** Invoke loadFastPairDeviceMetadata. */
+    /**
+     * Invokes system api loadFastPairEligibleAccounts.
+     *
+     * @return an array of acccounts and their opt in status.
+     */
     @WorkerThread
     @Nullable
-    Rpcs.GetObservedDeviceResponse loadFastPairDeviceMetadata(byte[] modelId) {
+    public FastPairEligibleAccountParcel[] loadFastPairEligibleAccounts(
+            FastPairEligibleAccountsRequestParcel requestParcel) {
         final CountDownLatch waitForCompletionLatch = new CountDownLatch(1);
-        final AtomicReference<Rpcs.GetObservedDeviceResponse> response = new AtomicReference<>();
+        final AtomicReference<FastPairEligibleAccountParcel[]> response = new AtomicReference<>();
         mServiceMonitor.runOnBinder(new ServiceMonitor.BinderOperation() {
             @Override
             public void run(IBinder binder) throws RemoteException {
                 IFastPairDataProvider provider = IFastPairDataProvider.Stub.asInterface(binder);
-                FastPairAntispoofkeyDeviceMetadataRequestParcel requestParcel =
-                        new FastPairAntispoofkeyDeviceMetadataRequestParcel();
-                requestParcel.modelId = modelId;
+                IFastPairEligibleAccountsCallback callback =
+                        new IFastPairEligibleAccountsCallback.Stub() {
+                            public void onFastPairEligibleAccountsReceived(
+                                    FastPairEligibleAccountParcel[] accountParcels) {
+                                response.set(accountParcels);
+                                waitForCompletionLatch.countDown();
+                            }
+
+                            public void onError(int code, String message) {
+                                waitForCompletionLatch.countDown();
+                            }
+                        };
+                provider.loadFastPairEligibleAccounts(requestParcel, callback);
+            }
+
+            @Override
+            public void onError() {
+                waitForCompletionLatch.countDown();
+            }
+        });
+        try {
+            waitForCompletionLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            // skip.
+        }
+        return response.get();
+    }
+
+    /**
+     * Invokes system api manageFastPairAccount to opt in account, or opt out account.
+     */
+    @WorkerThread
+    public void manageFastPairAccount(FastPairManageAccountRequestParcel requestParcel) {
+        final CountDownLatch waitForCompletionLatch = new CountDownLatch(1);
+        mServiceMonitor.runOnBinder(new ServiceMonitor.BinderOperation() {
+            @Override
+            public void run(IBinder binder) throws RemoteException {
+                IFastPairDataProvider provider = IFastPairDataProvider.Stub.asInterface(binder);
+                IFastPairManageAccountCallback callback =
+                        new IFastPairManageAccountCallback.Stub() {
+                            public void onSuccess() {
+                                waitForCompletionLatch.countDown();
+                            }
+
+                            public void onError(int code, String message) {
+                                waitForCompletionLatch.countDown();
+                            }
+                        };
+                provider.manageFastPairAccount(requestParcel, callback);
+            }
+
+            @Override
+            public void onError() {
+                waitForCompletionLatch.countDown();
+            }
+        });
+        try {
+            waitForCompletionLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            // skip.
+        }
+        return;
+    }
+
+    /**
+     * Invokes system api manageFastPairAccountDevice to add or remove a device from a Fast Pair
+     * account.
+     */
+    @WorkerThread
+    public void manageFastPairAccountDevice(
+            FastPairManageAccountDeviceRequestParcel requestParcel) {
+        final CountDownLatch waitForCompletionLatch = new CountDownLatch(1);
+        mServiceMonitor.runOnBinder(new ServiceMonitor.BinderOperation() {
+            @Override
+            public void run(IBinder binder) throws RemoteException {
+                IFastPairDataProvider provider = IFastPairDataProvider.Stub.asInterface(binder);
+                IFastPairManageAccountDeviceCallback callback =
+                        new IFastPairManageAccountDeviceCallback.Stub() {
+                            public void onSuccess() {
+                                waitForCompletionLatch.countDown();
+                            }
+
+                            public void onError(int code, String message) {
+                                waitForCompletionLatch.countDown();
+                            }
+                        };
+                provider.manageFastPairAccountDevice(requestParcel, callback);
+            }
+
+            @Override
+            public void onError() {
+                waitForCompletionLatch.countDown();
+            }
+        });
+        try {
+            waitForCompletionLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            // skip.
+        }
+        return;
+    }
+
+    /**
+     * Invokes system api loadFastPairAntispoofkeyDeviceMetadata.
+     *
+     * @return the Fast Pair AntispoofKeyDeviceMetadata of a given device.
+     */
+    @WorkerThread
+    @Nullable
+    FastPairAntispoofkeyDeviceMetadataParcel loadFastPairAntispoofkeyDeviceMetadata(
+            FastPairAntispoofkeyDeviceMetadataRequestParcel requestParcel) {
+        final CountDownLatch waitForCompletionLatch = new CountDownLatch(1);
+        final AtomicReference<FastPairAntispoofkeyDeviceMetadataParcel> response =
+                new AtomicReference<>();
+        mServiceMonitor.runOnBinder(new ServiceMonitor.BinderOperation() {
+            @Override
+            public void run(IBinder binder) throws RemoteException {
+                IFastPairDataProvider provider = IFastPairDataProvider.Stub.asInterface(binder);
                 IFastPairAntispoofkeyDeviceMetadataCallback callback =
                         new IFastPairAntispoofkeyDeviceMetadataCallback.Stub() {
-                    public void onFastPairAntispoofkeyDeviceMetadataReceived(
-                            FastPairAntispoofkeyDeviceMetadataParcel metadata) {
-                        response.set(Utils.convert(metadata));
-                        waitForCompletionLatch.countDown();
-                    }
-                    public void onError(int code, String message) {
-                    }
-                };
+                            public void onFastPairAntispoofkeyDeviceMetadataReceived(
+                                    FastPairAntispoofkeyDeviceMetadataParcel metadata) {
+                                response.set(metadata);
+                                waitForCompletionLatch.countDown();
+                            }
+
+                            public void onError(int code, String message) {
+                                waitForCompletionLatch.countDown();
+                            }
+                        };
                 provider.loadFastPairAntispoofkeyDeviceMetadata(requestParcel, callback);
             }
 
@@ -120,7 +254,51 @@
             }
         });
         try {
-            waitForCompletionLatch.await(10000, TimeUnit.MILLISECONDS);
+            waitForCompletionLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            // skip.
+        }
+        return response.get();
+    }
+
+    /**
+     * Invokes loadFastPairAccountDevicesMetadata.
+     *
+     * @return the metadata of Fast Pair devices that are associated with a given account.
+     */
+    @WorkerThread
+    @Nullable
+    FastPairAccountKeyDeviceMetadataParcel[] loadFastPairAccountDevicesMetadata(
+            FastPairAccountDevicesMetadataRequestParcel requestParcel) {
+        final CountDownLatch waitForCompletionLatch = new CountDownLatch(1);
+        final AtomicReference<FastPairAccountKeyDeviceMetadataParcel[]> response =
+                new AtomicReference<>();
+        mServiceMonitor.runOnBinder(new ServiceMonitor.BinderOperation() {
+            @Override
+            public void run(IBinder binder) throws RemoteException {
+                IFastPairDataProvider provider = IFastPairDataProvider.Stub.asInterface(binder);
+                IFastPairAccountDevicesMetadataCallback callback =
+                        new IFastPairAccountDevicesMetadataCallback.Stub() {
+                            public void onFastPairAccountDevicesMetadataReceived(
+                                    FastPairAccountKeyDeviceMetadataParcel[] metadatas) {
+                                response.set(metadatas);
+                                waitForCompletionLatch.countDown();
+                            }
+
+                            public void onError(int code, String message) {
+                                waitForCompletionLatch.countDown();
+                            }
+                        };
+                provider.loadFastPairAccountDevicesMetadata(requestParcel, callback);
+            }
+
+            @Override
+            public void onError() {
+                waitForCompletionLatch.countDown();
+            }
+        });
+        try {
+            waitForCompletionLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS);
         } catch (InterruptedException e) {
             // skip.
         }
diff --git a/nearby/service/java/com/android/server/nearby/provider/Utils.java b/nearby/service/java/com/android/server/nearby/provider/Utils.java
index ca0a390..c06d017 100644
--- a/nearby/service/java/com/android/server/nearby/provider/Utils.java
+++ b/nearby/service/java/com/android/server/nearby/provider/Utils.java
@@ -16,80 +16,248 @@
 
 package com.android.server.nearby.provider;
 
+import android.accounts.Account;
+import android.annotation.Nullable;
+import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel;
 import android.nearby.aidl.FastPairAntispoofkeyDeviceMetadataParcel;
+import android.nearby.aidl.FastPairDeviceMetadataParcel;
+import android.nearby.aidl.FastPairDiscoveryItemParcel;
+import android.nearby.aidl.FastPairEligibleAccountParcel;
+
+import com.android.server.nearby.fastpair.footprint.FastPairUploadInfo;
 
 import com.google.protobuf.ByteString;
 
+import java.util.ArrayList;
+import java.util.List;
+
+import service.proto.Cache;
+import service.proto.FastPairString.FastPairStrings;
 import service.proto.Rpcs;
 
+/**
+ * Utility classes to convert between different data classes.
+ */
 class Utils {
 
-    static Rpcs.GetObservedDeviceResponse convert(
-            FastPairAntispoofkeyDeviceMetadataParcel metadata) {
+    static List<Account> convertFastPairEligibleAccountsToAccountList(
+            @Nullable FastPairEligibleAccountParcel[] accountParcels) {
+        if (accountParcels == null) {
+            return new ArrayList<Account>(0);
+        }
+        List<Account> accounts = new ArrayList<Account>(accountParcels.length);
+        for (FastPairEligibleAccountParcel parcel : accountParcels) {
+            accounts.add(parcel.account);
+        }
+        return accounts;
+    }
+
+    static @Nullable Rpcs.GetObservedDeviceResponse
+            convertFastPairAntispoofkeyDeviceMetadataToGetObservedDeviceResponse(
+            @Nullable FastPairAntispoofkeyDeviceMetadataParcel metadata) {
+        if (metadata == null) {
+            return null;
+        }
         return Rpcs.GetObservedDeviceResponse.newBuilder()
-          .setDevice(Rpcs.Device.newBuilder()
-                  .setAntiSpoofingKeyPair(Rpcs.AntiSpoofingKeyPair.newBuilder()
-                          .setPublicKey(ByteString.copyFrom(metadata.antiSpoofPublicKey))
-                          .build())
-                  .setTrueWirelessImages(
-                          Rpcs.TrueWirelessHeadsetImages.newBuilder()
-                                  .setLeftBudUrl(
-                                          metadata.deviceMetadata.trueWirelessImageUrlLeftBud)
-                                  .setRightBudUrl(
-                                          metadata.deviceMetadata.trueWirelessImageUrlRightBud)
-                                  .setCaseUrl(metadata.deviceMetadata.trueWirelessImageUrlCase)
-                                  .build())
-                  .setImageUrl(metadata.deviceMetadata.imageUrl)
-                  .setIntentUri(metadata.deviceMetadata.intentUri)
-                  .setBleTxPower(metadata.deviceMetadata.bleTxPower)
-                  .setTriggerDistance(metadata.deviceMetadata.triggerDistance)
-                  .setDeviceType(Rpcs.DeviceType.forNumber(metadata.deviceMetadata.deviceType))
-                  .build())
-          .setImage(ByteString.copyFrom(metadata.deviceMetadata.image))
-          .setStrings(Rpcs.ObservedDeviceStrings.newBuilder()
-                  .setAssistantSetupHalfSheet(metadata.deviceMetadata.assistantSetupHalfSheet)
-                  .setAssistantSetupNotification(metadata.deviceMetadata.assistantSetupNotification)
-                  .setConfirmPinDescription(metadata.deviceMetadata.confirmPinDescription)
-                  .setConfirmPinTitle(metadata.deviceMetadata.confirmPinTitle)
-                  .setConnectSuccessCompanionAppInstalled(
-                          metadata.deviceMetadata.connectSuccessCompanionAppInstalled)
-                  .setConnectSuccessCompanionAppNotInstalled(
-                          metadata.deviceMetadata.connectSuccessCompanionAppNotInstalled)
-                  .setDownloadCompanionAppDescription(
-                          metadata.deviceMetadata.downloadCompanionAppDescription)
-                  .setFailConnectGoToSettingsDescription(
-                          metadata.deviceMetadata.failConnectGoToSettingsDescription)
-                  .setFastPairTvConnectDeviceNoAccountDescription(
-                          metadata.deviceMetadata.fastPairTvConnectDeviceNoAccountDescription)
-                  .setInitialNotificationDescription(
-                          metadata.deviceMetadata.initialNotificationDescription)
-                  .setInitialNotificationDescriptionNoAccount(
-                          metadata.deviceMetadata.initialNotificationDescriptionNoAccount)
-                  .setInitialPairingDescription(metadata.deviceMetadata.initialPairingDescription)
-                  .setLocale(metadata.deviceMetadata.locale)
-                  .setOpenCompanionAppDescription(
-                          metadata.deviceMetadata.openCompanionAppDescription)
-                  .setRetroactivePairingDescription(
-                          metadata.deviceMetadata.retroactivePairingDescription)
-                  .setSubsequentPairingDescription(
-                          metadata.deviceMetadata.subsequentPairingDescription)
-                  .setSyncContactsDescription(
-                          metadata.deviceMetadata.syncContactsDescription)
-                  .setSyncContactsTitle(
-                          metadata.deviceMetadata.syncContactsTitle)
-                  .setSyncSmsDescription(
-                          metadata.deviceMetadata.syncSmsDescription)
-                  .setSyncSmsTitle(
-                          metadata.deviceMetadata.syncSmsTitle)
-                  .setUnableToConnectDescription(
-                          metadata.deviceMetadata.unableToConnectDescription)
-                  .setUnableToConnectTitle(
-                          metadata.deviceMetadata.unableToConnectTitle)
-                  .setUpdateCompanionAppDescription(
-                          metadata.deviceMetadata.updateCompanionAppDescription)
-                  .setWaitLaunchCompanionAppDescription(
-                          metadata.deviceMetadata.waitLaunchCompanionAppDescription)
-                  .build())
+                .setDevice(Rpcs.Device.newBuilder()
+                        .setAntiSpoofingKeyPair(Rpcs.AntiSpoofingKeyPair.newBuilder()
+                                .setPublicKey(ByteString.copyFrom(metadata.antiSpoofPublicKey))
+                                .build())
+                        .setTrueWirelessImages(Rpcs.TrueWirelessHeadsetImages.newBuilder()
+                                        .setLeftBudUrl(
+                                                metadata.deviceMetadata.trueWirelessImageUrlLeftBud)
+                                        .setRightBudUrl(
+                                                metadata.deviceMetadata
+                                                        .trueWirelessImageUrlRightBud)
+                                        .setCaseUrl(
+                                                metadata.deviceMetadata
+                                                        .trueWirelessImageUrlCase
+                                        )
+                                        .build())
+                        .setImageUrl(metadata.deviceMetadata.imageUrl)
+                        .setIntentUri(metadata.deviceMetadata.intentUri)
+                        .setBleTxPower(metadata.deviceMetadata.bleTxPower)
+                        .setTriggerDistance(metadata.deviceMetadata.triggerDistance)
+                        .setDeviceType(
+                                Rpcs.DeviceType.forNumber(metadata.deviceMetadata.deviceType))
+                        .build())
+                .setImage(ByteString.copyFrom(metadata.deviceMetadata.image))
+                .setStrings(Rpcs.ObservedDeviceStrings.newBuilder()
+                        .setAssistantSetupHalfSheet(metadata.deviceMetadata.assistantSetupHalfSheet)
+                        .setAssistantSetupNotification(
+                                metadata.deviceMetadata.assistantSetupNotification)
+                        .setConfirmPinDescription(metadata.deviceMetadata.confirmPinDescription)
+                        .setConfirmPinTitle(metadata.deviceMetadata.confirmPinTitle)
+                        .setConnectSuccessCompanionAppInstalled(
+                                metadata.deviceMetadata.connectSuccessCompanionAppInstalled)
+                        .setConnectSuccessCompanionAppNotInstalled(
+                                metadata.deviceMetadata.connectSuccessCompanionAppNotInstalled)
+                        .setDownloadCompanionAppDescription(
+                                metadata.deviceMetadata.downloadCompanionAppDescription)
+                        .setFailConnectGoToSettingsDescription(
+                                metadata.deviceMetadata.failConnectGoToSettingsDescription)
+                        .setFastPairTvConnectDeviceNoAccountDescription(
+                                metadata.deviceMetadata.fastPairTvConnectDeviceNoAccountDescription)
+                        .setInitialNotificationDescription(
+                                metadata.deviceMetadata.initialNotificationDescription)
+                        .setInitialNotificationDescriptionNoAccount(
+                                metadata.deviceMetadata.initialNotificationDescriptionNoAccount)
+                        .setInitialPairingDescription(
+                                metadata.deviceMetadata.initialPairingDescription)
+                        .setLocale(metadata.deviceMetadata.locale)
+                        .setOpenCompanionAppDescription(
+                                metadata.deviceMetadata.openCompanionAppDescription)
+                        .setRetroactivePairingDescription(
+                                metadata.deviceMetadata.retroactivePairingDescription)
+                        .setSubsequentPairingDescription(
+                                metadata.deviceMetadata.subsequentPairingDescription)
+                        .setSyncContactsDescription(
+                                metadata.deviceMetadata.syncContactsDescription)
+                        .setSyncContactsTitle(
+                                metadata.deviceMetadata.syncContactsTitle)
+                        .setSyncSmsDescription(
+                                metadata.deviceMetadata.syncSmsDescription)
+                        .setSyncSmsTitle(
+                                metadata.deviceMetadata.syncSmsTitle)
+                        .setUnableToConnectDescription(
+                                metadata.deviceMetadata.unableToConnectDescription)
+                        .setUnableToConnectTitle(
+                                metadata.deviceMetadata.unableToConnectTitle)
+                        .setUpdateCompanionAppDescription(
+                                metadata.deviceMetadata.updateCompanionAppDescription)
+                        .setWaitLaunchCompanionAppDescription(
+                                metadata.deviceMetadata.waitLaunchCompanionAppDescription)
+                        .build())
                 .build();
     }
+
+    static @Nullable FastPairAccountKeyDeviceMetadataParcel
+            convertFastPairUploadInfoToFastPairAccountKeyDeviceMetadata(
+            FastPairUploadInfo uploadInfo) {
+        if (uploadInfo == null) {
+            return null;
+        }
+
+        FastPairAccountKeyDeviceMetadataParcel accountKeyDeviceMetadataParcel =
+                new FastPairAccountKeyDeviceMetadataParcel();
+        accountKeyDeviceMetadataParcel.accountKey = uploadInfo.getAccountKey().toByteArray();
+        accountKeyDeviceMetadataParcel.sha256AccountKeyPublicAddress =
+                uploadInfo.getSha256AccountKeyPublicAddress().toByteArray();
+        accountKeyDeviceMetadataParcel.metadata =
+                convertStoredDiscoveryItemToFastPairDeviceMetadata(
+                        uploadInfo.getStoredDiscoveryItem());
+        accountKeyDeviceMetadataParcel.discoveryItem =
+                convertStoredDiscoveryItemToFastPairDiscoveryItem(
+                        uploadInfo.getStoredDiscoveryItem());
+
+        return accountKeyDeviceMetadataParcel;
+    }
+
+    private static @Nullable FastPairDiscoveryItemParcel
+            convertStoredDiscoveryItemToFastPairDiscoveryItem(
+            @Nullable Cache.StoredDiscoveryItem storedDiscoveryItem) {
+        if (storedDiscoveryItem == null) {
+            return null;
+        }
+
+        FastPairDiscoveryItemParcel discoveryItemParcel = new FastPairDiscoveryItemParcel();
+        discoveryItemParcel.actionUrl = storedDiscoveryItem.getActionUrl();
+        discoveryItemParcel.actionUrlType = storedDiscoveryItem.getActionUrlType().getNumber();
+        discoveryItemParcel.appName = storedDiscoveryItem.getAppName();
+        discoveryItemParcel.attachmentType = storedDiscoveryItem.getAttachmentType().getNumber();
+        discoveryItemParcel.attachmentType = storedDiscoveryItem.getAttachmentType().getNumber();
+        discoveryItemParcel.authenticationPublicKeySecp256r1 =
+                storedDiscoveryItem.getAuthenticationPublicKeySecp256R1().toByteArray();
+        discoveryItemParcel.bleRecordBytes = storedDiscoveryItem.getBleRecordBytes().toByteArray();
+        discoveryItemParcel.debugCategory = storedDiscoveryItem.getDebugCategory().getNumber();
+        discoveryItemParcel.debugMessage = storedDiscoveryItem.getDebugMessage();
+        discoveryItemParcel.description = storedDiscoveryItem.getDescription();
+        discoveryItemParcel.deviceName = storedDiscoveryItem.getDeviceName();
+        discoveryItemParcel.displayUrl = storedDiscoveryItem.getDisplayUrl();
+        discoveryItemParcel.entityId = storedDiscoveryItem.getEntityId();
+        discoveryItemParcel.featureGraphicUrl = storedDiscoveryItem.getFeatureGraphicUrl();
+        discoveryItemParcel.firstObservationTimestampMillis =
+                storedDiscoveryItem.getFirstObservationTimestampMillis();
+        discoveryItemParcel.groupId = storedDiscoveryItem.getGroupId();
+        discoveryItemParcel.iconFifeUrl = storedDiscoveryItem.getIconFifeUrl();
+        discoveryItemParcel.iconPng = storedDiscoveryItem.getIconPng().toByteArray();
+        discoveryItemParcel.id = storedDiscoveryItem.getId();
+        discoveryItemParcel.lastObservationTimestampMillis =
+                storedDiscoveryItem.getLastObservationTimestampMillis();
+        discoveryItemParcel.lastUserExperience =
+                storedDiscoveryItem.getLastUserExperience().getNumber();
+        discoveryItemParcel.lostMillis = storedDiscoveryItem.getLostMillis();
+        discoveryItemParcel.macAddress = storedDiscoveryItem.getMacAddress();
+        discoveryItemParcel.packageName = storedDiscoveryItem.getPackageName();
+        discoveryItemParcel.pendingAppInstallTimestampMillis =
+                storedDiscoveryItem.getPendingAppInstallTimestampMillis();
+        discoveryItemParcel.rssi = storedDiscoveryItem.getRssi();
+        discoveryItemParcel.state = storedDiscoveryItem.getState().getNumber();
+        discoveryItemParcel.title = storedDiscoveryItem.getTitle();
+        discoveryItemParcel.triggerId = storedDiscoveryItem.getTriggerId();
+        discoveryItemParcel.txPower = storedDiscoveryItem.getTxPower();
+        discoveryItemParcel.type = storedDiscoveryItem.getType().getNumber();
+
+        return discoveryItemParcel;
+    }
+
+    /*  Do we upload these?
+        String downloadCompanionAppDescription =
+             bundle.getString("downloadCompanionAppDescription");
+        String locale = bundle.getString("locale");
+        String openCompanionAppDescription = bundle.getString("openCompanionAppDescription");
+        float triggerDistance = bundle.getFloat("triggerDistance");
+        String unableToConnectDescription = bundle.getString("unableToConnectDescription");
+        String unableToConnectTitle = bundle.getString("unableToConnectTitle");
+        String updateCompanionAppDescription = bundle.getString("updateCompanionAppDescription");
+    */
+    private static @Nullable FastPairDeviceMetadataParcel
+            convertStoredDiscoveryItemToFastPairDeviceMetadata(
+            @Nullable Cache.StoredDiscoveryItem storedDiscoveryItem) {
+        if (storedDiscoveryItem == null) {
+            return null;
+        }
+
+        FastPairStrings fpStrings = storedDiscoveryItem.getFastPairStrings();
+
+        FastPairDeviceMetadataParcel metadataParcel = new FastPairDeviceMetadataParcel();
+        metadataParcel.assistantSetupHalfSheet = fpStrings.getAssistantHalfSheetDescription();
+        metadataParcel.assistantSetupNotification = fpStrings.getAssistantNotificationDescription();
+        metadataParcel.confirmPinDescription = fpStrings.getConfirmPinDescription();
+        metadataParcel.confirmPinTitle = fpStrings.getConfirmPinTitle();
+        metadataParcel.connectSuccessCompanionAppInstalled =
+                fpStrings.getPairingFinishedCompanionAppInstalled();
+        metadataParcel.connectSuccessCompanionAppNotInstalled =
+                fpStrings.getPairingFinishedCompanionAppNotInstalled();
+        metadataParcel.failConnectGoToSettingsDescription = fpStrings.getPairingFailDescription();
+        metadataParcel.fastPairTvConnectDeviceNoAccountDescription =
+                fpStrings.getFastPairTvConnectDeviceNoAccountDescription();
+        metadataParcel.initialNotificationDescription = fpStrings.getTapToPairWithAccount();
+        metadataParcel.initialNotificationDescriptionNoAccount =
+                fpStrings.getTapToPairWithoutAccount();
+        metadataParcel.initialPairingDescription = fpStrings.getInitialPairingDescription();
+        metadataParcel.retroactivePairingDescription = fpStrings.getRetroactivePairingDescription();
+        metadataParcel.subsequentPairingDescription = fpStrings.getSubsequentPairingDescription();
+        metadataParcel.syncContactsDescription = fpStrings.getSyncContactsDescription();
+        metadataParcel.syncContactsTitle = fpStrings.getSyncContactsTitle();
+        metadataParcel.syncSmsDescription = fpStrings.getSyncSmsDescription();
+        metadataParcel.syncSmsTitle = fpStrings.getSyncSmsTitle();
+        metadataParcel.waitLaunchCompanionAppDescription = fpStrings.getWaitAppLaunchDescription();
+
+        Cache.FastPairInformation fpInformation = storedDiscoveryItem.getFastPairInformation();
+        metadataParcel.trueWirelessImageUrlCase =
+                fpInformation.getTrueWirelessImages().getCaseUrl();
+        metadataParcel.trueWirelessImageUrlLeftBud =
+                fpInformation.getTrueWirelessImages().getLeftBudUrl();
+        metadataParcel.trueWirelessImageUrlRightBud =
+                fpInformation.getTrueWirelessImages().getRightBudUrl();
+        metadataParcel.deviceType = fpInformation.getDeviceType().getNumber();
+
+        metadataParcel.bleTxPower = storedDiscoveryItem.getTxPower();
+        metadataParcel.image = storedDiscoveryItem.getIconPng().toByteArray();
+        metadataParcel.imageUrl = storedDiscoveryItem.getIconFifeUrl();
+        metadataParcel.intentUri = storedDiscoveryItem.getActionUrl();
+
+        return metadataParcel;
+    }
 }