Merge "Catch the exception otherwise if gms core side does not setup correctly system will crush"
diff --git a/nearby/framework/java/android/nearby/PresenceDevice.java b/nearby/framework/java/android/nearby/PresenceDevice.java
new file mode 100644
index 0000000..61326c6
--- /dev/null
+++ b/nearby/framework/java/android/nearby/PresenceDevice.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nearby;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Represents a Presence device from nearby scans.
+ *
+ * @hide
+ */
+public final class PresenceDevice extends NearbyDevice implements Parcelable {
+
+    /** The type of presence device. */
+    /** @hide **/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            DeviceType.UNKNOWN,
+            DeviceType.PHONE,
+            DeviceType.TABLET,
+            DeviceType.DISPLAY,
+            DeviceType.LAPTOP,
+            DeviceType.TV,
+            DeviceType.WATCH,
+    })
+    public @interface DeviceType {
+        /** The type of the device is unknown. */
+        int UNKNOWN = 0;
+        /** The device is a phone. */
+        int PHONE = 1;
+        /** The device is a tablet. */
+        int TABLET = 2;
+        /** The device is a display. */
+        int DISPLAY = 3;
+        /** The device is a laptop. */
+        int LAPTOP = 4;
+        /** The device is a TV. */
+        int TV = 5;
+        /** The device is a watch. */
+        int WATCH = 6;
+    }
+
+    private final String mDeviceId;
+    private final int mDeviceType;
+    private final String mDeviceImageUrl;
+    private final long mDiscoveryTimestampMillis;
+    private final Bundle mExtendedProperties;
+
+    /**
+     * Gets the name of the device, or {@code null} if not available.
+     *
+     * @hide
+     */
+    @Nullable
+    @Override
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * The id of the device.
+     *
+     * <p>This id is not a hardware id. It may rotate based on the remote device's broadcasts.
+     */
+    @NonNull
+    public String getDeviceId() {
+        return mDeviceId;
+    }
+
+    /** The type of the device. */
+    @DeviceType
+    public int getDeviceType() {
+        return mDeviceType;
+    }
+
+    /** An image URL representing the device. */
+    @Nullable
+    public String getDeviceImageUrl() {
+        return mDeviceImageUrl;
+    }
+
+    /** The timestamp (since boot) when the device is discovered. */
+    public long getDiscoveryTimestampMillis() {
+        return mDiscoveryTimestampMillis;
+    }
+
+    /**
+     * The extended properties of the device.
+     */
+    @NonNull
+    public Bundle getExtendedProperties() {
+        return mExtendedProperties;
+    }
+
+    private PresenceDevice(String deviceName, int mMedium, int rssi, String deviceId,
+            int deviceType,
+            String deviceImageUrl, long discoveryTimestampMillis,
+            Bundle extendedProperties) {
+        // TODO (b/217462253): change medium to a set in NearbyDevice.
+        super(deviceName, mMedium, rssi);
+        mDeviceId = deviceId;
+        mDeviceType = deviceType;
+        mDeviceImageUrl = deviceImageUrl;
+        mDiscoveryTimestampMillis = discoveryTimestampMillis;
+        mExtendedProperties = extendedProperties;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mName == null ? 0 : 1);
+        if (mName != null) {
+            dest.writeString(mName);
+        }
+        dest.writeInt(mMedium);
+        dest.writeInt(mRssi);
+        dest.writeString(mDeviceId);
+        dest.writeInt(mDeviceType);
+        dest.writeInt(mDeviceImageUrl == null ? 0 : 1);
+        if (mDeviceImageUrl != null) {
+            dest.writeString(mDeviceImageUrl);
+        }
+        dest.writeLong(mDiscoveryTimestampMillis);
+        dest.writeBundle(mExtendedProperties);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    public static final Creator<PresenceDevice> CREATOR = new Creator<PresenceDevice>() {
+        @Override
+        public PresenceDevice createFromParcel(Parcel in) {
+            Builder builder = new Builder();
+            if (in.readInt() == 1) {
+                builder.setName(in.readString());
+            }
+            builder.setMedium(in.readInt());
+            builder.setRssi(in.readInt());
+            builder.setDeviceId(in.readString());
+            builder.setDeviceType(in.readInt());
+            if (in.readInt() == 1) {
+                builder.setDeviceImageUrl(in.readString());
+            }
+            builder.setDiscoveryTimestampMillis(in.readLong());
+            Bundle bundle = in.readBundle();
+            for (String key : bundle.keySet()) {
+                builder.addExtendedProperty(key, bundle.getCharSequence(key).toString());
+            }
+            return builder.build();
+        }
+
+        @Override
+        public PresenceDevice[] newArray(int size) {
+            return new PresenceDevice[size];
+        }
+    };
+
+    /**
+     * Builder class for {@link PresenceDevice}.
+     *
+     * @hide
+     */
+    public static final class Builder {
+
+        private final Bundle mExtendedProperties;
+
+        private String mName;
+        private int mRssi;
+        private int mMedium;
+        private String mDeviceId;
+        private int mDeviceType;
+        private String mDeviceImageUrl;
+        private long mDiscoveryTimestampMillis;
+
+        public Builder() {
+            mExtendedProperties = new Bundle();
+            mRssi = -100;
+        }
+
+        /**
+         * Sets the name of the Presence device.
+         *
+         * @param name Name of the Presence. Can be {@code null} if there is no name.
+         */
+        @NonNull
+        public Builder setName(@android.annotation.Nullable String name) {
+            mName = name;
+            return this;
+        }
+
+        /**
+         * Sets the medium over which the Presence device is discovered.
+         *
+         * @param medium The {@link Medium} over which the device is discovered.
+         */
+        @NonNull
+        public Builder setMedium(@Medium int medium) {
+            mMedium = medium;
+            return this;
+        }
+
+        /**
+         * Sets the RSSI on the discovered Presence device.
+         *
+         * @param rssi The received signal strength in dBm.
+         */
+        @NonNull
+        public Builder setRssi(int rssi) {
+            mRssi = rssi;
+            return this;
+        }
+
+        /**
+         * Sets the identifier on the discovered Presence device.
+         *
+         * @param deviceId Identifier of the Presence device.
+         */
+        @NonNull
+        public Builder setDeviceId(@NonNull String deviceId) {
+            mDeviceId = deviceId;
+            return this;
+        }
+
+
+        /**
+         * Sets the type of discovered Presence device.
+         *
+         * @param deviceType Type of the Presence device.
+         */
+        @NonNull
+        public Builder setDeviceType(int deviceType) {
+            mDeviceType = deviceType;
+            return this;
+        }
+
+
+        /**
+         * Sets the image url of the discovered Presence device.
+         *
+         * @param deviceImageUrl Url of the image for the Presence device.
+         */
+        @NonNull
+        public Builder setDeviceImageUrl(@NonNull String deviceImageUrl) {
+            mDeviceImageUrl = deviceImageUrl;
+            return this;
+        }
+
+
+        /**
+         * Sets discovery timestamp, the clock is based on elapsed time.
+         *
+         * @param discoveryTimestampMillis Timestamp when the presence device is discovered.
+         */
+        @NonNull
+        public Builder setDiscoveryTimestampMillis(long discoveryTimestampMillis) {
+            mDiscoveryTimestampMillis = discoveryTimestampMillis;
+            return this;
+        }
+
+
+        /**
+         * Adds an extended property of the discovered presence device.
+         *
+         * @param key   Key of the extended property.
+         * @param value Value of the extended property,
+         */
+        @NonNull
+        public Builder addExtendedProperty(@NonNull String key, @NonNull String value) {
+            mExtendedProperties.putCharSequence(key, value);
+            return this;
+        }
+
+        /**
+         * Builds a Presence device.
+         */
+        @NonNull
+        public PresenceDevice build() {
+            return new PresenceDevice(mName, mMedium, mRssi, mDeviceId, mDeviceType,
+                    mDeviceImageUrl,
+                    mDiscoveryTimestampMillis, mExtendedProperties);
+        }
+    }
+}
diff --git a/nearby/framework/java/android/nearby/PresenceScanFilter.java b/nearby/framework/java/android/nearby/PresenceScanFilter.java
new file mode 100644
index 0000000..61e5049
--- /dev/null
+++ b/nearby/framework/java/android/nearby/PresenceScanFilter.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nearby;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Filter for scanning a nearby presence device.
+ *
+ * @hide
+ */
+public final class PresenceScanFilter extends ScanFilter implements Parcelable {
+
+    private final List<byte[]> mCertificates;
+    private final List<Integer> mPresenceIdentities;
+    private final List<Integer> mPresenceActions;
+    private final Bundle mExtendedProperties;
+
+    /**
+     * A list of certificates to filter on.
+     */
+    @NonNull
+    public List<byte[]> getCertificates() {
+        return mCertificates;
+    }
+
+    /**
+     * A list of presence identities for matching.
+     */
+    @NonNull
+    public List<Integer> getPresenceIdentities() {
+        return mPresenceIdentities;
+    }
+
+    /**
+     * A list of presence actions for matching.
+     */
+    @NonNull
+    public List<Integer> getPresenceActions() {
+        return mPresenceActions;
+    }
+
+    /**
+     * A bundle of extended properties for matching.
+     */
+    @NonNull
+    public Bundle getExtendedProperties() {
+        return mExtendedProperties;
+    }
+
+    private PresenceScanFilter(int rssiThreshold, List<byte[]> certificates,
+            List<Integer> presenceIdentities, List<Integer> presenceActions,
+            Bundle extendedProperties) {
+        super(ScanRequest.SCAN_TYPE_NEARBY_PRESENCE, rssiThreshold);
+        mCertificates = new ArrayList<>(certificates);
+        mPresenceIdentities = new ArrayList<>(presenceIdentities);
+        mPresenceActions = new ArrayList<>(presenceActions);
+        mExtendedProperties = extendedProperties;
+    }
+
+    private PresenceScanFilter(Parcel in) {
+        super(ScanRequest.SCAN_TYPE_NEARBY_PRESENCE, in);
+        mCertificates = new ArrayList<>();
+        int size = in.readInt();
+        for (int i = 0; i < size; i++) {
+            int len = in.readInt();
+            byte[] certificate = new byte[len];
+            in.readByteArray(certificate);
+            mCertificates.add(certificate);
+        }
+        mPresenceIdentities = new ArrayList<>();
+        if (in.readInt() != 0) {
+            in.readList(mPresenceIdentities, Integer.class.getClassLoader(), Integer.class);
+        }
+        mPresenceActions = new ArrayList<>();
+        if (in.readInt() != 0) {
+            in.readList(mPresenceActions, Integer.class.getClassLoader(), Integer.class);
+        }
+        mExtendedProperties = new Bundle();
+        Bundle bundle = in.readBundle(getClass().getClassLoader());
+        for (String key : bundle.keySet()) {
+            mExtendedProperties.putString(key, bundle.getString(key));
+        }
+    }
+
+    @NonNull
+    public static final Creator<PresenceScanFilter> CREATOR = new Creator<PresenceScanFilter>() {
+        @Override
+        public PresenceScanFilter createFromParcel(Parcel in) {
+            // Skip Scan Filter type as it's used for parent class.
+            in.readInt();
+            return createFromParcelBody(in);
+        }
+
+        @Override
+        public PresenceScanFilter[] newArray(int size) {
+            return new PresenceScanFilter[size];
+        }
+    };
+
+    /**
+     * Create a {@link PresenceScanFilter} from the parcel body. Scan Filter type is skipped.
+     */
+    static PresenceScanFilter createFromParcelBody(Parcel in) {
+        return new PresenceScanFilter(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(mCertificates.size());
+        for (byte[] certificate : mCertificates) {
+            dest.writeInt(certificate.length);
+            dest.writeByteArray(certificate);
+        }
+        dest.writeInt(mPresenceIdentities.size());
+        if (!mPresenceIdentities.isEmpty()) {
+            dest.writeList(mPresenceIdentities);
+        }
+        dest.writeInt(mPresenceActions.size());
+        if (!mPresenceActions.isEmpty()) {
+            dest.writeList(mPresenceActions);
+        }
+        dest.writeBundle(mExtendedProperties);
+    }
+
+    /**
+     * Builder for {@link PresenceScanFilter}.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private int mRssiThreshold;
+        private final Set<byte[]> mCertificates;
+        private final Set<Integer> mPresenceIdentities;
+        private final Set<Integer> mPresenceActions;
+        private final Bundle mExtendedProperties;
+
+        public Builder() {
+            mRssiThreshold = -100;
+            mCertificates = new ArraySet<>();
+            mPresenceIdentities = new ArraySet<>();
+            mPresenceActions = new ArraySet<>();
+            mExtendedProperties = new Bundle();
+        }
+
+        /**
+         * Sets the rssi threshold for the scan request.
+         */
+        @NonNull
+        public Builder setRssiThreshold(int rssiThreshold) {
+            mRssiThreshold = rssiThreshold;
+            return this;
+        }
+
+        /**
+         * Adds a list of certificates the scan filter is expected to match.
+         */
+
+        @NonNull
+        public Builder addCertificate(@NonNull byte[] certificate) {
+            mCertificates.add(certificate);
+            return this;
+        }
+
+        /**
+         * Adds a presence identity for filtering.
+         */
+        @NonNull
+        public Builder addPresenceIdentity(int identity) {
+            mPresenceIdentities.add(identity);
+            return this;
+        }
+
+        /**
+         * Adds a presence action for filtering.
+         */
+        @NonNull
+        public Builder addPresenceAction(int action) {
+            mPresenceActions.add(action);
+            return this;
+        }
+
+        /**
+         * Add an extended property for scan filtering.
+         */
+        @NonNull
+        public Builder addExtendedProperty(@NonNull String key, @Nullable String value) {
+            mExtendedProperties.putCharSequence(key, value);
+            return this;
+        }
+
+        /**
+         * Builds the scan filter.
+         */
+        @NonNull
+        public PresenceScanFilter build() {
+            Preconditions.checkState(!mCertificates.isEmpty(), "certificates cannot be empty");
+            return new PresenceScanFilter(mRssiThreshold, new ArrayList<>(mCertificates),
+                    new ArrayList<>(mPresenceIdentities),
+                    new ArrayList<>(mPresenceActions),
+                    mExtendedProperties);
+        }
+    }
+}
diff --git a/nearby/framework/java/android/nearby/ScanFilter.java b/nearby/framework/java/android/nearby/ScanFilter.java
new file mode 100644
index 0000000..0b2a754
--- /dev/null
+++ b/nearby/framework/java/android/nearby/ScanFilter.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nearby;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Filter for scanning a nearby device.
+ *
+ * @hide
+ */
+@SuppressLint("ParcelNotFinal")  // ScanFilter constructor is not public
+public abstract class ScanFilter implements Parcelable {
+    public static final @NonNull Creator<ScanFilter> CREATOR = new Creator<ScanFilter>() {
+        @Override
+        public ScanFilter createFromParcel(Parcel in) {
+            int type = in.readInt();
+            switch (type) {
+                case ScanRequest.SCAN_TYPE_NEARBY_PRESENCE:
+                    return PresenceScanFilter.createFromParcelBody(in);
+                default:
+                    throw new IllegalStateException(
+                            "Unexpected scan type (value " + type + ") in parcel.");
+            }
+        }
+
+        @Override
+        public ScanFilter[] newArray(int size) {
+            return new ScanFilter[size];
+        }
+    };
+
+    private final @ScanRequest.ScanType int mType;
+    private final int mRssiThreshold;
+
+    /**
+     * Constructs a Scan Filter.
+     *
+     * @hide
+     */
+    ScanFilter(@ScanRequest.ScanType int type, int rssiThreshold) {
+        mType = type;
+        mRssiThreshold = rssiThreshold;
+    }
+
+    /**
+     * Constructs a Scan Filter.
+     *
+     * @hide
+     */
+    ScanFilter(@ScanRequest.ScanType int type, Parcel in) {
+        mType = type;
+        mRssiThreshold = in.readInt();
+    }
+
+    /**
+     * Returns the type of this scan filter.
+     */
+    public @ScanRequest.ScanType int getType() {
+        return mType;
+    }
+
+    /**
+     * Minimum RSSI of the received scan result.
+     */
+    public int getRssiThreshold() {
+        return mRssiThreshold;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeInt(mRssiThreshold);
+    }
+
+    /**
+     * No special parcel contents.
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+}
diff --git a/nearby/framework/java/android/nearby/ScanRequest.java b/nearby/framework/java/android/nearby/ScanRequest.java
index 737f574..9180d5e 100644
--- a/nearby/framework/java/android/nearby/ScanRequest.java
+++ b/nearby/framework/java/android/nearby/ScanRequest.java
@@ -30,6 +30,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -48,6 +50,7 @@
     public static final int SCAN_TYPE_NEARBY_PRESENCE = 3;
     /** Scan type for scanning devices using exposure notification protocol. */
     public static final int SCAN_TYPE_EXPOSURE_NOTIFICATION = 4;
+
     /** Scan mode uses highest duty cycle. */
     public static final int SCAN_MODE_LOW_LATENCY = 2;
     /** Scan in balanced power mode.
@@ -73,7 +76,9 @@
                     /* scanType= */ in.readInt(),
                     /* scanMode= */ in.readInt(),
                     /* enableBle= */ in.readBoolean(),
-                    /* workSource= */ in.readTypedObject(WorkSource.CREATOR));
+                    /* workSource= */ in.readTypedObject(WorkSource.CREATOR),
+                    /* scanFilters= */
+                    in.readArrayList(ScanFilter.class.getClassLoader(), ScanFilter.class));
         }
 
         @Override
