1 /*
2  * Copyright (C) 2014 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.NonNull;
20 import android.annotation.Nullable;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.util.Log;
24 
25 import java.nio.BufferOverflowException;
26 import java.nio.BufferUnderflowException;
27 import java.nio.ByteBuffer;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.List;
32 
33 /**
34  * Wrapper for Transport Discovery Data AD Type. This class contains the Transport Discovery Data AD
35  * Type Code as well as a list of potential Transport Blocks.
36  *
37  * @see AdvertiseData
38  */
39 public final class TransportDiscoveryData implements Parcelable {
40     private static final String TAG = "TransportDiscoveryData";
41     private final int mTransportDataType;
42     private final List<TransportBlock> mTransportBlocks;
43 
44     /**
45      * Creates a TransportDiscoveryData instance.
46      *
47      * @param transportDataType the Transport Discovery Data AD Type
48      * @param transportBlocks the list of Transport Blocks
49      */
TransportDiscoveryData( int transportDataType, @NonNull List<TransportBlock> transportBlocks)50     public TransportDiscoveryData(
51             int transportDataType, @NonNull List<TransportBlock> transportBlocks) {
52         mTransportDataType = transportDataType;
53         mTransportBlocks = transportBlocks;
54     }
55 
56     /**
57      * Creates a TransportDiscoveryData instance from byte arrays.
58      *
59      * <p>Uses the transport discovery data bytes and parses them into an usable class.
60      *
61      * @param transportDiscoveryData the raw discovery data
62      */
TransportDiscoveryData(@onNull byte[] transportDiscoveryData)63     public TransportDiscoveryData(@NonNull byte[] transportDiscoveryData) {
64         ByteBuffer byteBuffer = ByteBuffer.wrap(transportDiscoveryData);
65         mTransportBlocks = new ArrayList();
66         if (byteBuffer.remaining() > 0) {
67             mTransportDataType = byteBuffer.get();
68         } else {
69             mTransportDataType = -1;
70         }
71         try {
72             while (byteBuffer.remaining() > 0) {
73                 int orgId = byteBuffer.get();
74                 int tdsFlags = byteBuffer.get();
75                 int transportDataLength = byteBuffer.get();
76                 byte[] transportData = new byte[transportDataLength];
77                 byteBuffer.get(transportData, 0, transportDataLength);
78                 mTransportBlocks.add(
79                         new TransportBlock(orgId, tdsFlags, transportDataLength, transportData));
80             }
81         } catch (BufferUnderflowException e) {
82             Log.e(TAG, "Error while parsing data: " + e.toString());
83         }
84     }
85 
TransportDiscoveryData(Parcel in)86     private TransportDiscoveryData(Parcel in) {
87         mTransportDataType = in.readInt();
88         mTransportBlocks = in.createTypedArrayList(TransportBlock.CREATOR);
89     }
90 
91     /** @hide */
92     @Override
describeContents()93     public int describeContents() {
94         return 0;
95     }
96 
97     /** @hide */
98     @Override
equals(@ullable Object obj)99     public boolean equals(@Nullable Object obj) {
100         if (this == obj) {
101             return true;
102         }
103         if (obj == null || getClass() != obj.getClass()) {
104             return false;
105         }
106         TransportDiscoveryData other = (TransportDiscoveryData) obj;
107         return Arrays.equals(toByteArray(), other.toByteArray());
108     }
109 
110     /** @hide */
111     @Override
hashCode()112     public int hashCode() {
113         return Arrays.hashCode(toByteArray());
114     }
115 
116     @Override
writeToParcel(@onNull Parcel dest, int flags)117     public void writeToParcel(@NonNull Parcel dest, int flags) {
118         dest.writeInt(mTransportDataType);
119         dest.writeTypedList(mTransportBlocks);
120     }
121 
122     public static final @NonNull Creator<TransportDiscoveryData> CREATOR =
123             new Creator<TransportDiscoveryData>() {
124                 @Override
125                 public TransportDiscoveryData createFromParcel(Parcel in) {
126                     return new TransportDiscoveryData(in);
127                 }
128 
129                 @Override
130                 public TransportDiscoveryData[] newArray(int size) {
131                     return new TransportDiscoveryData[size];
132                 }
133             };
134 
135     /** Gets the transport data type. */
getTransportDataType()136     public int getTransportDataType() {
137         return mTransportDataType;
138     }
139 
140     /**
141      * @return the list of {@link TransportBlock} in this TransportDiscoveryData or an empty list if
142      *     there are no Transport Blocks
143      */
144     @NonNull
getTransportBlocks()145     public List<TransportBlock> getTransportBlocks() {
146         if (mTransportBlocks == null) {
147             return Collections.emptyList();
148         }
149         return mTransportBlocks;
150     }
151 
152     /**
153      * Converts this TransportDiscoveryData to byte array
154      *
155      * @return byte array representation of this Transport Discovery Data or null if the conversion
156      *     failed
157      */
158     @Nullable
toByteArray()159     public byte[] toByteArray() {
160         try {
161             ByteBuffer buffer = ByteBuffer.allocate(totalBytes());
162             buffer.put((byte) mTransportDataType);
163             for (TransportBlock transportBlock : getTransportBlocks()) {
164                 buffer.put(transportBlock.toByteArray());
165             }
166             return buffer.array();
167         } catch (BufferOverflowException e) {
168             Log.e(TAG, "Error converting to byte array: " + e.toString());
169             return null;
170         }
171     }
172 
173     @Override
toString()174     public String toString() {
175         return BluetoothLeUtils.toString(toByteArray());
176     }
177 
178     /**
179      * @return total byte count of this TransportDataDiscovery
180      */
totalBytes()181     public int totalBytes() {
182         int size = 1; // Counting Transport Data Type here.
183         for (TransportBlock transportBlock : getTransportBlocks()) {
184             size += transportBlock.totalBytes();
185         }
186         return size;
187     }
188 }
189