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