@@ -81,17 +86,20 @@
             return new ScanRequest[size];
         }
     };
+
     private final @ScanType int mScanType;
     private final @ScanMode int mScanMode;
     private final boolean mEnableBle;
     private final @NonNull WorkSource mWorkSource;
+    private final List<ScanFilter> mScanFilters;
 
     private ScanRequest(@ScanType int scanType, @ScanMode int scanMode, boolean enableBle,
-            @NonNull WorkSource workSource) {
+            @NonNull WorkSource workSource, List<ScanFilter> scanFilters) {
         mScanType = scanType;
         mScanMode = scanMode;
         mEnableBle = enableBle;
         mWorkSource = workSource;
+        mScanFilters = scanFilters;
     }
 
     /**
@@ -157,6 +165,16 @@
     }
 
     /**
+     * Returns Scan Filters for this request.
+     *
+     * @hide
+     */
+    @NonNull
+    public List<ScanFilter> getScanFilters() {
+        return mScanFilters;
+    }
+
+    /**
      * Returns the work source used for power attribution of this request.
      *
      * @hide
@@ -186,6 +204,7 @@
         stringBuilder.append(", scanMode=").append(scanModeToString(mScanMode));
         stringBuilder.append(", enableBle=").append(mEnableBle);
         stringBuilder.append(", workSource=").append(mWorkSource);
+        stringBuilder.append(", scanFilters=").append(mScanFilters);
         stringBuilder.append("]");
         return stringBuilder.toString();
     }
@@ -196,6 +215,7 @@
         dest.writeInt(mScanMode);
         dest.writeBoolean(mEnableBle);
         dest.writeTypedObject(mWorkSource, /* parcelableFlags= */0);
