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 17package android.bluetooth.le; 18 19import android.bluetooth.BluetoothAdapter; 20import android.bluetooth.BluetoothDevice; 21import android.os.Parcel; 22import android.os.Parcelable; 23 24/** 25 * The {@link AdvertisingSetParameters} provide a way to adjust advertising 26 * preferences for each 27 * Bluetooth LE advertising set. Use {@link AdvertisingSetParameters.Builder} to 28 * create an 29 * instance of this class. 30 */ 31public final class AdvertisingSetParameters implements Parcelable { 32 33 /** 34 * Advertise on low frequency, around every 1000ms. This is the default and 35 * preferred advertising mode as it consumes the least power. 36 */ 37 public static final int INTERVAL_HIGH = 1600; 38 39 /** 40 * Advertise on medium frequency, around every 250ms. This is balanced 41 * between advertising frequency and power consumption. 42 */ 43 public static final int INTERVAL_MEDIUM = 400; 44 45 /** 46 * Perform high frequency, low latency advertising, around every 100ms. This 47 * has the highest power consumption and should not be used for continuous 48 * background advertising. 49 */ 50 public static final int INTERVAL_LOW = 160; 51 52 /** 53 * Minimum value for advertising interval. 54 */ 55 public static final int INTERVAL_MIN = 160; 56 57 /** 58 * Maximum value for advertising interval. 59 */ 60 public static final int INTERVAL_MAX = 16777215; 61 62 /** 63 * Advertise using the lowest transmission (TX) power level. Low transmission 64 * power can be used to restrict the visibility range of advertising packets. 65 */ 66 public static final int TX_POWER_ULTRA_LOW = -21; 67 68 /** 69 * Advertise using low TX power level. 70 */ 71 public static final int TX_POWER_LOW = -15; 72 73 /** 74 * Advertise using medium TX power level. 75 */ 76 public static final int TX_POWER_MEDIUM = -7; 77 78 /** 79 * Advertise using high TX power level. This corresponds to largest visibility 80 * range of the advertising packet. 81 */ 82 public static final int TX_POWER_HIGH = 1; 83 84 /** 85 * Minimum value for TX power. 86 */ 87 public static final int TX_POWER_MIN = -127; 88 89 /** 90 * Maximum value for TX power. 91 */ 92 public static final int TX_POWER_MAX = 1; 93 94 /** 95 * The maximum limited advertisement duration as specified by the Bluetooth 96 * SIG 97 */ 98 private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000; 99 100 private final boolean isLegacy; 101 private final boolean isAnonymous; 102 private final boolean includeTxPower; 103 private final int primaryPhy; 104 private final int secondaryPhy; 105 private final boolean connectable; 106 private final boolean scannable; 107 private final int interval; 108 private final int txPowerLevel; 109 110 private AdvertisingSetParameters(boolean connectable, boolean scannable, boolean isLegacy, 111 boolean isAnonymous, boolean includeTxPower, 112 int primaryPhy, int secondaryPhy, 113 int interval, int txPowerLevel) { 114 this.connectable = connectable; 115 this.scannable = scannable; 116 this.isLegacy = isLegacy; 117 this.isAnonymous = isAnonymous; 118 this.includeTxPower = includeTxPower; 119 this.primaryPhy = primaryPhy; 120 this.secondaryPhy = secondaryPhy; 121 this.interval = interval; 122 this.txPowerLevel = txPowerLevel; 123 } 124 125 private AdvertisingSetParameters(Parcel in) { 126 connectable = in.readInt() != 0 ? true : false; 127 scannable = in.readInt() != 0 ? true : false; 128 isLegacy = in.readInt() != 0 ? true : false; 129 isAnonymous = in.readInt() != 0 ? true : false; 130 includeTxPower = in.readInt() != 0 ? true : false; 131 primaryPhy = in.readInt(); 132 secondaryPhy = in.readInt(); 133 interval = in.readInt(); 134 txPowerLevel = in.readInt(); 135 } 136 137 /** 138 * Returns whether the advertisement will be connectable. 139 */ 140 public boolean isConnectable() { return connectable; } 141 142 /** 143 * Returns whether the advertisement will be scannable. 144 */ 145 public boolean isScannable() { return scannable; } 146 147 /** 148 * Returns whether the legacy advertisement will be used. 149 */ 150 public boolean isLegacy() { return isLegacy; } 151 152 /** 153 * Returns whether the advertisement will be anonymous. 154 */ 155 public boolean isAnonymous() { return isAnonymous; } 156 157 /** 158 * Returns whether the TX Power will be included. 159 */ 160 public boolean includeTxPower() { return includeTxPower; } 161 162 /** 163 * Returns the primary advertising phy. 164 */ 165 public int getPrimaryPhy() { return primaryPhy; } 166 167 /** 168 * Returns the secondary advertising phy. 169 */ 170 public int getSecondaryPhy() { return secondaryPhy; } 171 172 /** 173 * Returns the advertising interval. 174 */ 175 public int getInterval() { return interval; } 176 177 /** 178 * Returns the TX power level for advertising. 179 */ 180 public int getTxPowerLevel() { return txPowerLevel; } 181 182 @Override 183 public String toString() { 184 return "AdvertisingSetParameters [connectable=" + connectable 185 + ", isLegacy=" + isLegacy 186 + ", isAnonymous=" + isAnonymous 187 + ", includeTxPower=" + includeTxPower 188 + ", primaryPhy=" + primaryPhy 189 + ", secondaryPhy=" + secondaryPhy 190 + ", interval=" + interval 191 + ", txPowerLevel=" + txPowerLevel + "]"; 192 } 193 194 @Override 195 public int describeContents() { 196 return 0; 197 } 198 199 @Override 200 public void writeToParcel(Parcel dest, int flags) { 201 dest.writeInt(connectable ? 1 : 0); 202 dest.writeInt(scannable ? 1 : 0); 203 dest.writeInt(isLegacy ? 1 : 0); 204 dest.writeInt(isAnonymous ? 1 : 0); 205 dest.writeInt(includeTxPower ? 1 : 0); 206 dest.writeInt(primaryPhy); 207 dest.writeInt(secondaryPhy); 208 dest.writeInt(interval); 209 dest.writeInt(txPowerLevel); 210 } 211 212 public static final Parcelable.Creator<AdvertisingSetParameters> CREATOR = 213 new Creator<AdvertisingSetParameters>() { 214 @Override 215 public AdvertisingSetParameters[] newArray(int size) { 216 return new AdvertisingSetParameters[size]; 217 } 218 219 @Override 220 public AdvertisingSetParameters createFromParcel(Parcel in) { 221 return new AdvertisingSetParameters(in); 222 } 223 }; 224 225 /** 226 * Builder class for {@link AdvertisingSetParameters}. 227 */ 228 public static final class Builder { 229 230 private boolean connectable = false; 231 private boolean scannable = false; 232 private boolean isLegacy = false; 233 private boolean isAnonymous = false; 234 private boolean includeTxPower = false; 235 private int primaryPhy = BluetoothDevice.PHY_LE_1M; 236 private int secondaryPhy = BluetoothDevice.PHY_LE_1M; 237 private int interval = INTERVAL_LOW; 238 private int txPowerLevel = TX_POWER_MEDIUM; 239 240 /** 241 * Set whether the advertisement type should be connectable or 242 * non-connectable. 243 * Legacy advertisements can be both connectable and scannable. Non-legacy 244 * advertisements can be only scannable or only connectable. 245 * @param connectable Controls whether the advertisement type will be 246 * connectable (true) or non-connectable (false). 247 */ 248 public Builder setConnectable(boolean connectable) { 249 this.connectable = connectable; 250 return this; 251 } 252 253 /** 254 * Set whether the advertisement type should be scannable. 255 * Legacy advertisements can be both connectable and scannable. Non-legacy 256 * advertisements can be only scannable or only connectable. 257 * @param scannable Controls whether the advertisement type will be 258 * scannable (true) or non-scannable (false). 259 */ 260 public Builder setScannable(boolean scannable) { 261 this.scannable = scannable; 262 return this; 263 } 264 265 /** 266 * When set to true, advertising set will advertise 4.x Spec compliant 267 * advertisements. 268 * 269 * @param isLegacy whether legacy advertising mode should be used. 270 */ 271 public Builder setLegacyMode(boolean isLegacy) { 272 this.isLegacy = isLegacy; 273 return this; 274 } 275 276 /** 277 * Set whether advertiser address should be ommited from all packets. If this 278 * mode is used, periodic advertising can't be enabled for this set. 279 * 280 * This is used only if legacy mode is not used. 281 * 282 * @param isAnonymous whether anonymous advertising should be used. 283 */ 284 public Builder setAnonymous(boolean isAnonymous) { 285 this.isAnonymous = isAnonymous; 286 return this; 287 } 288 289 /** 290 * Set whether TX power should be included in the extended header. 291 * 292 * This is used only if legacy mode is not used. 293 * 294 * @param includeTxPower whether TX power should be included in extended 295 * header 296 */ 297 public Builder setIncludeTxPower(boolean includeTxPower) { 298 this.includeTxPower = includeTxPower; 299 return this; 300 } 301 302 /** 303 * Set the primary physical channel used for this advertising set. 304 * 305 * This is used only if legacy mode is not used. 306 * 307 * Use {@link BluetoothAdapter#isLeCodedPhySupported} to determine if LE Coded PHY is 308 * supported on this device. 309 * @param primaryPhy Primary advertising physical channel, can only be 310 * {@link BluetoothDevice#PHY_LE_1M} or 311 * {@link BluetoothDevice#PHY_LE_CODED}. 312 * @throws IllegalArgumentException If the primaryPhy is invalid. 313 */ 314 public Builder setPrimaryPhy(int primaryPhy) { 315 if (primaryPhy != BluetoothDevice.PHY_LE_1M && 316 primaryPhy != BluetoothDevice.PHY_LE_CODED) { 317 throw new IllegalArgumentException("bad primaryPhy " + primaryPhy); 318 } 319 this.primaryPhy = primaryPhy; 320 return this; 321 } 322 323 /** 324 * Set the secondary physical channel used for this advertising set. 325 * 326 * This is used only if legacy mode is not used. 327 * 328 * Use {@link BluetoothAdapter#isLeCodedPhySupported} and 329 * {@link BluetoothAdapter#isLe2MPhySupported} to determine if LE Coded PHY or 2M PHY is 330 * supported on this device. 331 * 332 * @param secondaryPhy Secondary advertising physical channel, can only be 333 * one of {@link BluetoothDevice#PHY_LE_1M}, 334 * {@link BluetoothDevice#PHY_LE_2M} or 335 * {@link BluetoothDevice#PHY_LE_CODED}. 336 * @throws IllegalArgumentException If the secondaryPhy is invalid. 337 */ 338 public Builder setSecondaryPhy(int secondaryPhy) { 339 if (secondaryPhy != BluetoothDevice.PHY_LE_1M && 340 secondaryPhy != BluetoothDevice.PHY_LE_2M && 341 secondaryPhy != BluetoothDevice.PHY_LE_CODED) { 342 throw new IllegalArgumentException("bad secondaryPhy " + secondaryPhy); 343 } 344 this.secondaryPhy = secondaryPhy; 345 return this; 346 } 347 348 /** 349 * Set advertising interval. 350 * 351 * @param interval Bluetooth LE Advertising interval, in 0.625ms unit. Valid 352 * range is from 160 (100ms) to 16777215 (10,485.759375 s). 353 * Recommended values are: 354 * {@link AdvertisingSetParameters#INTERVAL_LOW}, 355 * {@link AdvertisingSetParameters#INTERVAL_MEDIUM}, or 356 * {@link AdvertisingSetParameters#INTERVAL_HIGH}. 357 * @throws IllegalArgumentException If the interval is invalid. 358 */ 359 public Builder setInterval(int interval) { 360 if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { 361 throw new IllegalArgumentException("unknown interval " + interval); 362 } 363 this.interval = interval; 364 return this; 365 } 366 367 /** 368 * Set the transmission power level for the advertising. 369 * @param txPowerLevel Transmission power of Bluetooth LE Advertising, in 370 * dBm. The valid range is [-127, 1] Recommended values are: 371 * {@link AdvertisingSetParameters#TX_POWER_ULTRA_LOW}, 372 * {@link AdvertisingSetParameters#TX_POWER_LOW}, 373 * {@link AdvertisingSetParameters#TX_POWER_MEDIUM}, or 374 * {@link AdvertisingSetParameters#TX_POWER_HIGH}. 375 * 376 * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid. 377 */ 378 public Builder setTxPowerLevel(int txPowerLevel) { 379 if (txPowerLevel < TX_POWER_MIN || txPowerLevel > TX_POWER_MAX) { 380 throw new IllegalArgumentException("unknown txPowerLevel " + 381 txPowerLevel); 382 } 383 this.txPowerLevel = txPowerLevel; 384 return this; 385 } 386 387 /** 388 * Build the {@link AdvertisingSetParameters} object. 389 * @throws IllegalStateException if invalid combination of parameters is used. 390 */ 391 public AdvertisingSetParameters build() { 392 if (isLegacy) { 393 if (isAnonymous) { 394 throw new IllegalArgumentException("Legacy advertising can't be anonymous"); 395 } 396 397 if (connectable == true && scannable == false) { 398 throw new IllegalStateException( 399 "Legacy advertisement can't be connectable and non-scannable"); 400 } 401 402 if (includeTxPower) { 403 throw new IllegalStateException( 404 "Legacy advertising can't include TX power level in header"); 405 } 406 } else { 407 if (connectable && scannable) { 408 throw new IllegalStateException( 409 "Advertising can't be both connectable and scannable"); 410 } 411 412 if (isAnonymous && connectable) { 413 throw new IllegalStateException( 414 "Advertising can't be both connectable and anonymous"); 415 } 416 } 417 418 return new AdvertisingSetParameters(connectable, scannable, isLegacy, isAnonymous, 419 includeTxPower, primaryPhy, 420 secondaryPhy, interval, txPowerLevel); 421 } 422 } 423}