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 > 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} < 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