+        dest.writeTypedList(mScanFilters);
     }
 
     @Override
@@ -237,12 +257,14 @@
 
         private boolean mEnableBle;
         private WorkSource mWorkSource;
+        private List<ScanFilter> mScanFilters;
 
         /** Creates a new Builder with the given scan type. */
         public Builder() {
             mScanType = INVALID_SCAN_TYPE;
             mEnableBle = true;
             mWorkSource = new WorkSource();
+            mScanFilters = new ArrayList<>();
         }
 
         /**
@@ -304,6 +326,24 @@
         }
 
         /**
+         * Adds a scan filter to the request. Client can call this method multiple times to add
+         * more than one scan filter. Scan results that match any of these scan filters will
+         * be returned.
+         *
+         * <p>On devices with hardware support, scan filters can significantly improve the battery
+         * usage of Nearby scans.
+         *
+         * @param scanFilter Filter for scanning the request.
+         *
+         * @hide
+         */
+        @NonNull
+        public Builder addScanFilter(@NonNull ScanFilter scanFilter) {
+            mScanFilters.add(scanFilter);
+            return this;
+        }
+
+        /**
          * Builds a scan request from this builder.
          *
          * @return a new nearby scan request.
@@ -318,7 +358,7 @@
             Preconditions.checkState(isValidScanMode(mScanMode),
                     "invalid scan mode : " + mScanMode
                             + ", scan mode must be one of ScanMode#SCAN_MODE_");
-            return new ScanRequest(mScanType, mScanMode, mEnableBle, mWorkSource);
+            return new ScanRequest(mScanType, mScanMode, mEnableBle, mWorkSource, mScanFilters);
         }
     }
 }
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 a7ce771..0151543 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
@@ -16,11 +16,13 @@
 
 package com.android.server.nearby.fastpair;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.nearby.FastPairDevice;
 import android.nearby.NearbyDevice;
 import android.util.Log;
 
+import com.android.server.nearby.common.bloomfilter.BloomFilter;
 import com.android.server.nearby.common.locator.Locator;
 import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager;
 import com.android.server.nearby.provider.FastPairDataProvider;
@@ -29,6 +31,8 @@
 
 import com.google.protobuf.ByteString;
 
+import java.util.List;
+
 import service.proto.Cache;
 import service.proto.Rpcs;
 
@@ -87,4 +91,21 @@
             // Start to process bloomfilter
         }
     }
+
+    /**
+     * Checks the bloom filter to see if any of the devices are recognized and should have a
+     * notification displayed for them. A device is recognized if the account key + salt combination
+     * is inside the bloom filter.
+     */
+    @Nullable
+    static FastPairDevice findRecognizedDevice(
+            List<FastPairDevice> devices, BloomFilter bloomFilter, byte[] salt) {
+        for (FastPairDevice device : devices) {
+            // byte[] rotatedKey = concat(device.getAccountKey().toByteArray(), salt);
+//            if (bloomFilter.possiblyContains(rotatedKey)) {
+//                return device;
+//            }
+        }
+        return null;
+    }
 }
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 d59e696..34db620 100644
--- a/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
@@ -20,7 +20,6 @@
 import android.annotation.Nullable;
 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;
