/* * 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.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.le.ScanRecord; import android.os.Parcel; import android.os.Parcelable; import java.util.Arrays; import java.util.Objects; /** * A data class representing scan result from Nearby Service. Scan result can come from multiple * mediums like BLE, Wi-Fi Aware, and etc. A scan result consists of An encapsulation of various * parameters for requesting nearby scans. * *

All scan results generated through {@link NearbyManager} are guaranteed to have a valid * medium, identifier, timestamp (both UTC time and elapsed real-time since boot), and accuracy. All * other parameters are optional. * * @hide */ public final class NearbyDeviceParcelable implements Parcelable { /** Used to read a NearbyDeviceParcelable from a Parcel. */ @NonNull public static final Creator CREATOR = new Creator() { @Override public NearbyDeviceParcelable createFromParcel(Parcel in) { Builder builder = new Builder(); builder.setDeviceId(in.readLong()); builder.setScanType(in.readInt()); if (in.readInt() == 1) { builder.setName(in.readString()); } builder.setMedium(in.readInt()); builder.setTxPower(in.readInt()); builder.setRssi(in.readInt()); builder.setAction(in.readInt()); builder.setPublicCredential( in.readParcelable( PublicCredential.class.getClassLoader(), PublicCredential.class)); if (in.readInt() == 1) { builder.setFastPairModelId(in.readString()); } if (in.readInt() == 1) { builder.setBluetoothAddress(in.readString()); } if (in.readInt() == 1) { int dataLength = in.readInt(); byte[] data = new byte[dataLength]; in.readByteArray(data); builder.setData(data); } if (in.readInt() == 1) { int saltLength = in.readInt(); byte[] salt = new byte[saltLength]; in.readByteArray(salt); builder.setData(salt); } if (in.readInt() == 1) { builder.setPresenceDevice(in.readParcelable( PresenceDevice.class.getClassLoader(), PresenceDevice.class)); } if (in.readInt() == 1) { int encryptionKeyTagLength = in.readInt(); byte[] keyTag = new byte[encryptionKeyTagLength]; in.readByteArray(keyTag); builder.setData(keyTag); } return builder.build(); } @Override public NearbyDeviceParcelable[] newArray(int size) { return new NearbyDeviceParcelable[size]; } }; private final long mDeviceId; @ScanRequest.ScanType int mScanType; @Nullable private final String mName; @NearbyDevice.Medium private final int mMedium; private final int mTxPower; private final int mRssi; private final int mAction; private final PublicCredential mPublicCredential; @Nullable private final String mBluetoothAddress; @Nullable private final String mFastPairModelId; @Nullable private final byte[] mData; @Nullable private final byte[] mSalt; @Nullable private final PresenceDevice mPresenceDevice; @Nullable private final byte[] mEncryptionKeyTag; private NearbyDeviceParcelable( long deviceId, @ScanRequest.ScanType int scanType, @Nullable String name, int medium, int TxPower, int rssi, int action, PublicCredential publicCredential, @Nullable String fastPairModelId, @Nullable String bluetoothAddress, @Nullable byte[] data, @Nullable byte[] salt, @Nullable PresenceDevice presenceDevice, @Nullable byte[] encryptionKeyTag) { mDeviceId = deviceId; mScanType = scanType; mName = name; mMedium = medium; mTxPower = TxPower; mRssi = rssi; mAction = action; mPublicCredential = publicCredential; mFastPairModelId = fastPairModelId; mBluetoothAddress = bluetoothAddress; mData = data; mSalt = salt; mPresenceDevice = presenceDevice; mEncryptionKeyTag = encryptionKeyTag; } /** No special parcel contents. */ @Override public int describeContents() { return 0; } /** * Flatten this NearbyDeviceParcelable in to a Parcel. * * @param dest The Parcel in which the object should be written. * @param flags Additional flags about how the object should be written. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeLong(mDeviceId); dest.writeInt(mScanType); dest.writeInt(mName == null ? 0 : 1); if (mName != null) { dest.writeString(mName); } dest.writeInt(mMedium); dest.writeInt(mTxPower); dest.writeInt(mRssi); dest.writeInt(mAction); dest.writeParcelable(mPublicCredential, flags); dest.writeInt(mFastPairModelId == null ? 0 : 1); if (mFastPairModelId != null) { dest.writeString(mFastPairModelId); } dest.writeInt(mBluetoothAddress == null ? 0 : 1); if (mBluetoothAddress != null) { dest.writeString(mBluetoothAddress); } dest.writeInt(mData == null ? 0 : 1); if (mData != null) { dest.writeInt(mData.length); dest.writeByteArray(mData); } dest.writeInt(mSalt == null ? 0 : 1); if (mSalt != null) { dest.writeInt(mSalt.length); dest.writeByteArray(mSalt); } dest.writeInt(mPresenceDevice == null ? 0 : 1); if (mPresenceDevice != null) { dest.writeParcelable(mPresenceDevice, /* parcelableFlags= */ 0); } dest.writeInt(mEncryptionKeyTag == null ? 0 : 1); if (mEncryptionKeyTag != null) { dest.writeInt(mEncryptionKeyTag.length); dest.writeByteArray(mEncryptionKeyTag); } } /** Returns a string representation of this ScanRequest. */ @Override public String toString() { return "NearbyDeviceParcelable[" + "deviceId=" + mDeviceId + ", scanType=" + mScanType + ", name=" + mName + ", medium=" + NearbyDevice.mediumToString(mMedium) + ", txPower=" + mTxPower + ", rssi=" + mRssi + ", action=" + mAction + ", bluetoothAddress=" + mBluetoothAddress + ", fastPairModelId=" + mFastPairModelId + ", data=" + Arrays.toString(mData) + ", salt=" + Arrays.toString(mSalt) + "]"; } @Override public boolean equals(Object other) { if (other instanceof NearbyDeviceParcelable) { NearbyDeviceParcelable otherNearbyDeviceParcelable = (NearbyDeviceParcelable) other; return mDeviceId == otherNearbyDeviceParcelable.mDeviceId && mScanType == otherNearbyDeviceParcelable.mScanType && (Objects.equals(mName, otherNearbyDeviceParcelable.mName)) && (mMedium == otherNearbyDeviceParcelable.mMedium) && (mTxPower == otherNearbyDeviceParcelable.mTxPower) && (mRssi == otherNearbyDeviceParcelable.mRssi) && (mAction == otherNearbyDeviceParcelable.mAction) && (Objects.equals( mPublicCredential, otherNearbyDeviceParcelable.mPublicCredential)) && (Objects.equals( mBluetoothAddress, otherNearbyDeviceParcelable.mBluetoothAddress)) && (Objects.equals( mFastPairModelId, otherNearbyDeviceParcelable.mFastPairModelId)) && (Arrays.equals(mData, otherNearbyDeviceParcelable.mData)) && (Arrays.equals(mSalt, otherNearbyDeviceParcelable.mSalt)) && (Objects.equals( mPresenceDevice, otherNearbyDeviceParcelable.mPresenceDevice)) && (Arrays.equals( mEncryptionKeyTag, otherNearbyDeviceParcelable.mEncryptionKeyTag)); } return false; } @Override public int hashCode() { return Objects.hash( mDeviceId, mScanType, mName, mMedium, mRssi, mAction, mPublicCredential.hashCode(), mBluetoothAddress, mFastPairModelId, Arrays.hashCode(mData), Arrays.hashCode(mSalt), mPresenceDevice, Arrays.hashCode(mEncryptionKeyTag)); } /** * The id of the device. *

This id is not a hardware id. It may rotate based on the remote device's broadcasts. * * @hide */ public long getDeviceId() { return mDeviceId; } /** * Returns the type of the scan. * * @hide */ @ScanRequest.ScanType public int getScanType() { return mScanType; } /** * Gets the name of the NearbyDeviceParcelable. Returns {@code null} If there is no name. * * Used in Fast Pair. */ @Nullable public String getName() { return mName; } /** * Gets the {@link android.nearby.NearbyDevice.Medium} of the NearbyDeviceParcelable over which * it is discovered. * * Used in Fast Pair and Nearby Presence. */ @NearbyDevice.Medium public int getMedium() { return mMedium; } /** * Gets the transmission power in dBm. * * Used in Fast Pair. * * @hide */ @IntRange(from = -127, to = 126) public int getTxPower() { return mTxPower; } /** * Gets the received signal strength in dBm. * * Used in Fast Pair and Nearby Presence. */ @IntRange(from = -127, to = 126) public int getRssi() { return mRssi; } /** * Gets the Action. * * Used in Nearby Presence. * * @hide */ @IntRange(from = -127, to = 126) public int getAction() { return mAction; } /** * Gets the public credential. * * Used in Nearby Presence. * * @hide */ @NonNull public PublicCredential getPublicCredential() { return mPublicCredential; } /** * Gets the Fast Pair identifier. Returns {@code null} if there is no Model ID or this is not a * Fast Pair device. * * Used in Fast Pair. */ @Nullable public String getFastPairModelId() { return mFastPairModelId; } /** * Gets the Bluetooth device hardware address. Returns {@code null} if the device is not * discovered by Bluetooth. * * Used in Fast Pair. */ @Nullable public String getBluetoothAddress() { return mBluetoothAddress; } /** * Gets the raw data from the scanning. * Returns {@code null} if there is no extra data or this is not a Fast Pair device. * * Used in Fast Pair. */ @Nullable public byte[] getData() { return mData; } /** * Gets the salt in the advertisement from the Nearby Presence device. * Returns {@code null} if this is not a Nearby Presence device. * * Used in Nearby Presence. */ @Nullable public byte[] getSalt() { return mSalt; } /** * Gets the {@link PresenceDevice} Nearby Presence device. This field is * for Fast Pair client only. */ @Nullable public PresenceDevice getPresenceDevice() { return mPresenceDevice; } /** * Gets the encryption key tag calculated from advertisement * Returns {@code null} if the data is not encrypted or this is not a Presence device. * * Used in Presence. */ @Nullable public byte[] getEncryptionKeyTag() { return mEncryptionKeyTag; } /** Builder class for {@link NearbyDeviceParcelable}. */ public static final class Builder { private long mDeviceId = -1; @Nullable private String mName; @NearbyDevice.Medium private int mMedium; private int mTxPower; private int mRssi; private int mAction; private PublicCredential mPublicCredential; @ScanRequest.ScanType int mScanType; @Nullable private String mFastPairModelId; @Nullable private String mBluetoothAddress; @Nullable private byte[] mData; @Nullable private byte[] mSalt; @Nullable private PresenceDevice mPresenceDevice; @Nullable private byte[] mEncryptionKeyTag; /** Sets the id of the device. */ public Builder setDeviceId(long deviceId) { this.mDeviceId = deviceId; return this; } /** * Sets the scan type of the NearbyDeviceParcelable. * * @hide */ public Builder setScanType(@ScanRequest.ScanType int scanType) { mScanType = scanType; return this; } /** * Sets the name of the scanned device. * * @param name The local name of the scanned device. */ @NonNull public Builder setName(@Nullable String name) { mName = name; return this; } /** * Sets the medium over which the device is discovered. * * @param medium The {@link NearbyDevice.Medium} over which the device is discovered. */ @NonNull public Builder setMedium(@NearbyDevice.Medium int medium) { mMedium = medium; return this; } /** * Sets the transmission power of the discovered device. * * @param txPower The transmission power in dBm. * @hide */ @NonNull public Builder setTxPower(int txPower) { mTxPower = txPower; return this; } /** * Sets the RSSI between scanned device and the discovered device. * * @param rssi The received signal strength in dBm. */ @NonNull public Builder setRssi(@IntRange(from = -127, to = 126) int rssi) { mRssi = rssi; return this; } /** * Sets the action from the discovered device. * * @param action The action of the discovered device. * @hide */ @NonNull public Builder setAction(int action) { mAction = action; return this; } /** * Sets the public credential of the discovered device. * * @param publicCredential The public credential. * @hide */ @NonNull public Builder setPublicCredential(@NonNull PublicCredential publicCredential) { mPublicCredential = publicCredential; return this; } /** * Sets the Fast Pair model Id. * * @param fastPairModelId Fast Pair device identifier. */ @NonNull public Builder setFastPairModelId(@Nullable String fastPairModelId) { mFastPairModelId = fastPairModelId; return this; } /** * Sets the bluetooth address. * * @param bluetoothAddress The hardware address of the bluetooth device. */ @NonNull public Builder setBluetoothAddress(@Nullable String bluetoothAddress) { mBluetoothAddress = bluetoothAddress; return this; } /** * Sets the scanned raw data. * * @param data raw data scanned, like {@link ScanRecord#getServiceData()} if scanned by * Bluetooth. */ @NonNull public Builder setData(@Nullable byte[] data) { mData = data; return this; } /** * Sets the encryption key tag calculated from the advertisement. * * @param encryptionKeyTag calculated from identity scanned from the advertisement */ @NonNull public Builder setEncryptionKeyTag(@Nullable byte[] encryptionKeyTag) { mEncryptionKeyTag = encryptionKeyTag; return this; } /** * Sets the slat in the advertisement from the Nearby Presence device. * * @param salt in the advertisement from the Nearby Presence device. */ @NonNull public Builder setSalt(@Nullable byte[] salt) { mSalt = salt; return this; } /** * Sets the {@link PresenceDevice} if there is any. * The current {@link NearbyDeviceParcelable} can be seen as the wrapper of the * {@link PresenceDevice}. */ @Nullable public Builder setPresenceDevice(@Nullable PresenceDevice presenceDevice) { mPresenceDevice = presenceDevice; return this; } /** Builds a ScanResult. */ @NonNull public NearbyDeviceParcelable build() { return new NearbyDeviceParcelable( mDeviceId, mScanType, mName, mMedium, mTxPower, mRssi, mAction, mPublicCredential, mFastPairModelId, mBluetoothAddress, mData, mSalt, mPresenceDevice, mEncryptionKeyTag); } } }