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