1 /* 2 * Copyright (C) 2017 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.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.SystemApi; 22 import android.bluetooth.BluetoothAdapter; 23 import android.bluetooth.BluetoothDevice; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 27 import java.lang.annotation.Retention; 28 import java.lang.annotation.RetentionPolicy; 29 30 /** 31 * The {@link AdvertisingSetParameters} provide a way to adjust advertising 32 * preferences for each 33 * Bluetooth LE advertising set. Use {@link AdvertisingSetParameters.Builder} to 34 * create an 35 * instance of this class. 36 */ 37 public final class AdvertisingSetParameters implements Parcelable { 38 39 /** 40 * Advertise on low frequency, around every 1000ms. This is the default and 41 * preferred advertising mode as it consumes the least power. 42 */ 43 public static final int INTERVAL_HIGH = 1600; 44 45 /** 46 * Advertise on medium frequency, around every 250ms. This is balanced 47 * between advertising frequency and power consumption. 48 */ 49 public static final int INTERVAL_MEDIUM = 400; 50 51 /** 52 * Perform high frequency, low latency advertising, around every 100ms. This 53 * has the highest power consumption and should not be used for continuous 54 * background advertising. 55 */ 56 public static final int INTERVAL_LOW = 160; 57 58 /** 59 * Minimum value for advertising interval. 60 */ 61 public static final int INTERVAL_MIN = 160; 62 63 /** 64 * Maximum value for advertising interval. 65 */ 66 public static final int INTERVAL_MAX = 16777215; 67 68 /** 69 * Advertise using the lowest transmission (TX) power level. Low transmission 70 * power can be used to restrict the visibility range of advertising packets. 71 */ 72 public static final int TX_POWER_ULTRA_LOW = -21; 73 74 /** 75 * Advertise using low TX power level. 76 */ 77 public static final int TX_POWER_LOW = -15; 78 79 /** 80 * Advertise using medium TX power level. 81 */ 82 public static final int TX_POWER_MEDIUM = -7; 83 84 /** 85 * Advertise using high TX power level. This corresponds to largest visibility 86 * range of the advertising packet. 87 */ 88 public static final int TX_POWER_HIGH = 1; 89 90 /** 91 * Minimum value for TX power. 92 */ 93 public static final int TX_POWER_MIN = -127; 94 95 /** 96 * Maximum value for TX power. 97 */ 98 public static final int TX_POWER_MAX = 1; 99 100 /** 101 * The maximum limited advertisement duration as specified by the Bluetooth 102 * SIG 103 */ 104 private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000; 105 106 /** @hide */ 107 @IntDef(prefix = "ADDRESS_TYPE_", value = { 108 ADDRESS_TYPE_DEFAULT, 109 ADDRESS_TYPE_PUBLIC, 110 ADDRESS_TYPE_RANDOM, 111 ADDRESS_TYPE_RANDOM_NON_RESOLVABLE, 112 }) 113 @Retention(RetentionPolicy.SOURCE) 114 public @interface AddressTypeStatus {} 115 116 /** 117 * Advertise own address type that corresponds privacy settings of the device. 118 * 119 * @hide 120 */ 121 @SystemApi 122 public static final int ADDRESS_TYPE_DEFAULT = -1; 123 124 /** 125 * Advertise own public address type. 126 * 127 * @hide 128 */ 129 @SystemApi 130 public static final int ADDRESS_TYPE_PUBLIC = 0; 131 132 /** 133 * Generate and adverise own resolvable private address. 134 * 135 * @hide 136 */ 137 @SystemApi 138 public static final int ADDRESS_TYPE_RANDOM = 1; 139 140 /** 141 * Generate and advertise on non-resolvable private address. 142 * 143 * @hide 144 */ 145 @SystemApi 146 public static final int ADDRESS_TYPE_RANDOM_NON_RESOLVABLE = 2; 147 148 private final boolean mIsLegacy; 149 private final boolean mIsAnonymous; 150 private final boolean mIncludeTxPower; 151 private final int mPrimaryPhy; 152 private final int mSecondaryPhy; 153 private final boolean mConnectable; 154 private final boolean mDiscoverable; 155 private final boolean mScannable; 156 private final int mInterval; 157 private final int mTxPowerLevel; 158 private final int mOwnAddressType; 159 AdvertisingSetParameters( boolean connectable, boolean discoverable, boolean scannable, boolean isLegacy, boolean isAnonymous, boolean includeTxPower, int primaryPhy, int secondaryPhy, int interval, int txPowerLevel, @AddressTypeStatus int ownAddressType)160 private AdvertisingSetParameters( 161 boolean connectable, 162 boolean discoverable, 163 boolean scannable, 164 boolean isLegacy, 165 boolean isAnonymous, 166 boolean includeTxPower, 167 int primaryPhy, 168 int secondaryPhy, 169 int interval, 170 int txPowerLevel, 171 @AddressTypeStatus int ownAddressType) { 172 mConnectable = connectable; 173 mDiscoverable = discoverable; 174 mScannable = scannable; 175 mIsLegacy = isLegacy; 176 mIsAnonymous = isAnonymous; 177 mIncludeTxPower = includeTxPower; 178 mPrimaryPhy = primaryPhy; 179 mSecondaryPhy = secondaryPhy; 180 mInterval = interval; 181 mTxPowerLevel = txPowerLevel; 182 mOwnAddressType = ownAddressType; 183 } 184 AdvertisingSetParameters(Parcel in)185 private AdvertisingSetParameters(Parcel in) { 186 mConnectable = in.readInt() != 0; 187 mScannable = in.readInt() != 0; 188 mIsLegacy = in.readInt() != 0; 189 mIsAnonymous = in.readInt() != 0; 190 mIncludeTxPower = in.readInt() != 0; 191 mPrimaryPhy = in.readInt(); 192 mSecondaryPhy = in.readInt(); 193 mInterval = in.readInt(); 194 mTxPowerLevel = in.readInt(); 195 mOwnAddressType = in.readInt(); 196 mDiscoverable = in.readInt() != 0; 197 } 198 199 /** 200 * Returns whether the advertisement will be connectable. 201 */ isConnectable()202 public boolean isConnectable() { 203 return mConnectable; 204 } 205 206 /** 207 * Returns whether the advertisement will be discoverable. 208 */ isDiscoverable()209 public boolean isDiscoverable() { 210 return mDiscoverable; 211 } 212 213 /** 214 * Returns whether the advertisement will be scannable. 215 */ isScannable()216 public boolean isScannable() { 217 return mScannable; 218 } 219 220 /** 221 * Returns whether the legacy advertisement will be used. 222 */ isLegacy()223 public boolean isLegacy() { 224 return mIsLegacy; 225 } 226 227 /** 228 * Returns whether the advertisement will be anonymous. 229 */ isAnonymous()230 public boolean isAnonymous() { 231 return mIsAnonymous; 232 } 233 234 /** 235 * Returns whether the TX Power will be included. 236 */ includeTxPower()237 public boolean includeTxPower() { 238 return mIncludeTxPower; 239 } 240 241 /** 242 * Returns the primary advertising phy. 243 */ getPrimaryPhy()244 public int getPrimaryPhy() { 245 return mPrimaryPhy; 246 } 247 248 /** 249 * Returns the secondary advertising phy. 250 */ getSecondaryPhy()251 public int getSecondaryPhy() { 252 return mSecondaryPhy; 253 } 254 255 /** 256 * Returns the advertising interval. 257 */ getInterval()258 public int getInterval() { 259 return mInterval; 260 } 261 262 /** 263 * Returns the TX power level for advertising. 264 */ getTxPowerLevel()265 public int getTxPowerLevel() { 266 return mTxPowerLevel; 267 } 268 269 /** 270 * @return the own address type for advertising 271 * 272 * @hide 273 */ 274 @SystemApi getOwnAddressType()275 public @AddressTypeStatus int getOwnAddressType() { 276 return mOwnAddressType; 277 } 278 279 @Override toString()280 public String toString() { 281 return "AdvertisingSetParameters [connectable=" + mConnectable 282 + ", discoverable=" + mDiscoverable 283 + ", isLegacy=" + mIsLegacy 284 + ", isAnonymous=" + mIsAnonymous 285 + ", includeTxPower=" + mIncludeTxPower 286 + ", primaryPhy=" + mPrimaryPhy 287 + ", secondaryPhy=" + mSecondaryPhy 288 + ", interval=" + mInterval 289 + ", txPowerLevel=" + mTxPowerLevel 290 + ", ownAddressType=" + mOwnAddressType + "]"; 291 } 292 293 @Override describeContents()294 public int describeContents() { 295 return 0; 296 } 297 298 @Override writeToParcel(Parcel dest, int flags)299 public void writeToParcel(Parcel dest, int flags) { 300 dest.writeInt(mConnectable ? 1 : 0); 301 dest.writeInt(mScannable ? 1 : 0); 302 dest.writeInt(mIsLegacy ? 1 : 0); 303 dest.writeInt(mIsAnonymous ? 1 : 0); 304 dest.writeInt(mIncludeTxPower ? 1 : 0); 305 dest.writeInt(mPrimaryPhy); 306 dest.writeInt(mSecondaryPhy); 307 dest.writeInt(mInterval); 308 dest.writeInt(mTxPowerLevel); 309 dest.writeInt(mOwnAddressType); 310 dest.writeInt(mDiscoverable ? 1 : 0); 311 } 312 313 public static final @android.annotation.NonNull Parcelable.Creator<AdvertisingSetParameters> CREATOR = 314 new Creator<AdvertisingSetParameters>() { 315 @Override 316 public AdvertisingSetParameters[] newArray(int size) { 317 return new AdvertisingSetParameters[size]; 318 } 319 320 @Override 321 public AdvertisingSetParameters createFromParcel(Parcel in) { 322 return new AdvertisingSetParameters(in); 323 } 324 }; 325 326 /** 327 * Builder class for {@link AdvertisingSetParameters}. 328 */ 329 public static final class Builder { 330 private boolean mConnectable = false; 331 private boolean mDiscoverable = true; 332 private boolean mScannable = false; 333 private boolean mIsLegacy = false; 334 private boolean mIsAnonymous = false; 335 private boolean mIncludeTxPower = false; 336 private int mPrimaryPhy = BluetoothDevice.PHY_LE_1M; 337 private int mSecondaryPhy = BluetoothDevice.PHY_LE_1M; 338 private int mInterval = INTERVAL_LOW; 339 private int mTxPowerLevel = TX_POWER_MEDIUM; 340 private int mOwnAddressType = ADDRESS_TYPE_DEFAULT; 341 342 /** 343 * Set whether the advertisement type should be connectable or 344 * non-connectable. 345 * Legacy advertisements can be both connectable and scannable. Non-legacy 346 * advertisements can be only scannable or only connectable. 347 * 348 * @param connectable Controls whether the advertisement type will be connectable (true) or 349 * non-connectable (false). 350 */ setConnectable(boolean connectable)351 public Builder setConnectable(boolean connectable) { 352 mConnectable = connectable; 353 return this; 354 } 355 356 /** 357 * Set whether the advertisement type should be discoverable or non-discoverable. By 358 * default, advertisements will be discoverable. Devices connecting to non-discoverable 359 * advertisements cannot initiate bonding. 360 * 361 * @param discoverable Controls whether the advertisement type will be discoverable 362 * ({@code true}) or non-discoverable ({@code false}). 363 */ setDiscoverable(boolean discoverable)364 public @NonNull Builder setDiscoverable(boolean discoverable) { 365 mDiscoverable = discoverable; 366 return this; 367 } 368 369 /** 370 * Set whether the advertisement type should be scannable. 371 * Legacy advertisements can be both connectable and scannable. Non-legacy 372 * advertisements can be only scannable or only connectable. 373 * 374 * @param scannable Controls whether the advertisement type will be scannable (true) or 375 * non-scannable (false). 376 */ setScannable(boolean scannable)377 public Builder setScannable(boolean scannable) { 378 mScannable = scannable; 379 return this; 380 } 381 382 /** 383 * When set to true, advertising set will advertise 4.x Spec compliant 384 * advertisements. 385 * 386 * @param isLegacy whether legacy advertising mode should be used. 387 */ setLegacyMode(boolean isLegacy)388 public Builder setLegacyMode(boolean isLegacy) { 389 mIsLegacy = isLegacy; 390 return this; 391 } 392 393 /** 394 * Set whether advertiser address should be ommited from all packets. If this 395 * mode is used, periodic advertising can't be enabled for this set. 396 * 397 * This is used only if legacy mode is not used. 398 * 399 * @param isAnonymous whether anonymous advertising should be used. 400 */ setAnonymous(boolean isAnonymous)401 public Builder setAnonymous(boolean isAnonymous) { 402 mIsAnonymous = isAnonymous; 403 return this; 404 } 405 406 /** 407 * Set whether TX power should be included in the extended header. 408 * 409 * This is used only if legacy mode is not used. 410 * 411 * @param includeTxPower whether TX power should be included in extended header 412 */ setIncludeTxPower(boolean includeTxPower)413 public Builder setIncludeTxPower(boolean includeTxPower) { 414 mIncludeTxPower = includeTxPower; 415 return this; 416 } 417 418 /** 419 * Set the primary physical channel used for this advertising set. 420 * 421 * This is used only if legacy mode is not used. 422 * 423 * Use {@link BluetoothAdapter#isLeCodedPhySupported} to determine if LE Coded PHY is 424 * supported on this device. 425 * 426 * @param primaryPhy Primary advertising physical channel, can only be {@link 427 * BluetoothDevice#PHY_LE_1M} or {@link BluetoothDevice#PHY_LE_CODED}. 428 * @throws IllegalArgumentException If the primaryPhy is invalid. 429 */ setPrimaryPhy(int primaryPhy)430 public Builder setPrimaryPhy(int primaryPhy) { 431 if (primaryPhy != BluetoothDevice.PHY_LE_1M 432 && primaryPhy != BluetoothDevice.PHY_LE_CODED) { 433 throw new IllegalArgumentException("bad primaryPhy " + primaryPhy); 434 } 435 mPrimaryPhy = primaryPhy; 436 return this; 437 } 438 439 /** 440 * Set the secondary physical channel used for this advertising set. 441 * 442 * This is used only if legacy mode is not used. 443 * 444 * Use {@link BluetoothAdapter#isLeCodedPhySupported} and 445 * {@link BluetoothAdapter#isLe2MPhySupported} to determine if LE Coded PHY or 2M PHY is 446 * supported on this device. 447 * 448 * @param secondaryPhy Secondary advertising physical channel, can only be one of {@link 449 * BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M} or {@link 450 * BluetoothDevice#PHY_LE_CODED}. 451 * @throws IllegalArgumentException If the secondaryPhy is invalid. 452 */ setSecondaryPhy(int secondaryPhy)453 public Builder setSecondaryPhy(int secondaryPhy) { 454 if (secondaryPhy != BluetoothDevice.PHY_LE_1M 455 && secondaryPhy != BluetoothDevice.PHY_LE_2M 456 && secondaryPhy != BluetoothDevice.PHY_LE_CODED) { 457 throw new IllegalArgumentException("bad secondaryPhy " + secondaryPhy); 458 } 459 mSecondaryPhy = secondaryPhy; 460 return this; 461 } 462 463 /** 464 * Set advertising interval. 465 * 466 * @param interval Bluetooth LE Advertising interval, in 0.625ms unit. Valid range is from 467 * 160 (100ms) to 16777215 (10,485.759375 s). Recommended values are: {@link 468 * AdvertisingSetParameters#INTERVAL_LOW}, {@link AdvertisingSetParameters#INTERVAL_MEDIUM}, 469 * or {@link AdvertisingSetParameters#INTERVAL_HIGH}. 470 * @throws IllegalArgumentException If the interval is invalid. 471 */ setInterval(int interval)472 public Builder setInterval(int interval) { 473 if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { 474 throw new IllegalArgumentException("unknown interval " + interval); 475 } 476 mInterval = interval; 477 return this; 478 } 479 480 /** 481 * Set the transmission power level for the advertising. 482 * 483 * @param txPowerLevel Transmission power of Bluetooth LE Advertising, in dBm. The valid 484 * range is [-127, 1] Recommended values are: 485 * {@link AdvertisingSetParameters#TX_POWER_ULTRA_LOW}, 486 * {@link AdvertisingSetParameters#TX_POWER_LOW}, 487 * {@link AdvertisingSetParameters#TX_POWER_MEDIUM}, 488 * or {@link AdvertisingSetParameters#TX_POWER_HIGH}. 489 * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid. 490 */ setTxPowerLevel(int txPowerLevel)491 public Builder setTxPowerLevel(int txPowerLevel) { 492 if (txPowerLevel < TX_POWER_MIN || txPowerLevel > TX_POWER_MAX) { 493 throw new IllegalArgumentException("unknown txPowerLevel " + txPowerLevel); 494 } 495 mTxPowerLevel = txPowerLevel; 496 return this; 497 } 498 499 /** 500 * Set own address type for advertising to control public or privacy mode. If used to set 501 * address type anything other than {@link AdvertisingSetParameters#ADDRESS_TYPE_DEFAULT}, 502 * then it will require BLUETOOTH_PRIVILEGED permission and will be checked at the 503 * time of starting advertising. 504 * 505 * @throws IllegalArgumentException If the {@code ownAddressType} is invalid 506 * 507 * @hide 508 */ 509 @SystemApi setOwnAddressType(@ddressTypeStatus int ownAddressType)510 public @NonNull Builder setOwnAddressType(@AddressTypeStatus int ownAddressType) { 511 if (ownAddressType < AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT 512 || ownAddressType 513 > AdvertisingSetParameters.ADDRESS_TYPE_RANDOM_NON_RESOLVABLE) { 514 throw new IllegalArgumentException("unknown address type " + ownAddressType); 515 } 516 mOwnAddressType = ownAddressType; 517 return this; 518 } 519 520 /** 521 * Build the {@link AdvertisingSetParameters} object. 522 * 523 * @throws IllegalStateException if invalid combination of parameters is used. 524 */ build()525 public AdvertisingSetParameters build() { 526 if (mIsLegacy) { 527 if (mIsAnonymous) { 528 throw new IllegalArgumentException("Legacy advertising can't be anonymous"); 529 } 530 531 if (mConnectable && !mScannable) { 532 throw new IllegalStateException( 533 "Legacy advertisement can't be connectable and non-scannable"); 534 } 535 536 if (mIncludeTxPower) { 537 throw new IllegalStateException( 538 "Legacy advertising can't include TX power level in header"); 539 } 540 } else { 541 if (mConnectable && mScannable) { 542 throw new IllegalStateException( 543 "Advertising can't be both connectable and scannable"); 544 } 545 546 if (mIsAnonymous && mConnectable) { 547 throw new IllegalStateException( 548 "Advertising can't be both connectable and anonymous"); 549 } 550 } 551 552 return new AdvertisingSetParameters( 553 mConnectable, 554 mDiscoverable, 555 mScannable, 556 mIsLegacy, 557 mIsAnonymous, 558 mIncludeTxPower, 559 mPrimaryPhy, 560 mSecondaryPhy, 561 mInterval, 562 mTxPowerLevel, 563 mOwnAddressType); 564 } 565 } 566 } 567