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