• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.bluetooth.le;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.bluetooth.BluetoothDevice;
22 import android.content.Attributable;
23 import android.content.AttributionSource;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 
27 import java.util.Objects;
28 
29 /**
30  * ScanResult for Bluetooth LE scan.
31  */
32 public final class ScanResult implements Parcelable, Attributable {
33 
34     /**
35      * For chained advertisements, inidcates tha the data contained in this
36      * scan result is complete.
37      */
38     public static final int DATA_COMPLETE = 0x00;
39 
40     /**
41      * For chained advertisements, indicates that the controller was
42      * unable to receive all chained packets and the scan result contains
43      * incomplete truncated data.
44      */
45     public static final int DATA_TRUNCATED = 0x02;
46 
47     /**
48      * Indicates that the secondary physical layer was not used.
49      */
50     public static final int PHY_UNUSED = 0x00;
51 
52     /**
53      * Advertising Set ID is not present in the packet.
54      */
55     public static final int SID_NOT_PRESENT = 0xFF;
56 
57     /**
58      * TX power is not present in the packet.
59      */
60     public static final int TX_POWER_NOT_PRESENT = 0x7F;
61 
62     /**
63      * Periodic advertising interval is not present in the packet.
64      */
65     public static final int PERIODIC_INTERVAL_NOT_PRESENT = 0x00;
66 
67     /**
68      * Mask for checking whether event type represents legacy advertisement.
69      */
70     private static final int ET_LEGACY_MASK = 0x10;
71 
72     /**
73      * Mask for checking whether event type represents connectable advertisement.
74      */
75     private static final int ET_CONNECTABLE_MASK = 0x01;
76 
77     // Remote Bluetooth device.
78     private BluetoothDevice mDevice;
79 
80     // Scan record, including advertising data and scan response data.
81     @Nullable
82     private ScanRecord mScanRecord;
83 
84     // Received signal strength.
85     private int mRssi;
86 
87     // Device timestamp when the result was last seen.
88     private long mTimestampNanos;
89 
90     private int mEventType;
91     private int mPrimaryPhy;
92     private int mSecondaryPhy;
93     private int mAdvertisingSid;
94     private int mTxPower;
95     private int mPeriodicAdvertisingInterval;
96 
97     /**
98      * Constructs a new ScanResult.
99      *
100      * @param device Remote Bluetooth device found.
101      * @param scanRecord Scan record including both advertising data and scan response data.
102      * @param rssi Received signal strength.
103      * @param timestampNanos Timestamp at which the scan result was observed.
104      * @deprecated use {@link #ScanResult(BluetoothDevice, int, int, int, int, int, int, int,
105      * ScanRecord, long)}
106      */
107     @Deprecated
ScanResult(BluetoothDevice device, ScanRecord scanRecord, int rssi, long timestampNanos)108     public ScanResult(BluetoothDevice device, ScanRecord scanRecord, int rssi,
109             long timestampNanos) {
110         mDevice = device;
111         mScanRecord = scanRecord;
112         mRssi = rssi;
113         mTimestampNanos = timestampNanos;
114         mEventType = (DATA_COMPLETE << 5) | ET_LEGACY_MASK | ET_CONNECTABLE_MASK;
115         mPrimaryPhy = BluetoothDevice.PHY_LE_1M;
116         mSecondaryPhy = PHY_UNUSED;
117         mAdvertisingSid = SID_NOT_PRESENT;
118         mTxPower = 127;
119         mPeriodicAdvertisingInterval = 0;
120     }
121 
122     /**
123      * Constructs a new ScanResult.
124      *
125      * @param device Remote Bluetooth device found.
126      * @param eventType Event type.
127      * @param primaryPhy Primary advertising phy.
128      * @param secondaryPhy Secondary advertising phy.
129      * @param advertisingSid Advertising set ID.
130      * @param txPower Transmit power.
131      * @param rssi Received signal strength.
132      * @param periodicAdvertisingInterval Periodic advertising interval.
133      * @param scanRecord Scan record including both advertising data and scan response data.
134      * @param timestampNanos Timestamp at which the scan result was observed.
135      */
ScanResult(BluetoothDevice device, int eventType, int primaryPhy, int secondaryPhy, int advertisingSid, int txPower, int rssi, int periodicAdvertisingInterval, ScanRecord scanRecord, long timestampNanos)136     public ScanResult(BluetoothDevice device, int eventType, int primaryPhy, int secondaryPhy,
137             int advertisingSid, int txPower, int rssi, int periodicAdvertisingInterval,
138             ScanRecord scanRecord, long timestampNanos) {
139         mDevice = device;
140         mEventType = eventType;
141         mPrimaryPhy = primaryPhy;
142         mSecondaryPhy = secondaryPhy;
143         mAdvertisingSid = advertisingSid;
144         mTxPower = txPower;
145         mRssi = rssi;
146         mPeriodicAdvertisingInterval = periodicAdvertisingInterval;
147         mScanRecord = scanRecord;
148         mTimestampNanos = timestampNanos;
149     }
150 
ScanResult(Parcel in)151     private ScanResult(Parcel in) {
152         readFromParcel(in);
153     }
154 
155     @Override
writeToParcel(Parcel dest, int flags)156     public void writeToParcel(Parcel dest, int flags) {
157         if (mDevice != null) {
158             dest.writeInt(1);
159             mDevice.writeToParcel(dest, flags);
160         } else {
161             dest.writeInt(0);
162         }
163         if (mScanRecord != null) {
164             dest.writeInt(1);
165             dest.writeByteArray(mScanRecord.getBytes());
166         } else {
167             dest.writeInt(0);
168         }
169         dest.writeInt(mRssi);
170         dest.writeLong(mTimestampNanos);
171         dest.writeInt(mEventType);
172         dest.writeInt(mPrimaryPhy);
173         dest.writeInt(mSecondaryPhy);
174         dest.writeInt(mAdvertisingSid);
175         dest.writeInt(mTxPower);
176         dest.writeInt(mPeriodicAdvertisingInterval);
177     }
178 
readFromParcel(Parcel in)179     private void readFromParcel(Parcel in) {
180         if (in.readInt() == 1) {
181             mDevice = BluetoothDevice.CREATOR.createFromParcel(in);
182         }
183         if (in.readInt() == 1) {
184             mScanRecord = ScanRecord.parseFromBytes(in.createByteArray());
185         }
186         mRssi = in.readInt();
187         mTimestampNanos = in.readLong();
188         mEventType = in.readInt();
189         mPrimaryPhy = in.readInt();
190         mSecondaryPhy = in.readInt();
191         mAdvertisingSid = in.readInt();
192         mTxPower = in.readInt();
193         mPeriodicAdvertisingInterval = in.readInt();
194     }
195 
196     @Override
describeContents()197     public int describeContents() {
198         return 0;
199     }
200 
201     /** {@hide} */
setAttributionSource(@onNull AttributionSource attributionSource)202     public void setAttributionSource(@NonNull AttributionSource attributionSource) {
203         Attributable.setAttributionSource(mDevice, attributionSource);
204     }
205 
206     /**
207      * Returns the remote Bluetooth device identified by the Bluetooth device address.
208      */
getDevice()209     public BluetoothDevice getDevice() {
210         return mDevice;
211     }
212 
213     /**
214      * Returns the scan record, which is a combination of advertisement and scan response.
215      */
216     @Nullable
getScanRecord()217     public ScanRecord getScanRecord() {
218         return mScanRecord;
219     }
220 
221     /**
222      * Returns the received signal strength in dBm. The valid range is [-127, 126].
223      */
getRssi()224     public int getRssi() {
225         return mRssi;
226     }
227 
228     /**
229      * Returns timestamp since boot when the scan record was observed.
230      */
getTimestampNanos()231     public long getTimestampNanos() {
232         return mTimestampNanos;
233     }
234 
235     /**
236      * Returns true if this object represents legacy scan result.
237      * Legacy scan results do not contain advanced advertising information
238      * as specified in the Bluetooth Core Specification v5.
239      */
isLegacy()240     public boolean isLegacy() {
241         return (mEventType & ET_LEGACY_MASK) != 0;
242     }
243 
244     /**
245      * Returns true if this object represents connectable scan result.
246      */
isConnectable()247     public boolean isConnectable() {
248         return (mEventType & ET_CONNECTABLE_MASK) != 0;
249     }
250 
251     /**
252      * Returns the data status.
253      * Can be one of {@link ScanResult#DATA_COMPLETE} or
254      * {@link ScanResult#DATA_TRUNCATED}.
255      */
getDataStatus()256     public int getDataStatus() {
257         // return bit 5 and 6
258         return (mEventType >> 5) & 0x03;
259     }
260 
261     /**
262      * Returns the primary Physical Layer
263      * on which this advertisment was received.
264      * Can be one of {@link BluetoothDevice#PHY_LE_1M} or
265      * {@link BluetoothDevice#PHY_LE_CODED}.
266      */
getPrimaryPhy()267     public int getPrimaryPhy() {
268         return mPrimaryPhy;
269     }
270 
271     /**
272      * Returns the secondary Physical Layer
273      * on which this advertisment was received.
274      * Can be one of {@link BluetoothDevice#PHY_LE_1M},
275      * {@link BluetoothDevice#PHY_LE_2M}, {@link BluetoothDevice#PHY_LE_CODED}
276      * or {@link ScanResult#PHY_UNUSED} - if the advertisement
277      * was not received on a secondary physical channel.
278      */
getSecondaryPhy()279     public int getSecondaryPhy() {
280         return mSecondaryPhy;
281     }
282 
283     /**
284      * Returns the advertising set id.
285      * May return {@link ScanResult#SID_NOT_PRESENT} if
286      * no set id was is present.
287      */
getAdvertisingSid()288     public int getAdvertisingSid() {
289         return mAdvertisingSid;
290     }
291 
292     /**
293      * Returns the transmit power in dBm.
294      * Valid range is [-127, 126]. A value of {@link ScanResult#TX_POWER_NOT_PRESENT}
295      * indicates that the TX power is not present.
296      */
getTxPower()297     public int getTxPower() {
298         return mTxPower;
299     }
300 
301     /**
302      * Returns the periodic advertising interval in units of 1.25ms.
303      * Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of
304      * {@link ScanResult#PERIODIC_INTERVAL_NOT_PRESENT} means periodic
305      * advertising interval is not present.
306      */
getPeriodicAdvertisingInterval()307     public int getPeriodicAdvertisingInterval() {
308         return mPeriodicAdvertisingInterval;
309     }
310 
311     @Override
hashCode()312     public int hashCode() {
313         return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos,
314                 mEventType, mPrimaryPhy, mSecondaryPhy,
315                 mAdvertisingSid, mTxPower,
316                 mPeriodicAdvertisingInterval);
317     }
318 
319     @Override
equals(@ullable Object obj)320     public boolean equals(@Nullable Object obj) {
321         if (this == obj) {
322             return true;
323         }
324         if (obj == null || getClass() != obj.getClass()) {
325             return false;
326         }
327         ScanResult other = (ScanResult) obj;
328         return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi)
329                 && Objects.equals(mScanRecord, other.mScanRecord)
330                 && (mTimestampNanos == other.mTimestampNanos)
331                 && mEventType == other.mEventType
332                 && mPrimaryPhy == other.mPrimaryPhy
333                 && mSecondaryPhy == other.mSecondaryPhy
334                 && mAdvertisingSid == other.mAdvertisingSid
335                 && mTxPower == other.mTxPower
336                 && mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval;
337     }
338 
339     @Override
toString()340     public String toString() {
341         return "ScanResult{" + "device=" + mDevice + ", scanRecord="
342                 + Objects.toString(mScanRecord) + ", rssi=" + mRssi
343                 + ", timestampNanos=" + mTimestampNanos + ", eventType=" + mEventType
344                 + ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy
345                 + ", advertisingSid=" + mAdvertisingSid + ", txPower=" + mTxPower
346                 + ", periodicAdvertisingInterval=" + mPeriodicAdvertisingInterval + '}';
347     }
348 
349     public static final @android.annotation.NonNull Parcelable.Creator<ScanResult> CREATOR = new Creator<ScanResult>() {
350         @Override
351         public ScanResult createFromParcel(Parcel source) {
352             return new ScanResult(source);
353         }
354 
355         @Override
356         public ScanResult[] newArray(int size) {
357             return new ScanResult[size];
358         }
359     };
360 
361 }
362