@@ -34,6 +33,7 @@
 
 import java.util.List;
 
+import service.proto.Data;
 import service.proto.Rpcs;
 
 /**
@@ -51,7 +51,6 @@
      * Initializes FastPairDataProvider singleton.
      */
     public static synchronized FastPairDataProvider init(Context context) {
-
         if (sInstance == null) {
             sInstance = new FastPairDataProvider(context);
         }
@@ -135,8 +134,9 @@
     /**
      * Get recognized device from bloom filter.
      */
-    public FastPairDevice getRecognizedDevice(BloomFilter bloomFilter, byte[] salt) {
-        return new FastPairDevice.Builder().build();
+    public Data.FastPairDeviceWithAccountKey getRecognizedDevice(BloomFilter bloomFilter,
+            byte[] salt) {
+        return Data.FastPairDeviceWithAccountKey.newBuilder().build();
     }
 
     /**
diff --git a/nearby/service/proto/src/fastpair/data.proto b/nearby/service/proto/src/fastpair/data.proto
new file mode 100644
index 0000000..37dfac2
--- /dev/null
+++ b/nearby/service/proto/src/fastpair/data.proto
@@ -0,0 +1,25 @@
+syntax = "proto3";
+
+package service.proto;
+
+// A device that has been Fast Paired with.
+message FastPairDeviceWithAccountKey {
+  // The account key which was written to the device after pairing completed.
+  bytes account_key = 1;
+
+  // The stored discovery item which represents the notification that should be
+  // associated with the device. Note, this is stored as a raw byte array
+  // instead of StoredDiscoveryItem because icing only supports proto lite and
+  // StoredDiscoveryItem is handed around as a nano proto in implementation,
+  // which are not compatible with each other.
+  bytes discovery_item_bytes = 3;
+
+  // SHA256 of "account key + headset's public address", this is used to
+  // identify the paired headset. Because of adding account key to generate the
+  // hash value, it makes the information anonymous, even for the same headset,
+  // different accounts have different values.
+  bytes sha256_account_key_public_address = 4;
+
+  // Deprecated fields.
+  reserved 2;
+}
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/ScanRequestTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/ScanRequestTest.java
index b5939f8..3bb348b 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/ScanRequestTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/ScanRequestTest.java
@@ -89,7 +89,8 @@
 
         assertThat(request.toString()).isEqualTo(
                 "Request[scanType=2, scanMode=SCAN_MODE_BALANCED, "
-                        + "enableBle=true, workSource=WorkSource{" + UID + " " + APP_NAME + "}]");
+                        + "enableBle=true, workSource=WorkSource{" + UID + " " + APP_NAME
+                        + "}, scanFilters=[]]");
     }
 
     /** Verify toString works correctly with null WorkSource. */
@@ -100,7 +101,8 @@
                 SCAN_TYPE_FAST_PAIR).setWorkSource(null).build();
 
         assertThat(request.toString()).isEqualTo("Request[scanType=1, "
-                + "scanMode=SCAN_MODE_LOW_POWER, enableBle=true, workSource=WorkSource{}]");
+                + "scanMode=SCAN_MODE_LOW_POWER, enableBle=true, workSource=WorkSource{}, "
+                + "scanFilters=[]]");
     }
 
     @Test
