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.SystemApi;
20 import android.bluetooth.BluetoothDevice;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 
24 /**
25  * Bluetooth LE scan settings are passed to {@link BluetoothLeScanner#startScan} to define the
26  * parameters for the scan.
27  */
28 public final class ScanSettings implements Parcelable {
29 
30     /**
31      * A special Bluetooth LE scan mode. Applications using this scan mode will passively listen for
32      * other scan results without starting BLE scans themselves.
33      */
34     public static final int SCAN_MODE_OPPORTUNISTIC = -1;
35 
36     /**
37      * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the
38      * least power. This mode is enforced if the scanning application is not in foreground.
39      */
40     public static final int SCAN_MODE_LOW_POWER = 0;
41 
42     /**
43      * Perform Bluetooth LE scan in balanced power mode. Scan results are returned at a rate that
44      * provides a good trade-off between scan frequency and power consumption.
45      */
46     public static final int SCAN_MODE_BALANCED = 1;
47 
48     /**
49      * Scan using highest duty cycle. It's recommended to only use this mode when the application is
50      * running in the foreground.
51      */
52     public static final int SCAN_MODE_LOW_LATENCY = 2;
53 
54     /**
55      * Trigger a callback for every Bluetooth advertisement found that matches the filter criteria.
56      * If no filter is active, all advertisement packets are reported.
57      */
58     public static final int CALLBACK_TYPE_ALL_MATCHES = 1;
59 
60     /**
61      * A result callback is only triggered for the first advertisement packet received that matches
62      * the filter criteria.
63      */
64     public static final int CALLBACK_TYPE_FIRST_MATCH = 2;
65 
66     /**
67      * Receive a callback when advertisements are no longer received from a device that has been
68      * previously reported by a first match callback.
69      */
70     public static final int CALLBACK_TYPE_MATCH_LOST = 4;
71 
72 
73     /**
74      * Determines how many advertisements to match per filter, as this is scarce hw resource
75      */
76     /**
77      * Match one advertisement per filter
78      */
79     public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1;
80 
81     /**
82      * Match few advertisement per filter, depends on current capability and availibility of
83      * the resources in hw
84      */
85     public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2;
86 
87     /**
88      * Match as many advertisement per filter as hw could allow, depends on current
89      * capability and availibility of the resources in hw
90      */
91     public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3;
92 
93 
94     /**
95      * In Aggressive mode, hw will determine a match sooner even with feeble signal strength
96      * and few number of sightings/match in a duration.
97      */
98     public static final int MATCH_MODE_AGGRESSIVE = 1;
99 
100     /**
101      * For sticky mode, higher threshold of signal strength and sightings is required
102      * before reporting by hw
103      */
104     public static final int MATCH_MODE_STICKY = 2;
105 
106     /**
107      * Request full scan results which contain the device, rssi, advertising data, scan response
108      * as well as the scan timestamp.
109      *
110      * @hide
111      */
112     @SystemApi
113     public static final int SCAN_RESULT_TYPE_FULL = 0;
114 
115     /**
116      * Request abbreviated scan results which contain the device, rssi and scan timestamp.
117      * <p>
118      * <b>Note:</b> It is possible for an application to get more scan results than it asked for, if
119      * there are multiple apps using this type.
120      *
121      * @hide
122      */
123     @SystemApi
124     public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1;
125 
126     /**
127      * Use all supported PHYs for scanning.
128      * This will check the controller capabilities, and start
129      * the scan on 1Mbit and LE Coded PHYs if supported, or on
130      * the 1Mbit PHY only.
131      */
132     public static final int PHY_LE_ALL_SUPPORTED = 255;
133 
134     // Bluetooth LE scan mode.
135     private int mScanMode;
136 
137     // Bluetooth LE scan callback type
138     private int mCallbackType;
139 
140     // Bluetooth LE scan result type
141     private int mScanResultType;
142 
143     // Time of delay for reporting the scan result
144     private long mReportDelayMillis;
145 
146     private int mMatchMode;
147 
148     private int mNumOfMatchesPerFilter;
149 
150     // Include only legacy advertising results
151     private boolean mLegacy;
152 
153     private int mPhy;
154 
getScanMode()155     public int getScanMode() {
156         return mScanMode;
157     }
158 
getCallbackType()159     public int getCallbackType() {
160         return mCallbackType;
161     }
162 
getScanResultType()163     public int getScanResultType() {
164         return mScanResultType;
165     }
166 
167     /**
168      * @hide
169      */
getMatchMode()170     public int getMatchMode() {
171         return mMatchMode;
172     }
173 
174     /**
175      * @hide
176      */
getNumOfMatches()177     public int getNumOfMatches() {
178         return mNumOfMatchesPerFilter;
179     }
180 
181     /**
182      * Returns whether only legacy advertisements will be returned.
183      * Legacy advertisements include advertisements as specified
184      * by the Bluetooth core specification 4.2 and below.
185      */
getLegacy()186     public boolean getLegacy() {
187         return mLegacy;
188     }
189 
190     /**
191      * Returns the physical layer used during a scan.
192      */
getPhy()193     public int getPhy() {
194         return mPhy;
195     }
196 
197     /**
198      * Returns report delay timestamp based on the device clock.
199      */
getReportDelayMillis()200     public long getReportDelayMillis() {
201         return mReportDelayMillis;
202     }
203 
ScanSettings(int scanMode, int callbackType, int scanResultType, long reportDelayMillis, int matchMode, int numOfMatchesPerFilter, boolean legacy, int phy)204     private ScanSettings(int scanMode, int callbackType, int scanResultType,
205             long reportDelayMillis, int matchMode,
206             int numOfMatchesPerFilter, boolean legacy, int phy) {
207         mScanMode = scanMode;
208         mCallbackType = callbackType;
209         mScanResultType = scanResultType;
210         mReportDelayMillis = reportDelayMillis;
211         mNumOfMatchesPerFilter = numOfMatchesPerFilter;
212         mMatchMode = matchMode;
213         mLegacy = legacy;
214         mPhy = phy;
215     }
216 
ScanSettings(Parcel in)217     private ScanSettings(Parcel in) {
218         mScanMode = in.readInt();
219         mCallbackType = in.readInt();
220         mScanResultType = in.readInt();
221         mReportDelayMillis = in.readLong();
222         mMatchMode = in.readInt();
223         mNumOfMatchesPerFilter = in.readInt();
224         mLegacy = in.readInt() != 0;
225         mPhy = in.readInt();
226     }
227 
228     @Override
writeToParcel(Parcel dest, int flags)229     public void writeToParcel(Parcel dest, int flags) {
230         dest.writeInt(mScanMode);
231         dest.writeInt(mCallbackType);
232         dest.writeInt(mScanResultType);
233         dest.writeLong(mReportDelayMillis);
234         dest.writeInt(mMatchMode);
235         dest.writeInt(mNumOfMatchesPerFilter);
236         dest.writeInt(mLegacy ? 1 : 0);
237         dest.writeInt(mPhy);
238     }
239 
240     @Override
describeContents()241     public int describeContents() {
242         return 0;
243     }
244 
245     public static final @android.annotation.NonNull Parcelable.Creator<ScanSettings> CREATOR =
246             new Creator<ScanSettings>() {
247         @Override
248         public ScanSettings[] newArray(int size) {
249             return new ScanSettings[size];
250         }
251 
252         @Override
253         public ScanSettings createFromParcel(Parcel in) {
254             return new ScanSettings(in);
255         }
256     };
257 
258     /**
259      * Builder for {@link ScanSettings}.
260      */
261     public static final class Builder {
262         private int mScanMode = SCAN_MODE_LOW_POWER;
263         private int mCallbackType = CALLBACK_TYPE_ALL_MATCHES;
264         private int mScanResultType = SCAN_RESULT_TYPE_FULL;
265         private long mReportDelayMillis = 0;
266         private int mMatchMode = MATCH_MODE_AGGRESSIVE;
267         private int mNumOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT;
268         private boolean mLegacy = true;
269         private int mPhy = PHY_LE_ALL_SUPPORTED;
270 
271         /**
272          * Set scan mode for Bluetooth LE scan.
273          *
274          * @param scanMode The scan mode can be one of {@link ScanSettings#SCAN_MODE_LOW_POWER},
275          * {@link ScanSettings#SCAN_MODE_BALANCED} or {@link ScanSettings#SCAN_MODE_LOW_LATENCY}.
276          * @throws IllegalArgumentException If the {@code scanMode} is invalid.
277          */
setScanMode(int scanMode)278         public Builder setScanMode(int scanMode) {
279             if (scanMode < SCAN_MODE_OPPORTUNISTIC || scanMode > SCAN_MODE_LOW_LATENCY) {
280                 throw new IllegalArgumentException("invalid scan mode " + scanMode);
281             }
282             mScanMode = scanMode;
283             return this;
284         }
285 
286         /**
287          * Set callback type for Bluetooth LE scan.
288          *
289          * @param callbackType The callback type flags for the scan.
290          * @throws IllegalArgumentException If the {@code callbackType} is invalid.
291          */
setCallbackType(int callbackType)292         public Builder setCallbackType(int callbackType) {
293 
294             if (!isValidCallbackType(callbackType)) {
295                 throw new IllegalArgumentException("invalid callback type - " + callbackType);
296             }
297             mCallbackType = callbackType;
298             return this;
299         }
300 
301         // Returns true if the callbackType is valid.
isValidCallbackType(int callbackType)302         private boolean isValidCallbackType(int callbackType) {
303             if (callbackType == CALLBACK_TYPE_ALL_MATCHES
304                     || callbackType == CALLBACK_TYPE_FIRST_MATCH
305                     || callbackType == CALLBACK_TYPE_MATCH_LOST) {
306                 return true;
307             }
308             return callbackType == (CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST);
309         }
310 
311         /**
312          * Set scan result type for Bluetooth LE scan.
313          *
314          * @param scanResultType Type for scan result, could be either {@link
315          * ScanSettings#SCAN_RESULT_TYPE_FULL} or {@link ScanSettings#SCAN_RESULT_TYPE_ABBREVIATED}.
316          * @throws IllegalArgumentException If the {@code scanResultType} is invalid.
317          * @hide
318          */
319         @SystemApi
setScanResultType(int scanResultType)320         public Builder setScanResultType(int scanResultType) {
321             if (scanResultType < SCAN_RESULT_TYPE_FULL
322                     || scanResultType > SCAN_RESULT_TYPE_ABBREVIATED) {
323                 throw new IllegalArgumentException(
324                         "invalid scanResultType - " + scanResultType);
325             }
326             mScanResultType = scanResultType;
327             return this;
328         }
329 
330         /**
331          * Set report delay timestamp for Bluetooth LE scan.
332          *
333          * @param reportDelayMillis Delay of report in milliseconds. Set to 0 to be notified of
334          * results immediately. Values &gt; 0 causes the scan results to be queued up and delivered
335          * after the requested delay or when the internal buffers fill up.
336          * @throws IllegalArgumentException If {@code reportDelayMillis} &lt; 0.
337          */
setReportDelay(long reportDelayMillis)338         public Builder setReportDelay(long reportDelayMillis) {
339             if (reportDelayMillis < 0) {
340                 throw new IllegalArgumentException("reportDelay must be > 0");
341             }
342             mReportDelayMillis = reportDelayMillis;
343             return this;
344         }
345 
346         /**
347          * Set the number of matches for Bluetooth LE scan filters hardware match
348          *
349          * @param numOfMatches The num of matches can be one of
350          * {@link ScanSettings#MATCH_NUM_ONE_ADVERTISEMENT}
351          * or {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or {@link
352          * ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT}
353          * @throws IllegalArgumentException If the {@code matchMode} is invalid.
354          */
setNumOfMatches(int numOfMatches)355         public Builder setNumOfMatches(int numOfMatches) {
356             if (numOfMatches < MATCH_NUM_ONE_ADVERTISEMENT
357                     || numOfMatches > MATCH_NUM_MAX_ADVERTISEMENT) {
358                 throw new IllegalArgumentException("invalid numOfMatches " + numOfMatches);
359             }
360             mNumOfMatchesPerFilter = numOfMatches;
361             return this;
362         }
363 
364         /**
365          * Set match mode for Bluetooth LE scan filters hardware match
366          *
367          * @param matchMode The match mode can be one of {@link ScanSettings#MATCH_MODE_AGGRESSIVE}
368          * or {@link ScanSettings#MATCH_MODE_STICKY}
369          * @throws IllegalArgumentException If the {@code matchMode} is invalid.
370          */
setMatchMode(int matchMode)371         public Builder setMatchMode(int matchMode) {
372             if (matchMode < MATCH_MODE_AGGRESSIVE
373                     || matchMode > MATCH_MODE_STICKY) {
374                 throw new IllegalArgumentException("invalid matchMode " + matchMode);
375             }
376             mMatchMode = matchMode;
377             return this;
378         }
379 
380         /**
381          * Set whether only legacy advertisments should be returned in scan results.
382          * Legacy advertisements include advertisements as specified by the
383          * Bluetooth core specification 4.2 and below. This is true by default
384          * for compatibility with older apps.
385          *
386          * @param legacy true if only legacy advertisements will be returned
387          */
setLegacy(boolean legacy)388         public Builder setLegacy(boolean legacy) {
389             mLegacy = legacy;
390             return this;
391         }
392 
393         /**
394          * Set the Physical Layer to use during this scan.
395          * This is used only if {@link ScanSettings.Builder#setLegacy}
396          * is set to false.
397          * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}
398          * may be used to check whether LE Coded phy is supported by calling
399          * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}.
400          * Selecting an unsupported phy will result in failure to start scan.
401          *
402          * @param phy Can be one of {@link BluetoothDevice#PHY_LE_1M}, {@link
403          * BluetoothDevice#PHY_LE_CODED} or {@link ScanSettings#PHY_LE_ALL_SUPPORTED}
404          */
setPhy(int phy)405         public Builder setPhy(int phy) {
406             mPhy = phy;
407             return this;
408         }
409 
410         /**
411          * Build {@link ScanSettings}.
412          */
build()413         public ScanSettings build() {
414             return new ScanSettings(mScanMode, mCallbackType, mScanResultType,
415                     mReportDelayMillis, mMatchMode,
416                     mNumOfMatchesPerFilter, mLegacy, mPhy);
417         }
418     }
419 }
420