OSDN Git Service

Bluetooth LE Advertising minor improvements
authorJakub Pawlowski <jpawlowski@google.com>
Thu, 30 Mar 2017 18:19:24 +0000 (11:19 -0700)
committerJakub Pawlowski <jpawlowski@google.com>
Sat, 1 Apr 2017 04:33:24 +0000 (04:33 +0000)
This patch adds some additional error checking for the advertising set
parameters, and some more comments.

Test: manual
Bug: 30622771
Change-Id: I87bd44f4179ef63694ad3ed656dc2acc52e40f1e

core/java/android/bluetooth/le/AdvertisingSet.java
core/java/android/bluetooth/le/AdvertisingSetParameters.java
core/java/android/bluetooth/le/BluetoothLeAdvertiser.java

index 7355b0d..d6991bf 100644 (file)
@@ -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 @@ public final class AdvertisingSet {
 
     /**
      * 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 @@ public final class AdvertisingSet {
      * 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 @@ public final class AdvertisingSet {
      * 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 @@ public final class AdvertisingSet {
      * 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 @@ public final class AdvertisingSet {
      * 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 @@ public final class AdvertisingSet {
     /**
      * 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 {
index 3e13ad3..7e37157 100644 (file)
@@ -16,6 +16,7 @@
 
 package android.bluetooth.le;
 
+import android.bluetooth.BluetoothAdapter;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -305,7 +306,7 @@ public final class AdvertisingSetParameters implements Parcelable {
          * 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 @@ public final class AdvertisingSetParameters implements Parcelable {
          *
          * 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 @@ public final class AdvertisingSetParameters implements Parcelable {
          *
          * 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 @@ public final class AdvertisingSetParameters implements Parcelable {
          * 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);
index 07d9b6d..2ccf08e 100644 (file)
@@ -50,7 +50,8 @@ public final class BluetoothLeAdvertiser {
 
     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 @@ public final class BluetoothLeAdvertiser {
                 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 @@ public final class BluetoothLeAdvertiser {
     }
 
     /**
-    * 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 @@ public final class BluetoothLeAdvertiser {
     }
 
     /**
-    * 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 @@ public final class BluetoothLeAdvertiser {
     }
 
     /**
-    * 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 @@ public final class BluetoothLeAdvertiser {
     }
 
     /**
-    * 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();