Bluetooth LE Advertising minor improvements
This patch adds some additional error checking for the advertising set
parameters, and some more comments.
Test: manual
Bug: 30622771
Change-Id: I87bd44f4179ef63694ad3ed656dc2acc52e40f1e
(cherry picked from commit f4ed33f5fa6ffa3bda6faff773a3fb90b16a760c)
diff --git a/core/java/android/bluetooth/le/AdvertisingSet.java b/core/java/android/bluetooth/le/AdvertisingSet.java
index 7355b0d..d6991bf 100644
--- a/core/java/android/bluetooth/le/AdvertisingSet.java
+++ b/core/java/android/bluetooth/le/AdvertisingSet.java
@@ -16,6 +16,7 @@
package android.bluetooth.le;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothManager;
import android.bluetooth.le.IAdvertisingSetCallback;
@@ -57,11 +58,12 @@
/**
* Enables Advertising. This method returns immediately, the operation status is
- * delivered
- * through {@code callback.onAdvertisingEnabled()}.
+ * delivered through {@code callback.onAdvertisingEnabled()}.
* <p>
* Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
+ * @param enable whether the advertising should be enabled (true), or disabled (false)
+ * @param timeoutMillis duration for which that advertising set is enabled.
*/
public void enableAdvertising(boolean enable, int timeout) {
try {
@@ -77,10 +79,16 @@
* delivered through {@code callback.onAdvertisingDataSet()}.
* <p>
* Advertising data must be empty if non-legacy scannable advertising is used.
+ *
+ * @param advertiseData Advertisement data to be broadcasted. Size must not exceed
+ * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the
+ * advertisement is connectable, three bytes will be added for flags. If the
+ * update takes place when the advertising set is enabled, the data can be
+ * maximum 251 bytes long.
*/
- public void setAdvertisingData(AdvertiseData data) {
+ public void setAdvertisingData(AdvertiseData advertiseData) {
try {
- gatt.setAdvertisingData(this.advertiserId, data);
+ gatt.setAdvertisingData(this.advertiserId, advertiseData);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -90,10 +98,15 @@
* Set/update scan response data. Make sure that data doesn't exceed the size limit for
* specified AdvertisingSetParameters. This method returns immediately, the operation status
* is delivered through {@code callback.onScanResponseDataSet()}.
+ *
+ * @param scanResponse Scan response associated with the advertisement data. Size must not
+ * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the
+ * update takes place when the advertising set is enabled, the data can be
+ * maximum 251 bytes long.
*/
- public void setScanResponseData(AdvertiseData data) {
+ public void setScanResponseData(AdvertiseData scanResponse) {
try {
- gatt.setScanResponseData(this.advertiserId, data);
+ gatt.setScanResponseData(this.advertiserId, scanResponse);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -103,6 +116,8 @@
* Update advertising parameters associated with this AdvertisingSet. Must be called when
* advertising is not active. This method returns immediately, the operation status is delivered
* through {@code callback.onAdvertisingParametersUpdated}.
+ *
+ * @param parameters advertising set parameters.
*/
public void setAdvertisingParameters(AdvertisingSetParameters parameters) {
try {
@@ -130,10 +145,15 @@
* or after advertising was started with periodic advertising data set. This method returns
* immediately, the operation status is delivered through
* {@code callback.onPeriodicAdvertisingDataSet()}.
+ *
+ * @param periodicData Periodic advertising data. Size must not exceed
+ * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the
+ * update takes place when the periodic advertising is enabled for this set,
+ * the data can be maximum 251 bytes long.
*/
- public void setPeriodicAdvertisingData(AdvertiseData data) {
+ public void setPeriodicAdvertisingData(AdvertiseData periodicData) {
try {
- gatt.setPeriodicAdvertisingData(this.advertiserId, data);
+ gatt.setPeriodicAdvertisingData(this.advertiserId, periodicData);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -142,6 +162,8 @@
/**
* Used to enable/disable periodic advertising. This method returns immediately, the operation
* status is delivered through {@code callback.onPeriodicAdvertisingEnable()}.
+ *
+ * @param enable whether the periodic advertising should be enabled (true), or disabled (false).
*/
public void setPeriodicAdvertisingEnable(boolean enable) {
try {
diff --git a/core/java/android/bluetooth/le/AdvertisingSetParameters.java b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
index d36c0d6..f5c1f08 100644
--- a/core/java/android/bluetooth/le/AdvertisingSetParameters.java
+++ b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
@@ -16,6 +16,7 @@
package android.bluetooth.le;
+import android.bluetooth.BluetoothAdapter;
import android.os.Parcel;
import android.os.Parcelable;
@@ -305,7 +306,7 @@
* This is used only if legacy mode is not used.
*
* @param includeTxPower whether TX power should be included in extended
- * header
+ * header
*/
public Builder setIncludeTxPower(boolean includeTxPower) {
this.includeTxPower = includeTxPower;
@@ -317,6 +318,8 @@
*
* This is used only if legacy mode is not used.
*
+ * 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 AdvertisingSetParameters#PHY_LE_1M} or
* {@link AdvertisingSetParameters#PHY_LE_CODED}.
@@ -335,6 +338,10 @@
*
* This is used only if legacy mode is not used.
*
+ * 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 AdvertisingSetParameters#PHY_LE_1M},
* {@link AdvertisingSetParameters#PHY_LE_2M} or
@@ -393,6 +400,32 @@
* Build the {@link AdvertisingSetParameters} object.
*/
public AdvertisingSetParameters build() {
+ if (isLegacy) {
+ if (isAnonymous) {
+ throw new IllegalArgumentException("Legacy advertising can't be anonymous");
+ }
+
+ if (connectable == true && scannable == false) {
+ throw new IllegalArgumentException(
+ "Legacy advertisement can't be connectable and non-scannable");
+ }
+
+ if (includeTxPower) {
+ throw new IllegalArgumentException(
+ "Legacy advertising can't include TX power level in header");
+ }
+ } else {
+ if (connectable && scannable) {
+ throw new IllegalArgumentException(
+ "Advertising can't be both connectable and scannable");
+ }
+
+ if (isAnonymous && connectable) {
+ throw new IllegalArgumentException(
+ "Advertising can't be both connectable and anonymous");
+ }
+ }
+
return new AdvertisingSetParameters(connectable, scannable, isLegacy, isAnonymous,
includeTxPower, primaryPhy,
secondaryPhy, interval, txPowerLevel);
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 07d9b6d..2ccf08e 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -50,7 +50,8 @@
private static final String TAG = "BluetoothLeAdvertiser";
- private static final int MAX_ADVERTISING_DATA_BYTES = 31;
+ private static final int MAX_ADVERTISING_DATA_BYTES = 1650;
+ private static final int MAX_LEGACY_ADVERTISING_DATA_BYTES = 31;
// Each fields need one byte for field length and another byte for field type.
private static final int OVERHEAD_BYTES_PER_FIELD = 2;
// Flags field will be set by system.
@@ -116,8 +117,8 @@
throw new IllegalArgumentException("callback cannot be null");
}
boolean isConnectable = settings.isConnectable();
- if (totalBytes(advertiseData, isConnectable) > MAX_ADVERTISING_DATA_BYTES ||
- totalBytes(scanResponse, false) > MAX_ADVERTISING_DATA_BYTES) {
+ if (totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES ||
+ totalBytes(scanResponse, false) > MAX_LEGACY_ADVERTISING_DATA_BYTES) {
postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE);
return;
}
@@ -205,16 +206,26 @@
}
/**
- * Creates a new advertising set. If operation succeed, device will start advertising. This
- * method returns immediately, the operation status is delivered through
- * {@code callback.onAdvertisingSetStarted()}.
- * <p>
- * @param parameters advertising set parameters.
- * @param advertiseData Advertisement data to be broadcasted.
- * @param scanResponse Scan response associated with the advertisement data.
- * @param periodicData Periodic advertising data.
- * @param callback Callback for advertising set.
- */
+ * Creates a new advertising set. If operation succeed, device will start advertising. This
+ * method returns immediately, the operation status is delivered through
+ * {@code callback.onAdvertisingSetStarted()}.
+ * <p>
+ * @param parameters advertising set parameters.
+ * @param advertiseData Advertisement data to be broadcasted. Size must not exceed
+ * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the
+ * advertisement is connectable, three bytes will be added for flags.
+ * @param scanResponse Scan response associated with the advertisement data. Size must not exceed
+ * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
+ * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will
+ * not be started.
+ * @param periodicData Periodic advertising data. Size must not exceed
+ * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
+ * @param callback Callback for advertising set.
+ * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable
+ * size, or unsupported advertising PHY is selected, or when attempt to use
+ * Periodic Advertising feature is made when it's not supported by the
+ * controller.
+ */
public void startAdvertisingSet(AdvertisingSetParameters parameters,
AdvertiseData advertiseData, AdvertiseData scanResponse,
PeriodicAdvertisingParameters periodicParameters,
@@ -224,17 +235,27 @@
}
/**
- * Creates a new advertising set. If operation succeed, device will start advertising. This
- * method returns immediately, the operation status is delivered through
- * {@code callback.onAdvertisingSetStarted()}.
- * <p>
- * @param parameters advertising set parameters.
- * @param advertiseData Advertisement data to be broadcasted.
- * @param scanResponse Scan response associated with the advertisement data.
- * @param periodicData Periodic advertising data.
- * @param callback Callback for advertising set.
- * @param handler thread upon which the callbacks will be invoked.
- */
+ * Creates a new advertising set. If operation succeed, device will start advertising. This
+ * method returns immediately, the operation status is delivered through
+ * {@code callback.onAdvertisingSetStarted()}.
+ * <p>
+ * @param parameters advertising set parameters.
+ * @param advertiseData Advertisement data to be broadcasted. Size must not exceed
+ * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the
+ * advertisement is connectable, three bytes will be added for flags.
+ * @param scanResponse Scan response associated with the advertisement data. Size must not exceed
+ * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
+ * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will
+ * not be started.
+ * @param periodicData Periodic advertising data. Size must not exceed
+ * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
+ * @param callback Callback for advertising set.
+ * @param handler thread upon which the callbacks will be invoked.
+ * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable
+ * size, or unsupported advertising PHY is selected, or when attempt to use
+ * Periodic Advertising feature is made when it's not supported by the
+ * controller.
+ */
public void startAdvertisingSet(AdvertisingSetParameters parameters,
AdvertiseData advertiseData, AdvertiseData scanResponse,
PeriodicAdvertisingParameters periodicParameters,
@@ -245,17 +266,27 @@
}
/**
- * Creates a new advertising set. If operation succeed, device will start advertising. This
- * method returns immediately, the operation status is delivered through
- * {@code callback.onAdvertisingSetStarted()}.
- * <p>
- * @param parameters advertising set parameters.
- * @param advertiseData Advertisement data to be broadcasted.
- * @param scanResponse Scan response associated with the advertisement data.
- * @param periodicData Periodic advertising data.
- * @param timeoutMillis Advertising time limit. May not exceed 180000
- * @param callback Callback for advertising set.
- */
+ * Creates a new advertising set. If operation succeed, device will start advertising. This
+ * method returns immediately, the operation status is delivered through
+ * {@code callback.onAdvertisingSetStarted()}.
+ * <p>
+ * @param parameters advertising set parameters.
+ * @param advertiseData Advertisement data to be broadcasted. Size must not exceed
+ * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the
+ * advertisement is connectable, three bytes will be added for flags.
+ * @param scanResponse Scan response associated with the advertisement data. Size must not exceed
+ * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
+ * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will
+ * not be started.
+ * @param periodicData Periodic advertising data. Size must not exceed
+ * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
+ * @param timeoutMillis Advertising time limit. May not exceed 180000
+ * @param callback Callback for advertising set.
+ * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable
+ * size, or unsupported advertising PHY is selected, or when attempt to use
+ * Periodic Advertising feature is made when it's not supported by the
+ * controller.
+ */
public void startAdvertisingSet(AdvertisingSetParameters parameters,
AdvertiseData advertiseData, AdvertiseData scanResponse,
PeriodicAdvertisingParameters periodicParameters,
@@ -266,29 +297,81 @@
}
/**
- * Creates a new advertising set. If operation succeed, device will start advertising. This
- * method returns immediately, the operation status is delivered through
- * {@code callback.onAdvertisingSetStarted()}.
- * <p>
- * @param parameters advertising set parameters.
- * @param advertiseData Advertisement data to be broadcasted.
- * @param scanResponse Scan response associated with the advertisement data.
- * @param periodicData Periodic advertising data.
- * @param timeoutMillis Advertising time limit. May not exceed 180000
- * @param callback Callback for advertising set.
- * @param handler thread upon which the callbacks will be invoked.
- */
+ * Creates a new advertising set. If operation succeed, device will start advertising. This
+ * method returns immediately, the operation status is delivered through
+ * {@code callback.onAdvertisingSetStarted()}.
+ * <p>
+ * @param parameters advertising set parameters.
+ * @param advertiseData Advertisement data to be broadcasted. Size must not exceed
+ * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the
+ * advertisement is connectable, three bytes will be added for flags.
+ * @param scanResponse Scan response associated with the advertisement data. Size must not exceed
+ * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}
+ * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will
+ * not be started.
+ * @param periodicData Periodic advertising data. Size must not exceed
+ * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}
+ * @param timeoutMillis Advertising time limit. May not exceed 180000
+ * @param callback Callback for advertising set.
+ * @param handler thread upon which the callbacks will be invoked.
+ * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable
+ * size, or unsupported advertising PHY is selected, or when attempt to use
+ * Periodic Advertising feature is made when it's not supported by the
+ * controller.
+ */
public void startAdvertisingSet(AdvertisingSetParameters parameters,
AdvertiseData advertiseData, AdvertiseData scanResponse,
PeriodicAdvertisingParameters periodicParameters,
AdvertiseData periodicData, int timeoutMillis,
AdvertisingSetCallback callback, Handler handler) {
BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
-
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null");
}
+ boolean isConnectable = parameters.isConnectable();
+ if (parameters.isLegacy()) {
+ if (totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES) {
+ throw new IllegalArgumentException("Legacy advertising data too big");
+ }
+
+ if (totalBytes(scanResponse, false) > MAX_LEGACY_ADVERTISING_DATA_BYTES) {
+ throw new IllegalArgumentException("Legacy scan response data too big");
+ }
+ } else {
+ boolean supportCodedPhy = mBluetoothAdapter.isLeCodedPhySupported();
+ boolean support2MPhy = mBluetoothAdapter.isLe2MPhySupported();
+ int pphy = parameters.getPrimaryPhy();
+ int sphy = parameters.getSecondaryPhy();
+ if (pphy == AdvertisingSetParameters.PHY_LE_CODED && !supportCodedPhy) {
+ throw new IllegalArgumentException("Unsupported primary PHY selected");
+ }
+
+ if ((sphy == AdvertisingSetParameters.PHY_LE_CODED && !supportCodedPhy)
+ || (sphy == AdvertisingSetParameters.PHY_LE_2M && !support2MPhy)) {
+ throw new IllegalArgumentException("Unsupported secondary PHY selected");
+ }
+
+ int maxData = mBluetoothAdapter.getLeMaximumAdvertisingDataLength();
+ if (totalBytes(advertiseData, isConnectable) > maxData) {
+ throw new IllegalArgumentException("Advertising data too big");
+ }
+
+ if (totalBytes(scanResponse, false) > maxData) {
+ throw new IllegalArgumentException("Scan response data too big");
+ }
+
+ if (totalBytes(periodicData, false) > maxData) {
+ throw new IllegalArgumentException("Periodic advertising data too big");
+ }
+
+ boolean supportPeriodic = mBluetoothAdapter.isLePeriodicAdvertisingSupported();
+ if (periodicParameters != null && periodicParameters.getEnable() && !supportPeriodic) {
+ throw new IllegalArgumentException(
+ "Controller does not support LE Periodic Advertising");
+ }
+ }
+
IBluetoothGatt gatt;
try {
gatt = mBluetoothManager.getBluetoothGatt();