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.
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 ? true : false;
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 Parcelable.Creator<ScanSettings>
246             CREATOR = 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
276          *            {@link ScanSettings#SCAN_MODE_LOW_LATENCY}.
277          * @throws IllegalArgumentException If the {@code scanMode} is invalid.
278          */
setScanMode(int scanMode)279         public Builder setScanMode(int scanMode) {
280             if (scanMode < SCAN_MODE_OPPORTUNISTIC || scanMode > SCAN_MODE_LOW_LATENCY) {
281                 throw new IllegalArgumentException("invalid scan mode " + scanMode);
282             }
283             mScanMode = scanMode;
284             return this;
285         }
286 
287         /**
288          * Set callback type for Bluetooth LE scan.
289          *
290          * @param callbackType The callback type flags for the scan.
291          * @throws IllegalArgumentException If the {@code callbackType} is invalid.
292          */
setCallbackType(int callbackType)293         public Builder setCallbackType(int callbackType) {
294 
295             if (!isValidCallbackType(callbackType)) {
296                 throw new IllegalArgumentException("invalid callback type - " + callbackType);
297             }
298             mCallbackType = callbackType;
299             return this;
300         }
301 
302         // Returns true if the callbackType is valid.
isValidCallbackType(int callbackType)303         private boolean isValidCallbackType(int callbackType) {
304             if (callbackType == CALLBACK_TYPE_ALL_MATCHES ||
305                     callbackType == CALLBACK_TYPE_FIRST_MATCH ||
306                     callbackType == CALLBACK_TYPE_MATCH_LOST) {
307                 return true;
308             }
309             return callbackType == (CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST);
310         }
311 
312         /**
313          * Set scan result type for Bluetooth LE scan.
314          *
315          * @param scanResultType Type for scan result, could be either
316          *            {@link ScanSettings#SCAN_RESULT_TYPE_FULL} or
317          *            {@link ScanSettings#SCAN_RESULT_TYPE_ABBREVIATED}.
318          * @throws IllegalArgumentException If the {@code scanResultType} is invalid.
319          * @hide
320          */
321         @SystemApi
setScanResultType(int scanResultType)322         public Builder setScanResultType(int scanResultType) {
323             if (scanResultType < SCAN_RESULT_TYPE_FULL
324                     || scanResultType > SCAN_RESULT_TYPE_ABBREVIATED) {
325                 throw new IllegalArgumentException(
326                         "invalid scanResultType - " + scanResultType);
327             }
328             mScanResultType = scanResultType;
329             return this;
330         }
331 
332         /**
333          * Set report delay timestamp for Bluetooth LE scan.
334          *
335          * @param reportDelayMillis Delay of report in milliseconds. Set to 0 to be notified of
336          *            results immediately. Values &gt; 0 causes the scan results to be queued up and
337          *            delivered after the requested delay or when the internal buffers fill up.
338          * @throws IllegalArgumentException If {@code reportDelayMillis} &lt; 0.
339          */
setReportDelay(long reportDelayMillis)340         public Builder setReportDelay(long reportDelayMillis) {
341             if (reportDelayMillis < 0) {
342                 throw new IllegalArgumentException("reportDelay must be > 0");
343             }
344             mReportDelayMillis = reportDelayMillis;
345             return this;
346         }
347 
348         /**
349          * Set the number of matches for Bluetooth LE scan filters hardware match
350          *
351          * @param numOfMatches The num of matches can be one of
352          *              {@link ScanSettings#MATCH_NUM_ONE_ADVERTISEMENT} or
353          *              {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or
354          *              {@link ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT}
355          * @throws IllegalArgumentException If the {@code matchMode} is invalid.
356          */
setNumOfMatches(int numOfMatches)357         public Builder setNumOfMatches(int numOfMatches) {
358             if (numOfMatches < MATCH_NUM_ONE_ADVERTISEMENT
359                     || numOfMatches > MATCH_NUM_MAX_ADVERTISEMENT) {
360                 throw new IllegalArgumentException("invalid numOfMatches " + numOfMatches);
361             }
362             mNumOfMatchesPerFilter = numOfMatches;
363             return this;
364         }
365 
366         /**
367          * Set match mode for Bluetooth LE scan filters hardware match
368          *
369          * @param matchMode The match mode can be one of
370          *              {@link ScanSettings#MATCH_MODE_AGGRESSIVE} or
371          *              {@link ScanSettings#MATCH_MODE_STICKY}
372          * @throws IllegalArgumentException If the {@code matchMode} is invalid.
373          */
setMatchMode(int matchMode)374         public Builder setMatchMode(int matchMode) {
375             if (matchMode < MATCH_MODE_AGGRESSIVE
376                     || matchMode > MATCH_MODE_STICKY) {
377                 throw new IllegalArgumentException("invalid matchMode " + matchMode);
378             }
379             mMatchMode = matchMode;
380             return this;
381         }
382 
383         /**
384          * Set whether only legacy advertisments should be returned in scan results.
385          * Legacy advertisements include advertisements as specified by the
386          * Bluetooth core specification 4.2 and below. This is true by default
387          * for compatibility with older apps.
388          *
389          * @param legacy true if only legacy advertisements will be returned
390          */
setLegacy(boolean legacy)391         public Builder setLegacy(boolean legacy) {
392             mLegacy = legacy;
393             return this;
394         }
395 
396         /**
397          * Set the Physical Layer to use during this scan.
398          * This is used only if {@link ScanSettings.Builder#setLegacy}
399          * is set to false.
400          * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}
401          * may be used to check whether LE Coded phy is supported by calling
402          * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}.
403          * Selecting an unsupported phy will result in failure to start scan.
404          *
405          * @param phy Can be one of
406          *   {@link BluetoothDevice#PHY_LE_1M},
407          *   {@link BluetoothDevice#PHY_LE_CODED} or
408          *   {@link ScanSettings#PHY_LE_ALL_SUPPORTED}
409          */
setPhy(int phy)410         public Builder setPhy(int phy) {
411             mPhy = phy;
412             return this;
413         }
414 
415         /**
416          * Build {@link ScanSettings}.
417          */
build()418         public ScanSettings build() {
419             return new ScanSettings(mScanMode, mCallbackType, mScanResultType,
420                                     mReportDelayMillis, mMatchMode,
421                                     mNumOfMatchesPerFilter, mLegacy, mPhy);
422         }
423     }
424 }
425