diff --git a/nearby/tests/unit/src/android/nearby/ScanRequestTest.java b/nearby/tests/unit/src/android/nearby/ScanRequestTest.java
index fdb6db1..509fa1e 100644
--- a/nearby/tests/unit/src/android/nearby/ScanRequestTest.java
+++ b/nearby/tests/unit/src/android/nearby/ScanRequestTest.java
@@ -114,7 +114,8 @@
 
         assertThat(request.toString()).isEqualTo(
                 "Request[scanType=2, scanMode=SCAN_MODE_BALANCED, "
-                        + "enableBle=true, workSource=WorkSource{1001 android.nearby.tests}]");
+                        + "enableBle=true, workSource=WorkSource{1001 android.nearby.tests}, "
+                        + "scanFilters=[]]");
     }
 
     /** Verify toString works correctly with null WorkSource. */
@@ -124,7 +125,8 @@
                 SCAN_TYPE_FAST_PAIR).setWorkSource(null).build();
 
         assertThat(request.toString()).isEqualTo("Request[scanType=1, "
-                + "scanMode=SCAN_MODE_LOW_POWER, enableBle=true, workSource=WorkSource{}]");
+                + "scanMode=SCAN_MODE_LOW_POWER, enableBle=true, workSource=WorkSource{}, "
+                + "scanFilters=[]]");
     }
 
     /** Verify writing and reading from parcel for scan request. */