1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.nearby;
18 
19 import android.annotation.IntRange;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.bluetooth.le.ScanRecord;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 
26 import java.util.Arrays;
27 import java.util.Objects;
28 
29 /**
30  * A data class representing scan result from Nearby Service. Scan result can come from multiple
31  * mediums like BLE, Wi-Fi Aware, and etc. A scan result consists of An encapsulation of various
32  * parameters for requesting nearby scans.
33  *
34  * <p>All scan results generated through {@link NearbyManager} are guaranteed to have a valid
35  * medium, identifier, timestamp (both UTC time and elapsed real-time since boot), and accuracy. All
36  * other parameters are optional.
37  *
38  * @hide
39  */
40 public final class NearbyDeviceParcelable implements Parcelable {
41 
42     /** Used to read a NearbyDeviceParcelable from a Parcel. */
43     @NonNull
44     public static final Creator<NearbyDeviceParcelable> CREATOR =
45             new Creator<NearbyDeviceParcelable>() {
46                 @Override
47                 public NearbyDeviceParcelable createFromParcel(Parcel in) {
48                     Builder builder = new Builder();
49                     builder.setDeviceId(in.readLong());
50                     builder.setScanType(in.readInt());
51                     if (in.readInt() == 1) {
52                         builder.setName(in.readString());
53                     }
54                     builder.setMedium(in.readInt());
55                     builder.setTxPower(in.readInt());
56                     builder.setRssi(in.readInt());
57                     builder.setAction(in.readInt());
58                     builder.setPublicCredential(
59                             in.readParcelable(
60                                     PublicCredential.class.getClassLoader(),
61                                     PublicCredential.class));
62                     if (in.readInt() == 1) {
63                         builder.setFastPairModelId(in.readString());
64                     }
65                     if (in.readInt() == 1) {
66                         builder.setBluetoothAddress(in.readString());
67                     }
68                     if (in.readInt() == 1) {
69                         int dataLength = in.readInt();
70                         byte[] data = new byte[dataLength];
71                         in.readByteArray(data);
72                         builder.setData(data);
73                     }
74                     if (in.readInt() == 1) {
75                         int saltLength = in.readInt();
76                         byte[] salt = new byte[saltLength];
77                         in.readByteArray(salt);
78                         builder.setData(salt);
79                     }
80                     if (in.readInt() == 1) {
81                         builder.setPresenceDevice(in.readParcelable(
82                                 PresenceDevice.class.getClassLoader(),
83                                 PresenceDevice.class));
84                     }
85                     if (in.readInt() == 1) {
86                         int encryptionKeyTagLength = in.readInt();
87                         byte[] keyTag = new byte[encryptionKeyTagLength];
88                         in.readByteArray(keyTag);
89                         builder.setData(keyTag);
90                     }
91                     return builder.build();
92                 }
93 
94                 @Override
95                 public NearbyDeviceParcelable[] newArray(int size) {
96                     return new NearbyDeviceParcelable[size];
97                 }
98             };
99 
100     private final long mDeviceId;
101     @ScanRequest.ScanType int mScanType;
102     @Nullable private final String mName;
103     @NearbyDevice.Medium private final int mMedium;
104     private final int mTxPower;
105     private final int mRssi;
106     private final int mAction;
107     private final PublicCredential mPublicCredential;
108     @Nullable private final String mBluetoothAddress;
109     @Nullable private final String mFastPairModelId;
110     @Nullable private final byte[] mData;
111     @Nullable private final byte[] mSalt;
112     @Nullable private final PresenceDevice mPresenceDevice;
113     @Nullable private final byte[] mEncryptionKeyTag;
114 
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)115     private NearbyDeviceParcelable(
116             long deviceId,
117             @ScanRequest.ScanType int scanType,
118             @Nullable String name,
119             int medium,
120             int TxPower,
121             int rssi,
122             int action,
123             PublicCredential publicCredential,
124             @Nullable String fastPairModelId,
125             @Nullable String bluetoothAddress,
126             @Nullable byte[] data,
127             @Nullable byte[] salt,
128             @Nullable PresenceDevice presenceDevice,
129             @Nullable byte[] encryptionKeyTag) {
130         mDeviceId = deviceId;
131         mScanType = scanType;
132         mName = name;
133         mMedium = medium;
134         mTxPower = TxPower;
135         mRssi = rssi;
136         mAction = action;
137         mPublicCredential = publicCredential;
138         mFastPairModelId = fastPairModelId;
139         mBluetoothAddress = bluetoothAddress;
140         mData = data;
141         mSalt = salt;
142         mPresenceDevice = presenceDevice;
143         mEncryptionKeyTag = encryptionKeyTag;
144     }
145 
146     /** No special parcel contents. */
147     @Override
describeContents()148     public int describeContents() {
149         return 0;
150     }
151 
152     /**
153      * Flatten this NearbyDeviceParcelable in to a Parcel.
154      *
155      * @param dest The Parcel in which the object should be written.
156      * @param flags Additional flags about how the object should be written.
157      */
158     @Override
writeToParcel(@onNull Parcel dest, int flags)159     public void writeToParcel(@NonNull Parcel dest, int flags) {
160         dest.writeLong(mDeviceId);
161         dest.writeInt(mScanType);
162         dest.writeInt(mName == null ? 0 : 1);
163         if (mName != null) {
164             dest.writeString(mName);
165         }
166         dest.writeInt(mMedium);
167         dest.writeInt(mTxPower);
168         dest.writeInt(mRssi);
169         dest.writeInt(mAction);
170         dest.writeParcelable(mPublicCredential, flags);
171         dest.writeInt(mFastPairModelId == null ? 0 : 1);
172         if (mFastPairModelId != null) {
173             dest.writeString(mFastPairModelId);
174         }
175         dest.writeInt(mBluetoothAddress == null ? 0 : 1);
176         if (mBluetoothAddress != null) {
177             dest.writeString(mBluetoothAddress);
178         }
179         dest.writeInt(mData == null ? 0 : 1);
180         if (mData != null) {
181             dest.writeInt(mData.length);
182             dest.writeByteArray(mData);
183         }
184         dest.writeInt(mSalt == null ? 0 : 1);
185         if (mSalt != null) {
186             dest.writeInt(mSalt.length);
187             dest.writeByteArray(mSalt);
188         }
189         dest.writeInt(mPresenceDevice == null ? 0 : 1);
190         if (mPresenceDevice != null) {
191             dest.writeParcelable(mPresenceDevice, /* parcelableFlags= */ 0);
192         }
193         dest.writeInt(mEncryptionKeyTag == null ? 0 : 1);
194         if (mEncryptionKeyTag != null) {
195             dest.writeInt(mEncryptionKeyTag.length);
196             dest.writeByteArray(mEncryptionKeyTag);
197         }
198     }
199 
200     /** Returns a string representation of this ScanRequest. */
201     @Override
toString()202     public String toString() {
203         return "NearbyDeviceParcelable["
204                 + "deviceId="
205                 + mDeviceId
206                 + ", scanType="
207                 + mScanType
208                 + ", name="
209                 + mName
210                 + ", medium="
211                 + NearbyDevice.mediumToString(mMedium)
212                 + ", txPower="
213                 + mTxPower
214                 + ", rssi="
215                 + mRssi
216                 + ", action="
217                 + mAction
218                 + ", bluetoothAddress="
219                 + mBluetoothAddress
220                 + ", fastPairModelId="
221                 + mFastPairModelId
222                 + ", data="
223                 + Arrays.toString(mData)
224                 + ", salt="
225                 + Arrays.toString(mSalt)
226                 + "]";
227     }
228 
229     @Override
equals(Object other)230     public boolean equals(Object other) {
231         if (other instanceof NearbyDeviceParcelable) {
232             NearbyDeviceParcelable otherNearbyDeviceParcelable = (NearbyDeviceParcelable) other;
233             return  mDeviceId == otherNearbyDeviceParcelable.mDeviceId
234                     && mScanType == otherNearbyDeviceParcelable.mScanType
235                     && (Objects.equals(mName, otherNearbyDeviceParcelable.mName))
236                     && (mMedium == otherNearbyDeviceParcelable.mMedium)
237                     && (mTxPower == otherNearbyDeviceParcelable.mTxPower)
238                     && (mRssi == otherNearbyDeviceParcelable.mRssi)
239                     && (mAction == otherNearbyDeviceParcelable.mAction)
240                     && (Objects.equals(
241                     mPublicCredential, otherNearbyDeviceParcelable.mPublicCredential))
242                     && (Objects.equals(
243                     mBluetoothAddress, otherNearbyDeviceParcelable.mBluetoothAddress))
244                     && (Objects.equals(
245                     mFastPairModelId, otherNearbyDeviceParcelable.mFastPairModelId))
246                     && (Arrays.equals(mData, otherNearbyDeviceParcelable.mData))
247                     && (Arrays.equals(mSalt, otherNearbyDeviceParcelable.mSalt))
248                     && (Objects.equals(
249                     mPresenceDevice, otherNearbyDeviceParcelable.mPresenceDevice))
250                     && (Arrays.equals(
251                     mEncryptionKeyTag, otherNearbyDeviceParcelable.mEncryptionKeyTag));
252         }
253         return false;
254     }
255 
256     @Override
hashCode()257     public int hashCode() {
258         return Objects.hash(
259                 mDeviceId,
260                 mScanType,
261                 mName,
262                 mMedium,
263                 mRssi,
264                 mAction,
265                 mPublicCredential.hashCode(),
266                 mBluetoothAddress,
267                 mFastPairModelId,
268                 Arrays.hashCode(mData),
269                 Arrays.hashCode(mSalt),
270                 mPresenceDevice,
271                 Arrays.hashCode(mEncryptionKeyTag));
272     }
273 
274     /**
275      * The id of the device.
276      * <p>This id is not a hardware id. It may rotate based on the remote device's broadcasts.
277      *
278      * @hide
279      */
getDeviceId()280     public long getDeviceId() {
281         return mDeviceId;
282     }
283 
284     /**
285      * Returns the type of the scan.
286      *
287      * @hide
288      */
289     @ScanRequest.ScanType
getScanType()290     public int getScanType() {
291         return mScanType;
292     }
293 
294     /**
295      * Gets the name of the NearbyDeviceParcelable. Returns {@code null} If there is no name.
296      *
297      * Used in Fast Pair.
298      */
299     @Nullable
getName()300     public String getName() {
301         return mName;
302     }
303 
304     /**
305      * Gets the {@link android.nearby.NearbyDevice.Medium} of the NearbyDeviceParcelable over which
306      * it is discovered.
307      *
308      * Used in Fast Pair and Nearby Presence.
309      */
310     @NearbyDevice.Medium
getMedium()311     public int getMedium() {
312         return mMedium;
313     }
314 
315     /**
316      * Gets the transmission power in dBm.
317      *
318      * Used in Fast Pair.
319      *
320      * @hide
321      */
322     @IntRange(from = -127, to = 126)
getTxPower()323     public int getTxPower() {
324         return mTxPower;
325     }
326 
327     /**
328      * Gets the received signal strength in dBm.
329      *
330      * Used in Fast Pair and Nearby Presence.
331      */
332     @IntRange(from = -127, to = 126)
getRssi()333     public int getRssi() {
334         return mRssi;
335     }
336 
337     /**
338      * Gets the Action.
339      *
340      * Used in Nearby Presence.
341      *
342      * @hide
343      */
344     @IntRange(from = -127, to = 126)
getAction()345     public int getAction() {
346         return mAction;
347     }
348 
349     /**
350      * Gets the public credential.
351      *
352      * Used in Nearby Presence.
353      *
354      * @hide
355      */
356     @NonNull
getPublicCredential()357     public PublicCredential getPublicCredential() {
358         return mPublicCredential;
359     }
360 
361     /**
362      * Gets the Fast Pair identifier. Returns {@code null} if there is no Model ID or this is not a
363      * Fast Pair device.
364      *
365      * Used in Fast Pair.
366      */
367     @Nullable
getFastPairModelId()368     public String getFastPairModelId() {
369         return mFastPairModelId;
370     }
371 
372     /**
373      * Gets the Bluetooth device hardware address. Returns {@code null} if the device is not
374      * discovered by Bluetooth.
375      *
376      * Used in Fast Pair.
377      */
378     @Nullable
getBluetoothAddress()379     public String getBluetoothAddress() {
380         return mBluetoothAddress;
381     }
382 
383     /**
384      * Gets the raw data from the scanning.
385      * Returns {@code null} if there is no extra data or this is not a Fast Pair device.
386      *
387      * Used in Fast Pair.
388      */
389     @Nullable
getData()390     public byte[] getData() {
391         return mData;
392     }
393 
394     /**
395      * Gets the salt in the advertisement from the Nearby Presence device.
396      * Returns {@code null} if this is not a Nearby Presence device.
397      *
398      * Used in Nearby Presence.
399      */
400     @Nullable
getSalt()401     public byte[] getSalt() {
402         return mSalt;
403     }
404 
405     /**
406      * Gets the {@link PresenceDevice} Nearby Presence device. This field is
407      * for Fast Pair client only.
408      */
409     @Nullable
getPresenceDevice()410     public PresenceDevice getPresenceDevice() {
411         return mPresenceDevice;
412     }
413 
414     /**
415      * Gets the encryption key tag calculated from advertisement
416      * Returns {@code null} if the data is not encrypted or this is not a Presence device.
417      *
418      * Used in Presence.
419      */
420     @Nullable
getEncryptionKeyTag()421     public byte[] getEncryptionKeyTag() {
422         return mEncryptionKeyTag;
423     }
424 
425     /** Builder class for {@link NearbyDeviceParcelable}. */
426     public static final class Builder {
427         private long mDeviceId = -1;
428         @Nullable private String mName;
429         @NearbyDevice.Medium private int mMedium;
430         private int mTxPower;
431         private int mRssi;
432         private int mAction;
433         private PublicCredential mPublicCredential;
434         @ScanRequest.ScanType int mScanType;
435         @Nullable private String mFastPairModelId;
436         @Nullable private String mBluetoothAddress;
437         @Nullable private byte[] mData;
438         @Nullable private byte[] mSalt;
439         @Nullable private PresenceDevice mPresenceDevice;
440         @Nullable private byte[] mEncryptionKeyTag;
441 
442         /** Sets the id of the device. */
setDeviceId(long deviceId)443         public Builder setDeviceId(long deviceId) {
444             this.mDeviceId = deviceId;
445             return this;
446         }
447 
448         /**
449          * Sets the scan type of the NearbyDeviceParcelable.
450          *
451          * @hide
452          */
setScanType(@canRequest.ScanType int scanType)453         public Builder setScanType(@ScanRequest.ScanType int scanType) {
454             mScanType = scanType;
455             return this;
456         }
457 
458         /**
459          * Sets the name of the scanned device.
460          *
461          * @param name The local name of the scanned device.
462          */
463         @NonNull
setName(@ullable String name)464         public Builder setName(@Nullable String name) {
465             mName = name;
466             return this;
467         }
468 
469         /**
470          * Sets the medium over which the device is discovered.
471          *
472          * @param medium The {@link NearbyDevice.Medium} over which the device is discovered.
473          */
474         @NonNull
setMedium(@earbyDevice.Medium int medium)475         public Builder setMedium(@NearbyDevice.Medium int medium) {
476             mMedium = medium;
477             return this;
478         }
479 
480         /**
481          * Sets the transmission power of the discovered device.
482          *
483          * @param txPower The transmission power in dBm.
484          * @hide
485          */
486         @NonNull
setTxPower(int txPower)487         public Builder setTxPower(int txPower) {
488             mTxPower = txPower;
489             return this;
490         }
491 
492         /**
493          * Sets the RSSI between scanned device and the discovered device.
494          *
495          * @param rssi The received signal strength in dBm.
496          */
497         @NonNull
setRssi(@ntRangefrom = -127, to = 126) int rssi)498         public Builder setRssi(@IntRange(from = -127, to = 126) int rssi) {
499             mRssi = rssi;
500             return this;
501         }
502 
503         /**
504          * Sets the action from the discovered device.
505          *
506          * @param action The action of the discovered device.
507          * @hide
508          */
509         @NonNull
setAction(int action)510         public Builder setAction(int action) {
511             mAction = action;
512             return this;
513         }
514 
515         /**
516          * Sets the public credential of the discovered device.
517          *
518          * @param publicCredential The public credential.
519          * @hide
520          */
521         @NonNull
setPublicCredential(@onNull PublicCredential publicCredential)522         public Builder setPublicCredential(@NonNull PublicCredential publicCredential) {
523             mPublicCredential = publicCredential;
524             return this;
525         }
526 
527         /**
528          * Sets the Fast Pair model Id.
529          *
530          * @param fastPairModelId Fast Pair device identifier.
531          */
532         @NonNull
setFastPairModelId(@ullable String fastPairModelId)533         public Builder setFastPairModelId(@Nullable String fastPairModelId) {
534             mFastPairModelId = fastPairModelId;
535             return this;
536         }
537 
538         /**
539          * Sets the bluetooth address.
540          *
541          * @param bluetoothAddress The hardware address of the bluetooth device.
542          */
543         @NonNull
setBluetoothAddress(@ullable String bluetoothAddress)544         public Builder setBluetoothAddress(@Nullable String bluetoothAddress) {
545             mBluetoothAddress = bluetoothAddress;
546             return this;
547         }
548 
549         /**
550          * Sets the scanned raw data.
551          *
552          * @param data raw data scanned, like {@link ScanRecord#getServiceData()} if scanned by
553          *             Bluetooth.
554          */
555         @NonNull
setData(@ullable byte[] data)556         public Builder setData(@Nullable byte[] data) {
557             mData = data;
558             return this;
559         }
560 
561         /**
562          * Sets the encryption key tag calculated from the advertisement.
563          *
564          * @param encryptionKeyTag calculated from identity scanned from the advertisement
565          */
566         @NonNull
setEncryptionKeyTag(@ullable byte[] encryptionKeyTag)567         public Builder setEncryptionKeyTag(@Nullable byte[] encryptionKeyTag) {
568             mEncryptionKeyTag = encryptionKeyTag;
569             return this;
570         }
571 
572         /**
573          * Sets the slat in the advertisement from the Nearby Presence device.
574          *
575          * @param salt in the advertisement from the Nearby Presence device.
576          */
577         @NonNull
setSalt(@ullable byte[] salt)578         public Builder setSalt(@Nullable byte[] salt) {
579             mSalt = salt;
580             return this;
581         }
582 
583         /**
584          * Sets the {@link PresenceDevice} if there is any.
585          * The current {@link NearbyDeviceParcelable} can be seen as the wrapper of the
586          * {@link PresenceDevice}.
587          */
588         @Nullable
setPresenceDevice(@ullable PresenceDevice presenceDevice)589         public Builder setPresenceDevice(@Nullable PresenceDevice presenceDevice) {
590             mPresenceDevice = presenceDevice;
591             return this;
592         }
593 
594         /** Builds a ScanResult. */
595         @NonNull
build()596         public NearbyDeviceParcelable build() {
597             return new NearbyDeviceParcelable(
598                     mDeviceId,
599                     mScanType,
600                     mName,
601                     mMedium,
602                     mTxPower,
603                     mRssi,
604                     mAction,
605                     mPublicCredential,
606                     mFastPairModelId,
607                     mBluetoothAddress,
608                     mData,
609                     mSalt,
610                     mPresenceDevice,
611                     mEncryptionKeyTag);
612         }
613     }
614 }
615