diff options
| author | 2021-11-19 09:10:42 +0000 | |
|---|---|---|
| committer | 2021-11-19 09:10:42 +0000 | |
| commit | 4e5703d57e4b9da82d6bf1fa11a45d4fe36a2275 (patch) | |
| tree | a4893238f654cb65f62a560193df195d2b7e529d | |
| parent | 467fc191982cb92abb642662655985c5824c1fc9 (diff) | |
| parent | 4e43b68f20c1ac01951eeba971b9c5e491d82dc5 (diff) | |
Merge "Bluetooth: make it possible to advertise Transport Discovery Data" am: e7adf14ea3 am: 4e43b68f20
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1454977
Change-Id: I6865279b3684152e762ad6d86dcdea41c8c2e864
| -rw-r--r-- | core/api/current.txt | 27 | ||||
| -rw-r--r-- | core/java/android/bluetooth/le/AdvertiseData.java | 58 | ||||
| -rw-r--r-- | core/java/android/bluetooth/le/BluetoothLeAdvertiser.java | 3 | ||||
| -rw-r--r-- | core/java/android/bluetooth/le/TransportBlock.java | 155 | ||||
| -rw-r--r-- | core/java/android/bluetooth/le/TransportDiscoveryData.java | 168 | 
5 files changed, 406 insertions, 5 deletions
| diff --git a/core/api/current.txt b/core/api/current.txt index 087b54ee86f2..b519cbeffe24 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -9546,6 +9546,7 @@ package android.bluetooth.le {      method public java.util.Map<android.os.ParcelUuid,byte[]> getServiceData();      method @NonNull public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids();      method public java.util.List<android.os.ParcelUuid> getServiceUuids(); +    method @NonNull public java.util.List<android.bluetooth.le.TransportDiscoveryData> getTransportDiscoveryData();      method public void writeToParcel(android.os.Parcel, int);      field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertiseData> CREATOR;    } @@ -9556,6 +9557,7 @@ package android.bluetooth.le {      method public android.bluetooth.le.AdvertiseData.Builder addServiceData(android.os.ParcelUuid, byte[]);      method @NonNull public android.bluetooth.le.AdvertiseData.Builder addServiceSolicitationUuid(@NonNull android.os.ParcelUuid);      method public android.bluetooth.le.AdvertiseData.Builder addServiceUuid(android.os.ParcelUuid); +    method @NonNull public android.bluetooth.le.AdvertiseData.Builder addTransportDiscoveryData(@NonNull android.bluetooth.le.TransportDiscoveryData);      method public android.bluetooth.le.AdvertiseData build();      method public android.bluetooth.le.AdvertiseData.Builder setIncludeDeviceName(boolean);      method public android.bluetooth.le.AdvertiseData.Builder setIncludeTxPowerLevel(boolean); @@ -9815,6 +9817,31 @@ package android.bluetooth.le {      method public android.bluetooth.le.ScanSettings.Builder setScanMode(int);    } +  public final class TransportBlock implements android.os.Parcelable { +    ctor public TransportBlock(int, int, int, @Nullable byte[]); +    method public int describeContents(); +    method public int getOrgId(); +    method public int getTdsFlags(); +    method @Nullable public byte[] getTransportData(); +    method public int getTransportDataLength(); +    method @Nullable public byte[] toByteArray(); +    method public int totalBytes(); +    method public void writeToParcel(@NonNull android.os.Parcel, int); +    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.TransportBlock> CREATOR; +  } + +  public final class TransportDiscoveryData implements android.os.Parcelable { +    ctor public TransportDiscoveryData(int, @NonNull java.util.List<android.bluetooth.le.TransportBlock>); +    ctor public TransportDiscoveryData(@NonNull byte[]); +    method public int describeContents(); +    method @NonNull public java.util.List<android.bluetooth.le.TransportBlock> getTransportBlocks(); +    method public int getTransportDataType(); +    method @Nullable public byte[] toByteArray(); +    method public int totalBytes(); +    method public void writeToParcel(@NonNull android.os.Parcel, int); +    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.TransportDiscoveryData> CREATOR; +  } +  }  package android.companion { diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java index cec658049ca5..fdf62ec3a647 100644 --- a/core/java/android/bluetooth/le/AdvertiseData.java +++ b/core/java/android/bluetooth/le/AdvertiseData.java @@ -25,6 +25,7 @@ import android.util.ArrayMap;  import android.util.SparseArray;  import java.util.ArrayList; +import java.util.Collections;  import java.util.List;  import java.util.Map;  import java.util.Objects; @@ -47,6 +48,9 @@ public final class AdvertiseData implements Parcelable {      @NonNull      private final List<ParcelUuid> mServiceSolicitationUuids; +    @Nullable +    private final List<TransportDiscoveryData> mTransportDiscoveryData; +      private final SparseArray<byte[]> mManufacturerSpecificData;      private final Map<ParcelUuid, byte[]> mServiceData;      private final boolean mIncludeTxPowerLevel; @@ -54,12 +58,14 @@ public final class AdvertiseData implements Parcelable {      private AdvertiseData(List<ParcelUuid> serviceUuids,              List<ParcelUuid> serviceSolicitationUuids, +            List<TransportDiscoveryData> transportDiscoveryData,              SparseArray<byte[]> manufacturerData,              Map<ParcelUuid, byte[]> serviceData,              boolean includeTxPowerLevel,              boolean includeDeviceName) {          mServiceUuids = serviceUuids;          mServiceSolicitationUuids = serviceSolicitationUuids; +        mTransportDiscoveryData = transportDiscoveryData;          mManufacturerSpecificData = manufacturerData;          mServiceData = serviceData;          mIncludeTxPowerLevel = includeTxPowerLevel; @@ -83,6 +89,17 @@ public final class AdvertiseData implements Parcelable {      }      /** +     * Returns a list of {@link TransportDiscoveryData} within the advertisement. +     */ +    @NonNull +    public List<TransportDiscoveryData> getTransportDiscoveryData() { +        if (mTransportDiscoveryData == null) { +            return Collections.emptyList(); +        } +        return mTransportDiscoveryData; +    } + +    /**       * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The       * manufacturer id is a non-negative number assigned by Bluetooth SIG.       */ @@ -116,8 +133,8 @@ public final class AdvertiseData implements Parcelable {       */      @Override      public int hashCode() { -        return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mManufacturerSpecificData, -                mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); +        return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mTransportDiscoveryData, +                mManufacturerSpecificData, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel);      }      /** @@ -134,6 +151,7 @@ public final class AdvertiseData implements Parcelable {          AdvertiseData other = (AdvertiseData) obj;          return Objects.equals(mServiceUuids, other.mServiceUuids)                  && Objects.equals(mServiceSolicitationUuids, other.mServiceSolicitationUuids) +                && Objects.equals(mTransportDiscoveryData, other.mTransportDiscoveryData)                  && BluetoothLeUtils.equals(mManufacturerSpecificData,                      other.mManufacturerSpecificData)                  && BluetoothLeUtils.equals(mServiceData, other.mServiceData) @@ -144,7 +162,8 @@ public final class AdvertiseData implements Parcelable {      @Override      public String toString() {          return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mServiceSolicitationUuids=" -                + mServiceSolicitationUuids + ", mManufacturerSpecificData=" +                + mServiceSolicitationUuids + ", mTransportDiscoveryData=" +                + mTransportDiscoveryData + ", mManufacturerSpecificData="                  + BluetoothLeUtils.toString(mManufacturerSpecificData) + ", mServiceData="                  + BluetoothLeUtils.toString(mServiceData)                  + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" @@ -162,6 +181,8 @@ public final class AdvertiseData implements Parcelable {          dest.writeTypedArray(mServiceSolicitationUuids.toArray(                  new ParcelUuid[mServiceSolicitationUuids.size()]), flags); +        dest.writeTypedList(mTransportDiscoveryData); +          // mManufacturerSpecificData could not be null.          dest.writeInt(mManufacturerSpecificData.size());          for (int i = 0; i < mManufacturerSpecificData.size(); ++i) { @@ -197,6 +218,12 @@ public final class AdvertiseData implements Parcelable {                          builder.addServiceSolicitationUuid(uuid);                      } +                    List<TransportDiscoveryData> transportDiscoveryData = +                            in.createTypedArrayList(TransportDiscoveryData.CREATOR); +                    for (TransportDiscoveryData tdd : transportDiscoveryData) { +                        builder.addTransportDiscoveryData(tdd); +                    } +                      int manufacturerSize = in.readInt();                      for (int i = 0; i < manufacturerSize; ++i) {                          int manufacturerId = in.readInt(); @@ -223,6 +250,9 @@ public final class AdvertiseData implements Parcelable {          private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>();          @NonNull          private List<ParcelUuid> mServiceSolicitationUuids = new ArrayList<ParcelUuid>(); +        @Nullable +        private List<TransportDiscoveryData> mTransportDiscoveryData = +                new ArrayList<TransportDiscoveryData>();          private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>();          private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>();          private boolean mIncludeTxPowerLevel; @@ -256,6 +286,7 @@ public final class AdvertiseData implements Parcelable {              mServiceSolicitationUuids.add(serviceSolicitationUuid);              return this;          } +          /**           * Add service data to advertise data.           * @@ -274,6 +305,23 @@ public final class AdvertiseData implements Parcelable {          }          /** +         * Add Transport Discovery Data to advertise data. +         * +         * @param transportDiscoveryData Transport Discovery Data, consisting of one or more +         * Transport Blocks. Transport Discovery Data AD Type Code is already included. +         * @throws IllegalArgumentException If the {@code transportDiscoveryData} is empty +         */ +        @NonNull +        public Builder addTransportDiscoveryData( +                @NonNull TransportDiscoveryData transportDiscoveryData) { +            if (transportDiscoveryData == null) { +                throw new IllegalArgumentException("transportDiscoveryData is null"); +            } +            mTransportDiscoveryData.add(transportDiscoveryData); +            return this; +        } + +        /**           * Add manufacturer specific data.           * <p>           * Please refer to the Bluetooth Assigned Numbers document provided by the <a @@ -319,8 +367,8 @@ public final class AdvertiseData implements Parcelable {           */          public AdvertiseData build() {              return new AdvertiseData(mServiceUuids, mServiceSolicitationUuids, -                    mManufacturerSpecificData, mServiceData, mIncludeTxPowerLevel, -                    mIncludeDeviceName); +                    mTransportDiscoveryData, mManufacturerSpecificData, mServiceData, +                    mIncludeTxPowerLevel, mIncludeDeviceName);          }      }  } diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 58029745ab9c..b9f8a57a75ea 100644 --- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -567,6 +567,9 @@ public final class BluetoothLeAdvertiser {                          + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;              }          } +        for (TransportDiscoveryData transportDiscoveryData : data.getTransportDiscoveryData()) { +            size += OVERHEAD_BYTES_PER_FIELD + transportDiscoveryData.totalBytes(); +        }          for (ParcelUuid uuid : data.getServiceData().keySet()) {              int uuidLen = BluetoothUuid.uuidToBytes(uuid).length;              size += OVERHEAD_BYTES_PER_FIELD + uuidLen diff --git a/core/java/android/bluetooth/le/TransportBlock.java b/core/java/android/bluetooth/le/TransportBlock.java new file mode 100644 index 000000000000..b388beda6b3b --- /dev/null +++ b/core/java/android/bluetooth/le/TransportBlock.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2014 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.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; + +/** + * Wrapper for Transport Discovery Data Transport Blocks. + * This class represents a Transport Block from a Transport Discovery Data. + * + * @see TransportDiscoveryData + * @see AdvertiseData + */ +public final class TransportBlock implements Parcelable { +    private static final String TAG = "TransportBlock"; +    private final int mOrgId; +    private final int mTdsFlags; +    private final int mTransportDataLength; +    private final byte[] mTransportData; + +    /** +     * Creates an instance of TransportBlock from raw data. +     * +     * @param orgId the Organization ID +     * @param tdsFlags the TDS flags +     * @param transportDataLength the total length of the Transport Data +     * @param transportData the Transport Data +     */ +    public TransportBlock(int orgId, int tdsFlags, int transportDataLength, +            @Nullable byte[] transportData) { +        mOrgId = orgId; +        mTdsFlags = tdsFlags; +        mTransportDataLength = transportDataLength; +        mTransportData = transportData; +    } + +    private TransportBlock(Parcel in) { +        mOrgId = in.readInt(); +        mTdsFlags = in.readInt(); +        mTransportDataLength = in.readInt(); +        mTransportData = new byte[mTransportDataLength]; +        in.readByteArray(mTransportData); +    } + +    @Override +    public void writeToParcel(@NonNull Parcel dest, int flags) { +        dest.writeInt(mOrgId); +        dest.writeInt(mTdsFlags); +        dest.writeInt(mTransportDataLength); +        dest.writeByteArray(mTransportData); +    } + +    /** +     * @hide +     */ +    @Override +    public int describeContents() { +        return 0; +    } + +    public static final @NonNull Creator<TransportBlock> CREATOR = new Creator<TransportBlock>() { +        @Override +        public TransportBlock createFromParcel(Parcel in) { +            return new TransportBlock(in); +        } + +        @Override +        public TransportBlock[] newArray(int size) { +            return new TransportBlock[size]; +        } +    }; + +    /** +     * Gets the Organization ID of the Transport Block which corresponds to one of the +     * the Bluetooth SIG Assigned Numbers. +     */ +    public int getOrgId() { +        return mOrgId; +    } + +    /** +     * Gets the TDS flags of the Transport Block which represents the role of the device and +     * information about its state and supported features. +     */ +    public int getTdsFlags() { +        return mTdsFlags; +    } + +    /** +     * Gets the total number of octets in the Transport Data field in this Transport Block. +     */ +    public int getTransportDataLength() { +        return mTransportDataLength; +    } + +    /** +     * Gets the Transport Data of the Transport Block which contains organization-specific data. +     */ +    @Nullable +    public byte[] getTransportData() { +        return mTransportData; +    } + +    /** +     * Converts this TransportBlock to byte array +     * +     * @return byte array representation of this Transport Block or null if the conversion failed +     */ +    @Nullable +    public byte[] toByteArray() { +        try { +            ByteBuffer buffer = ByteBuffer.allocate(totalBytes()); +            buffer.put((byte) mOrgId); +            buffer.put((byte) mTdsFlags); +            buffer.put((byte) mTransportDataLength); +            if (mTransportData != null) { +                buffer.put(mTransportData); +            } +            return buffer.array(); +        } catch (BufferOverflowException e) { +            Log.e(TAG, "Error converting to byte array: " + e.toString()); +            return null; +        } +    } + +    /** +     * @return total byte count of this TransportBlock +     */ +    public int totalBytes() { +        // 3 uint8 + byte[] length +        int size = 3 + mTransportDataLength; +        return size; +    } +} diff --git a/core/java/android/bluetooth/le/TransportDiscoveryData.java b/core/java/android/bluetooth/le/TransportDiscoveryData.java new file mode 100644 index 000000000000..c8e97f9a823a --- /dev/null +++ b/core/java/android/bluetooth/le/TransportDiscoveryData.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2014 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.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Wrapper for Transport Discovery Data AD Type. + * This class contains the Transport Discovery Data AD Type Code as well as + * a list of potential Transport Blocks. + * + * @see AdvertiseData + */ +public final class TransportDiscoveryData implements Parcelable { +    private static final String TAG = "TransportDiscoveryData"; +    private final int mTransportDataType; +    private final List<TransportBlock> mTransportBlocks; + +    /** +     * Creates a TransportDiscoveryData instance. +     * +     * @param transportDataType the Transport Discovery Data AD Type +     * @param transportBlocks the list of Transport Blocks +     */ +    public TransportDiscoveryData(int transportDataType, +            @NonNull List<TransportBlock> transportBlocks) { +        mTransportDataType = transportDataType; +        mTransportBlocks = transportBlocks; +    } + +    /** +     * Creates a TransportDiscoveryData instance from byte arrays. +     * +     * Uses the transport discovery data bytes and parses them into an usable class. +     * +     * @param transportDiscoveryData the raw discovery data +     */ +    public TransportDiscoveryData(@NonNull byte[] transportDiscoveryData) { +        ByteBuffer byteBuffer = ByteBuffer.wrap(transportDiscoveryData); +        mTransportBlocks = new ArrayList(); +        if (byteBuffer.remaining() > 0) { +            mTransportDataType = byteBuffer.get(); +        } else { +            mTransportDataType = -1; +        } +        try { +            while (byteBuffer.remaining() > 0) { +                int orgId = byteBuffer.get(); +                int tdsFlags = byteBuffer.get(); +                int transportDataLength = byteBuffer.get(); +                byte[] transportData = new byte[transportDataLength]; +                byteBuffer.get(transportData, 0, transportDataLength); +                mTransportBlocks.add(new TransportBlock(orgId, tdsFlags, +                        transportDataLength, transportData)); +            } +        } catch (BufferUnderflowException e) { +            Log.e(TAG, "Error while parsing data: " + e.toString()); +        } +    } + +    private TransportDiscoveryData(Parcel in) { +        mTransportDataType = in.readInt(); +        mTransportBlocks = in.createTypedArrayList(TransportBlock.CREATOR); +    } + +    /** +     * @hide +     */ +    @Override +    public int describeContents() { +        return 0; +    } + +    @Override +    public void writeToParcel(@NonNull Parcel dest, int flags) { +        dest.writeInt(mTransportDataType); +        dest.writeTypedList(mTransportBlocks); +    } + +    public static final @NonNull Creator<TransportDiscoveryData> CREATOR = +            new Creator<TransportDiscoveryData>() { +                @Override +                public TransportDiscoveryData createFromParcel(Parcel in) { +                    return new TransportDiscoveryData(in); +                } + +                @Override +                public TransportDiscoveryData[] newArray(int size) { +                    return new TransportDiscoveryData[size]; +                } +    }; + +    /** +     * Gets the transport data type. +     */ +    public int getTransportDataType() { +        return mTransportDataType; +    } + +    /** +     * @return the list of {@link TransportBlock} in this TransportDiscoveryData +     *         or an empty list if there are no Transport Blocks +     */ +    @NonNull +    public List<TransportBlock> getTransportBlocks() { +        if (mTransportBlocks == null) { +            return Collections.emptyList(); +        } +        return mTransportBlocks; +    } + +    /** +     * Converts this TransportDiscoveryData to byte array +     * +     * @return byte array representation of this Transport Discovery Data or null if the +     *         conversion failed +     */ +    @Nullable +    public byte[] toByteArray() { +        try { +            ByteBuffer buffer = ByteBuffer.allocate(totalBytes()); +            buffer.put((byte) mTransportDataType); +            for (TransportBlock transportBlock : getTransportBlocks()) { +                buffer.put(transportBlock.toByteArray()); +            } +            return buffer.array(); +        } catch (BufferOverflowException e) { +            Log.e(TAG, "Error converting to byte array: " + e.toString()); +            return null; +        } +    } + +    /** +     * @return total byte count of this TransportDataDiscovery +     */ +    public int totalBytes() { +        int size = 1; // Counting Transport Data Type here. +        for (TransportBlock transportBlock : getTransportBlocks()) { +            size += transportBlock.totalBytes(); +        } +        return size; +    } +} |