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