531 lines
19 KiB
Java
531 lines
19 KiB
Java
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package android.bluetooth.le;
|
|
|
|
import android.annotation.IntDef;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.SystemApi;
|
|
import android.bluetooth.BluetoothAdapter;
|
|
import android.bluetooth.BluetoothDevice;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
|
|
/**
|
|
* The {@link AdvertisingSetParameters} provide a way to adjust advertising preferences for each
|
|
* Bluetooth LE advertising set. Use {@link AdvertisingSetParameters.Builder} to create an instance
|
|
* of this class.
|
|
*/
|
|
public final class AdvertisingSetParameters implements Parcelable {
|
|
|
|
/**
|
|
* Advertise on low frequency, around every 1000ms. This is the default and preferred
|
|
* advertising mode as it consumes the least power.
|
|
*/
|
|
public static final int INTERVAL_HIGH = 1600;
|
|
|
|
/**
|
|
* Advertise on medium frequency, around every 250ms. This is balanced between advertising
|
|
* frequency and power consumption.
|
|
*/
|
|
public static final int INTERVAL_MEDIUM = 400;
|
|
|
|
/**
|
|
* Perform high frequency, low latency advertising, around every 100ms. This has the highest
|
|
* power consumption and should not be used for continuous background advertising.
|
|
*/
|
|
public static final int INTERVAL_LOW = 160;
|
|
|
|
/** Minimum value for advertising interval. */
|
|
public static final int INTERVAL_MIN = 160;
|
|
|
|
/** Maximum value for advertising interval. */
|
|
public static final int INTERVAL_MAX = 16777215;
|
|
|
|
/**
|
|
* Advertise using the lowest transmission (TX) power level. Low transmission power can be used
|
|
* to restrict the visibility range of advertising packets.
|
|
*/
|
|
public static final int TX_POWER_ULTRA_LOW = -21;
|
|
|
|
/** Advertise using low TX power level. */
|
|
public static final int TX_POWER_LOW = -15;
|
|
|
|
/** Advertise using medium TX power level. */
|
|
public static final int TX_POWER_MEDIUM = -7;
|
|
|
|
/**
|
|
* Advertise using high TX power level. This corresponds to largest visibility range of the
|
|
* advertising packet.
|
|
*/
|
|
public static final int TX_POWER_HIGH = 1;
|
|
|
|
/** Minimum value for TX power. */
|
|
public static final int TX_POWER_MIN = -127;
|
|
|
|
/** Maximum value for TX power. */
|
|
public static final int TX_POWER_MAX = 1;
|
|
|
|
/** @hide */
|
|
@IntDef(
|
|
prefix = "ADDRESS_TYPE_",
|
|
value = {
|
|
ADDRESS_TYPE_DEFAULT,
|
|
ADDRESS_TYPE_PUBLIC,
|
|
ADDRESS_TYPE_RANDOM,
|
|
ADDRESS_TYPE_RANDOM_NON_RESOLVABLE,
|
|
})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
public @interface AddressTypeStatus {}
|
|
|
|
/**
|
|
* Advertise own address type that corresponds privacy settings of the device.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi public static final int ADDRESS_TYPE_DEFAULT = -1;
|
|
|
|
/**
|
|
* Advertise own public address type.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi public static final int ADDRESS_TYPE_PUBLIC = 0;
|
|
|
|
/**
|
|
* Generate and adverise own resolvable private address.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi public static final int ADDRESS_TYPE_RANDOM = 1;
|
|
|
|
/**
|
|
* Generate and advertise on non-resolvable private address.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi public static final int ADDRESS_TYPE_RANDOM_NON_RESOLVABLE = 2;
|
|
|
|
private final boolean mIsLegacy;
|
|
private final boolean mIsAnonymous;
|
|
private final boolean mIncludeTxPower;
|
|
private final int mPrimaryPhy;
|
|
private final int mSecondaryPhy;
|
|
private final boolean mConnectable;
|
|
private final boolean mDiscoverable;
|
|
private final boolean mScannable;
|
|
private final int mInterval;
|
|
private final int mTxPowerLevel;
|
|
private final int mOwnAddressType;
|
|
|
|
private AdvertisingSetParameters(
|
|
boolean connectable,
|
|
boolean discoverable,
|
|
boolean scannable,
|
|
boolean isLegacy,
|
|
boolean isAnonymous,
|
|
boolean includeTxPower,
|
|
int primaryPhy,
|
|
int secondaryPhy,
|
|
int interval,
|
|
int txPowerLevel,
|
|
@AddressTypeStatus int ownAddressType) {
|
|
mConnectable = connectable;
|
|
mDiscoverable = discoverable;
|
|
mScannable = scannable;
|
|
mIsLegacy = isLegacy;
|
|
mIsAnonymous = isAnonymous;
|
|
mIncludeTxPower = includeTxPower;
|
|
mPrimaryPhy = primaryPhy;
|
|
mSecondaryPhy = secondaryPhy;
|
|
mInterval = interval;
|
|
mTxPowerLevel = txPowerLevel;
|
|
mOwnAddressType = ownAddressType;
|
|
}
|
|
|
|
private AdvertisingSetParameters(Parcel in) {
|
|
mConnectable = in.readInt() != 0;
|
|
mScannable = in.readInt() != 0;
|
|
mIsLegacy = in.readInt() != 0;
|
|
mIsAnonymous = in.readInt() != 0;
|
|
mIncludeTxPower = in.readInt() != 0;
|
|
mPrimaryPhy = in.readInt();
|
|
mSecondaryPhy = in.readInt();
|
|
mInterval = in.readInt();
|
|
mTxPowerLevel = in.readInt();
|
|
mOwnAddressType = in.readInt();
|
|
mDiscoverable = in.readInt() != 0;
|
|
}
|
|
|
|
/** Returns whether the advertisement will be connectable. */
|
|
public boolean isConnectable() {
|
|
return mConnectable;
|
|
}
|
|
|
|
/** Returns whether the advertisement will be discoverable. */
|
|
public boolean isDiscoverable() {
|
|
return mDiscoverable;
|
|
}
|
|
|
|
/** Returns whether the advertisement will be scannable. */
|
|
public boolean isScannable() {
|
|
return mScannable;
|
|
}
|
|
|
|
/** Returns whether the legacy advertisement will be used. */
|
|
public boolean isLegacy() {
|
|
return mIsLegacy;
|
|
}
|
|
|
|
/** Returns whether the advertisement will be anonymous. */
|
|
public boolean isAnonymous() {
|
|
return mIsAnonymous;
|
|
}
|
|
|
|
/** Returns whether the TX Power will be included. */
|
|
public boolean includeTxPower() {
|
|
return mIncludeTxPower;
|
|
}
|
|
|
|
/** Returns the primary advertising phy. */
|
|
public int getPrimaryPhy() {
|
|
return mPrimaryPhy;
|
|
}
|
|
|
|
/** Returns the secondary advertising phy. */
|
|
public int getSecondaryPhy() {
|
|
return mSecondaryPhy;
|
|
}
|
|
|
|
/** Returns the advertising interval. */
|
|
public int getInterval() {
|
|
return mInterval;
|
|
}
|
|
|
|
/** Returns the TX power level for advertising. */
|
|
public int getTxPowerLevel() {
|
|
return mTxPowerLevel;
|
|
}
|
|
|
|
/**
|
|
* @return the own address type for advertising
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public @AddressTypeStatus int getOwnAddressType() {
|
|
return mOwnAddressType;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "AdvertisingSetParameters [connectable="
|
|
+ mConnectable
|
|
+ ", discoverable="
|
|
+ mDiscoverable
|
|
+ ", isLegacy="
|
|
+ mIsLegacy
|
|
+ ", isAnonymous="
|
|
+ mIsAnonymous
|
|
+ ", includeTxPower="
|
|
+ mIncludeTxPower
|
|
+ ", primaryPhy="
|
|
+ mPrimaryPhy
|
|
+ ", secondaryPhy="
|
|
+ mSecondaryPhy
|
|
+ ", interval="
|
|
+ mInterval
|
|
+ ", txPowerLevel="
|
|
+ mTxPowerLevel
|
|
+ ", ownAddressType="
|
|
+ mOwnAddressType
|
|
+ "]";
|
|
}
|
|
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(Parcel dest, int flags) {
|
|
dest.writeInt(mConnectable ? 1 : 0);
|
|
dest.writeInt(mScannable ? 1 : 0);
|
|
dest.writeInt(mIsLegacy ? 1 : 0);
|
|
dest.writeInt(mIsAnonymous ? 1 : 0);
|
|
dest.writeInt(mIncludeTxPower ? 1 : 0);
|
|
dest.writeInt(mPrimaryPhy);
|
|
dest.writeInt(mSecondaryPhy);
|
|
dest.writeInt(mInterval);
|
|
dest.writeInt(mTxPowerLevel);
|
|
dest.writeInt(mOwnAddressType);
|
|
dest.writeInt(mDiscoverable ? 1 : 0);
|
|
}
|
|
|
|
public static final @android.annotation.NonNull Parcelable.Creator<AdvertisingSetParameters>
|
|
CREATOR =
|
|
new Creator<AdvertisingSetParameters>() {
|
|
@Override
|
|
public AdvertisingSetParameters[] newArray(int size) {
|
|
return new AdvertisingSetParameters[size];
|
|
}
|
|
|
|
@Override
|
|
public AdvertisingSetParameters createFromParcel(Parcel in) {
|
|
return new AdvertisingSetParameters(in);
|
|
}
|
|
};
|
|
|
|
/** Builder class for {@link AdvertisingSetParameters}. */
|
|
public static final class Builder {
|
|
private boolean mConnectable = false;
|
|
private boolean mDiscoverable = true;
|
|
private boolean mScannable = false;
|
|
private boolean mIsLegacy = false;
|
|
private boolean mIsAnonymous = false;
|
|
private boolean mIncludeTxPower = false;
|
|
private int mPrimaryPhy = BluetoothDevice.PHY_LE_1M;
|
|
private int mSecondaryPhy = BluetoothDevice.PHY_LE_1M;
|
|
private int mInterval = INTERVAL_LOW;
|
|
private int mTxPowerLevel = TX_POWER_MEDIUM;
|
|
private int mOwnAddressType = ADDRESS_TYPE_DEFAULT;
|
|
|
|
/**
|
|
* Set whether the advertisement type should be connectable or non-connectable. Legacy
|
|
* advertisements can be both connectable and scannable. Non-legacy advertisements can be
|
|
* only scannable or only connectable.
|
|
*
|
|
* @param connectable Controls whether the advertisement type will be connectable (true) or
|
|
* non-connectable (false).
|
|
*/
|
|
public Builder setConnectable(boolean connectable) {
|
|
mConnectable = connectable;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Set whether the advertisement type should be discoverable or non-discoverable. By
|
|
* default, advertisements will be discoverable. Devices connecting to non-discoverable
|
|
* advertisements cannot initiate bonding.
|
|
*
|
|
* @param discoverable Controls whether the advertisement type will be discoverable ({@code
|
|
* true}) or non-discoverable ({@code false}).
|
|
*/
|
|
public @NonNull Builder setDiscoverable(boolean discoverable) {
|
|
mDiscoverable = discoverable;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Set whether the advertisement type should be scannable. Legacy advertisements can be both
|
|
* connectable and scannable. Non-legacy advertisements can be only scannable or only
|
|
* connectable.
|
|
*
|
|
* @param scannable Controls whether the advertisement type will be scannable (true) or
|
|
* non-scannable (false).
|
|
*/
|
|
public Builder setScannable(boolean scannable) {
|
|
mScannable = scannable;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* When set to true, advertising set will advertise 4.x Spec compliant advertisements.
|
|
*
|
|
* @param isLegacy whether legacy advertising mode should be used.
|
|
*/
|
|
public Builder setLegacyMode(boolean isLegacy) {
|
|
mIsLegacy = isLegacy;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Set whether advertiser address should be omitted from all packets. If this mode is used,
|
|
* periodic advertising can't be enabled for this set.
|
|
*
|
|
* <p>This is used only if legacy mode is not used.
|
|
*
|
|
* @param isAnonymous whether anonymous advertising should be used.
|
|
*/
|
|
public Builder setAnonymous(boolean isAnonymous) {
|
|
mIsAnonymous = isAnonymous;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Set whether TX power should be included in the extended header.
|
|
*
|
|
* <p>This is used only if legacy mode is not used.
|
|
*
|
|
* @param includeTxPower whether TX power should be included in extended header
|
|
*/
|
|
public Builder setIncludeTxPower(boolean includeTxPower) {
|
|
mIncludeTxPower = includeTxPower;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Set the primary physical channel used for this advertising set.
|
|
*
|
|
* <p>This is used only if legacy mode is not used.
|
|
*
|
|
* <p>Use {@link BluetoothAdapter#isLeCodedPhySupported} to determine if LE Coded PHY is
|
|
* supported on this device.
|
|
*
|
|
* @param primaryPhy Primary advertising physical channel, can only be {@link
|
|
* BluetoothDevice#PHY_LE_1M} or {@link BluetoothDevice#PHY_LE_CODED}.
|
|
* @throws IllegalArgumentException If the primaryPhy is invalid.
|
|
*/
|
|
public Builder setPrimaryPhy(int primaryPhy) {
|
|
if (primaryPhy != BluetoothDevice.PHY_LE_1M
|
|
&& primaryPhy != BluetoothDevice.PHY_LE_CODED) {
|
|
throw new IllegalArgumentException("bad primaryPhy " + primaryPhy);
|
|
}
|
|
mPrimaryPhy = primaryPhy;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Set the secondary physical channel used for this advertising set.
|
|
*
|
|
* <p>This is used only if legacy mode is not used.
|
|
*
|
|
* <p>Use {@link BluetoothAdapter#isLeCodedPhySupported} and {@link
|
|
* BluetoothAdapter#isLe2MPhySupported} to determine if LE Coded PHY or 2M PHY is supported
|
|
* on this device.
|
|
*
|
|
* @param secondaryPhy Secondary advertising physical channel, can only be one of {@link
|
|
* BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M} or {@link
|
|
* BluetoothDevice#PHY_LE_CODED}.
|
|
* @throws IllegalArgumentException If the secondaryPhy is invalid.
|
|
*/
|
|
public Builder setSecondaryPhy(int secondaryPhy) {
|
|
if (secondaryPhy != BluetoothDevice.PHY_LE_1M
|
|
&& secondaryPhy != BluetoothDevice.PHY_LE_2M
|
|
&& secondaryPhy != BluetoothDevice.PHY_LE_CODED) {
|
|
throw new IllegalArgumentException("bad secondaryPhy " + secondaryPhy);
|
|
}
|
|
mSecondaryPhy = secondaryPhy;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Set advertising interval.
|
|
*
|
|
* @param interval Bluetooth LE Advertising interval, in 0.625ms unit. Valid range is from
|
|
* 160 (100ms) to 16777215 (10,485.759375 s). Recommended values are: {@link
|
|
* AdvertisingSetParameters#INTERVAL_LOW}, {@link
|
|
* AdvertisingSetParameters#INTERVAL_MEDIUM}, or {@link
|
|
* AdvertisingSetParameters#INTERVAL_HIGH}.
|
|
* @throws IllegalArgumentException If the interval is invalid.
|
|
*/
|
|
public Builder setInterval(int interval) {
|
|
if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) {
|
|
throw new IllegalArgumentException("unknown interval " + interval);
|
|
}
|
|
mInterval = interval;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Set the transmission power level for the advertising.
|
|
*
|
|
* @param txPowerLevel Transmission power of Bluetooth LE Advertising, in dBm. The valid
|
|
* range is [-127, 1] Recommended values are: {@link
|
|
* AdvertisingSetParameters#TX_POWER_ULTRA_LOW}, {@link
|
|
* AdvertisingSetParameters#TX_POWER_LOW}, {@link
|
|
* AdvertisingSetParameters#TX_POWER_MEDIUM}, or {@link
|
|
* AdvertisingSetParameters#TX_POWER_HIGH}.
|
|
* @throws IllegalArgumentException If the {@code txPowerLevel} is invalid.
|
|
*/
|
|
public Builder setTxPowerLevel(int txPowerLevel) {
|
|
if (txPowerLevel < TX_POWER_MIN || txPowerLevel > TX_POWER_MAX) {
|
|
throw new IllegalArgumentException("unknown txPowerLevel " + txPowerLevel);
|
|
}
|
|
mTxPowerLevel = txPowerLevel;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Set own address type for advertising to control public or privacy mode. If used to set
|
|
* address type anything other than {@link AdvertisingSetParameters#ADDRESS_TYPE_DEFAULT},
|
|
* then it will require BLUETOOTH_PRIVILEGED permission and will be checked at the time of
|
|
* starting advertising.
|
|
*
|
|
* @throws IllegalArgumentException If the {@code ownAddressType} is invalid
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public @NonNull Builder setOwnAddressType(@AddressTypeStatus int ownAddressType) {
|
|
if (ownAddressType < AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT
|
|
|| ownAddressType
|
|
> AdvertisingSetParameters.ADDRESS_TYPE_RANDOM_NON_RESOLVABLE) {
|
|
throw new IllegalArgumentException("unknown address type " + ownAddressType);
|
|
}
|
|
mOwnAddressType = ownAddressType;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Build the {@link AdvertisingSetParameters} object.
|
|
*
|
|
* @throws IllegalStateException if invalid combination of parameters is used.
|
|
*/
|
|
public AdvertisingSetParameters build() {
|
|
if (mIsLegacy) {
|
|
if (mIsAnonymous) {
|
|
throw new IllegalArgumentException("Legacy advertising can't be anonymous");
|
|
}
|
|
|
|
if (mConnectable && !mScannable) {
|
|
throw new IllegalStateException(
|
|
"Legacy advertisement can't be connectable and non-scannable");
|
|
}
|
|
|
|
if (mIncludeTxPower) {
|
|
throw new IllegalStateException(
|
|
"Legacy advertising can't include TX power level in header");
|
|
}
|
|
} else {
|
|
if (mConnectable && mScannable) {
|
|
throw new IllegalStateException(
|
|
"Advertising can't be both connectable and scannable");
|
|
}
|
|
|
|
if (mIsAnonymous && mConnectable) {
|
|
throw new IllegalStateException(
|
|
"Advertising can't be both connectable and anonymous");
|
|
}
|
|
}
|
|
|
|
return new AdvertisingSetParameters(
|
|
mConnectable,
|
|
mDiscoverable,
|
|
mScannable,
|
|
mIsLegacy,
|
|
mIsAnonymous,
|
|
mIncludeTxPower,
|
|
mPrimaryPhy,
|
|
mSecondaryPhy,
|
|
mInterval,
|
|
mTxPowerLevel,
|
|
mOwnAddressType);
|
|
}
|
|
}
|
|
}
|