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