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