• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 com.android.bluetooth.gatt;
18 
19 import android.bluetooth.BluetoothUuid;
20 import android.bluetooth.le.AdvertiseData;
21 import android.bluetooth.le.TransportDiscoveryData;
22 import android.os.ParcelUuid;
23 import android.util.Log;
24 
25 import java.io.ByteArrayOutputStream;
26 
27 class AdvertiseHelper {
28 
29     private static final String TAG = "AdvertiseHelper";
30 
31     private static final int DEVICE_NAME_MAX = 26;
32 
33     private static final int COMPLETE_LIST_16_BIT_SERVICE_UUIDS = 0X03;
34     private static final int COMPLETE_LIST_32_BIT_SERVICE_UUIDS = 0X05;
35     private static final int COMPLETE_LIST_128_BIT_SERVICE_UUIDS = 0X07;
36     private static final int SHORTENED_LOCAL_NAME = 0X08;
37     private static final int COMPLETE_LOCAL_NAME = 0X09;
38     private static final int TX_POWER_LEVEL = 0x0A;
39     private static final int LIST_16_BIT_SERVICE_SOLICITATION_UUIDS = 0X14;
40     private static final int LIST_128_BIT_SERVICE_SOLICITATION_UUIDS = 0X15;
41     private static final int SERVICE_DATA_16_BIT_UUID = 0X16;
42     private static final int LIST_32_BIT_SERVICE_SOLICITATION_UUIDS = 0x1F;
43     private static final int SERVICE_DATA_32_BIT_UUID = 0X20;
44     private static final int SERVICE_DATA_128_BIT_UUID = 0X21;
45     private static final int TRANSPORT_DISCOVERY_DATA = 0X26;
46     private static final int MANUFACTURER_SPECIFIC_DATA = 0XFF;
47 
advertiseDataToBytes(AdvertiseData data, String name)48     public static byte[] advertiseDataToBytes(AdvertiseData data, String name) {
49 
50         if (data == null) {
51             return new byte[0];
52         }
53 
54         // Flags are added by lower layers of the stack, only if needed;
55         // no need to add them here.
56 
57         ByteArrayOutputStream ret = new ByteArrayOutputStream();
58 
59         if (data.getIncludeDeviceName()) {
60             try {
61                 byte[] nameBytes = name.getBytes("UTF-8");
62 
63                 int nameLength = nameBytes.length;
64                 byte type;
65 
66                 // TODO(jpawlowski) put a better limit on device name!
67                 if (nameLength > DEVICE_NAME_MAX) {
68                     nameLength = DEVICE_NAME_MAX;
69                     type = SHORTENED_LOCAL_NAME;
70                 } else {
71                     type = COMPLETE_LOCAL_NAME;
72                 }
73 
74                 check_length(type, nameLength + 1);
75                 ret.write(nameLength + 1);
76                 ret.write(type);
77                 ret.write(nameBytes, 0, nameLength);
78             } catch (java.io.UnsupportedEncodingException e) {
79                 Log.e(TAG, "Can't include name - encoding error!", e);
80             }
81         }
82 
83         for (int i = 0; i < data.getManufacturerSpecificData().size(); i++) {
84             int manufacturerId = data.getManufacturerSpecificData().keyAt(i);
85 
86             byte[] manufacturerData = data.getManufacturerSpecificData().get(manufacturerId);
87             int dataLen = 2 + (manufacturerData == null ? 0 : manufacturerData.length);
88             byte[] concated = new byte[dataLen];
89             // First two bytes are manufacturer id in little-endian.
90             concated[0] = (byte) (manufacturerId & 0xFF);
91             concated[1] = (byte) ((manufacturerId >> 8) & 0xFF);
92             if (manufacturerData != null) {
93                 System.arraycopy(manufacturerData, 0, concated, 2, manufacturerData.length);
94             }
95 
96             check_length(MANUFACTURER_SPECIFIC_DATA, concated.length + 1);
97             ret.write(concated.length + 1);
98             ret.write(MANUFACTURER_SPECIFIC_DATA);
99             ret.write(concated, 0, concated.length);
100         }
101 
102         if (data.getIncludeTxPowerLevel()) {
103             ret.write(2 /* Length */);
104             ret.write(TX_POWER_LEVEL);
105             ret.write(0); // lower layers will fill this value.
106         }
107 
108         if (data.getServiceUuids() != null) {
109             ByteArrayOutputStream serviceUuids16 = new ByteArrayOutputStream();
110             ByteArrayOutputStream serviceUuids32 = new ByteArrayOutputStream();
111             ByteArrayOutputStream serviceUuids128 = new ByteArrayOutputStream();
112 
113             for (ParcelUuid parcelUuid : data.getServiceUuids()) {
114                 byte[] uuid = BluetoothUuid.uuidToBytes(parcelUuid);
115 
116                 if (uuid.length == BluetoothUuid.UUID_BYTES_16_BIT) {
117                     serviceUuids16.write(uuid, 0, uuid.length);
118                 } else if (uuid.length == BluetoothUuid.UUID_BYTES_32_BIT) {
119                     serviceUuids32.write(uuid, 0, uuid.length);
120                 } else /*if (uuid.length == BluetoothUuid.UUID_BYTES_128_BIT)*/ {
121                     serviceUuids128.write(uuid, 0, uuid.length);
122                 }
123             }
124 
125             if (serviceUuids16.size() != 0) {
126                 check_length(COMPLETE_LIST_16_BIT_SERVICE_UUIDS, serviceUuids16.size() + 1);
127                 ret.write(serviceUuids16.size() + 1);
128                 ret.write(COMPLETE_LIST_16_BIT_SERVICE_UUIDS);
129                 ret.write(serviceUuids16.toByteArray(), 0, serviceUuids16.size());
130             }
131 
132             if (serviceUuids32.size() != 0) {
133                 check_length(COMPLETE_LIST_32_BIT_SERVICE_UUIDS, serviceUuids32.size() + 1);
134                 ret.write(serviceUuids32.size() + 1);
135                 ret.write(COMPLETE_LIST_32_BIT_SERVICE_UUIDS);
136                 ret.write(serviceUuids32.toByteArray(), 0, serviceUuids32.size());
137             }
138 
139             if (serviceUuids128.size() != 0) {
140                 check_length(COMPLETE_LIST_128_BIT_SERVICE_UUIDS, serviceUuids32.size() + 1);
141                 ret.write(serviceUuids128.size() + 1);
142                 ret.write(COMPLETE_LIST_128_BIT_SERVICE_UUIDS);
143                 ret.write(serviceUuids128.toByteArray(), 0, serviceUuids128.size());
144             }
145         }
146 
147         if (!data.getServiceData().isEmpty()) {
148             for (ParcelUuid parcelUuid : data.getServiceData().keySet()) {
149                 byte[] serviceData = data.getServiceData().get(parcelUuid);
150 
151                 byte[] uuid = BluetoothUuid.uuidToBytes(parcelUuid);
152                 int uuidLen = uuid.length;
153 
154                 int dataLen = uuidLen + (serviceData == null ? 0 : serviceData.length);
155                 byte[] concated = new byte[dataLen];
156 
157                 System.arraycopy(uuid, 0, concated, 0, uuidLen);
158 
159                 if (serviceData != null) {
160                     System.arraycopy(serviceData, 0, concated, uuidLen, serviceData.length);
161                 }
162 
163                 if (uuid.length == BluetoothUuid.UUID_BYTES_16_BIT) {
164                     check_length(SERVICE_DATA_16_BIT_UUID, concated.length + 1);
165                     ret.write(concated.length + 1);
166                     ret.write(SERVICE_DATA_16_BIT_UUID);
167                     ret.write(concated, 0, concated.length);
168                 } else if (uuid.length == BluetoothUuid.UUID_BYTES_32_BIT) {
169                     check_length(SERVICE_DATA_32_BIT_UUID, concated.length + 1);
170                     ret.write(concated.length + 1);
171                     ret.write(SERVICE_DATA_32_BIT_UUID);
172                     ret.write(concated, 0, concated.length);
173                 } else /*if (uuid.length == BluetoothUuid.UUID_BYTES_128_BIT)*/ {
174                     check_length(SERVICE_DATA_128_BIT_UUID, concated.length + 1);
175                     ret.write(concated.length + 1);
176                     ret.write(SERVICE_DATA_128_BIT_UUID);
177                     ret.write(concated, 0, concated.length);
178                 }
179             }
180         }
181 
182 
183         if (data.getServiceSolicitationUuids() != null) {
184             ByteArrayOutputStream serviceUuids16 = new ByteArrayOutputStream();
185             ByteArrayOutputStream serviceUuids32 = new ByteArrayOutputStream();
186             ByteArrayOutputStream serviceUuids128 = new ByteArrayOutputStream();
187 
188             for (ParcelUuid parcelUuid : data.getServiceSolicitationUuids()) {
189                 byte[] uuid = BluetoothUuid.uuidToBytes(parcelUuid);
190 
191                 if (uuid.length == BluetoothUuid.UUID_BYTES_16_BIT) {
192                     serviceUuids16.write(uuid, 0, uuid.length);
193                 } else if (uuid.length == BluetoothUuid.UUID_BYTES_32_BIT) {
194                     serviceUuids32.write(uuid, 0, uuid.length);
195                 } else /*if (uuid.length == BluetoothUuid.UUID_BYTES_128_BIT)*/ {
196                     serviceUuids128.write(uuid, 0, uuid.length);
197                 }
198             }
199 
200             if (serviceUuids16.size() != 0) {
201                 check_length(LIST_16_BIT_SERVICE_SOLICITATION_UUIDS, serviceUuids16.size() + 1);
202                 ret.write(serviceUuids16.size() + 1);
203                 ret.write(LIST_16_BIT_SERVICE_SOLICITATION_UUIDS);
204                 ret.write(serviceUuids16.toByteArray(), 0, serviceUuids16.size());
205             }
206 
207             if (serviceUuids32.size() != 0) {
208                 check_length(LIST_32_BIT_SERVICE_SOLICITATION_UUIDS, serviceUuids32.size() + 1);
209                 ret.write(serviceUuids32.size() + 1);
210                 ret.write(LIST_32_BIT_SERVICE_SOLICITATION_UUIDS);
211                 ret.write(serviceUuids32.toByteArray(), 0, serviceUuids32.size());
212             }
213 
214             if (serviceUuids128.size() != 0) {
215                 check_length(LIST_128_BIT_SERVICE_SOLICITATION_UUIDS, serviceUuids128.size() + 1);
216                 ret.write(serviceUuids128.size() + 1);
217                 ret.write(LIST_128_BIT_SERVICE_SOLICITATION_UUIDS);
218                 ret.write(serviceUuids128.toByteArray(), 0, serviceUuids128.size());
219             }
220         }
221 
222         for (TransportDiscoveryData transportDiscoveryData : data.getTransportDiscoveryData()) {
223             ret.write(transportDiscoveryData.totalBytes());
224             ret.write(transportDiscoveryData.toByteArray(),
225                     0, transportDiscoveryData.totalBytes());
226         }
227         return ret.toByteArray();
228     }
229 
check_length(int type, int length)230     static void check_length(int type, int length) {
231         if (length > 255) {
232             Log.w(TAG, "Length of data with type " + Integer.toString(type, 16)
233                     + " is grater than 255");
234             throw new IllegalArgumentException("Length of data is grater than 255");
235         }
236     }
237 }
238