AdvertiseHelper.java revision 917f34e18aa235e68ac0663e24f52100e95970b7
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
17package com.android.bluetooth.gatt;
18
19import android.bluetooth.BluetoothUuid;
20import android.bluetooth.le.AdvertiseData;
21import android.os.ParcelUuid;
22import android.util.Log;
23import com.android.bluetooth.Utils;
24import java.io.ByteArrayOutputStream;
25
26class AdvertiseHelper {
27
28  private static final String TAG = "AdvertiseHelper";
29
30  private static final int DEVICE_NAME_MAX = 18;
31
32  private static final int COMPLETE_LIST_16_BIT_SERVICE_UUIDS = 0X03;
33  private static final int COMPLETE_LIST_32_BIT_SERVICE_UUIDS = 0X05;
34  private static final int COMPLETE_LIST_128_BIT_SERVICE_UUIDS = 0X07;
35  private static final int SHORTENED_LOCAL_NAME = 0X08;
36  private static final int COMPLETE_LOCAL_NAME = 0X09;
37  private static final int TX_POWER_LEVEL = 0x0A;
38  private static final int SERVICE_DATA_16_BIT_UUID = 0X16;
39  private static final int SERVICE_DATA_32_BIT_UUID = 0X20;
40  private static final int SERVICE_DATA_128_BIT_UUID = 0X21;
41  private static final int MANUFACTURER_SPECIFIC_DATA = 0XFF;
42
43  public static byte[] advertiseDataToBytes(AdvertiseData data, String name) {
44
45    if (data == null) return new byte[0];
46
47    // Flags are added by lower layers of the stack, only if needed;
48    // no need to add them here.
49
50    ByteArrayOutputStream ret = new ByteArrayOutputStream();
51
52    if (data.getIncludeDeviceName()) {
53      try {
54        byte[] nameBytes = name.getBytes("UTF-8");
55
56        int nameLength = nameBytes.length;
57        byte type;
58
59        // TODO(jpawlowski) put a better limit on device name!
60        if (nameLength > DEVICE_NAME_MAX) {
61          nameLength = DEVICE_NAME_MAX;
62          type = SHORTENED_LOCAL_NAME;
63        } else {
64          type = COMPLETE_LOCAL_NAME;
65        }
66
67        ret.write(nameLength + 1);
68        ret.write(type);
69        ret.write(nameBytes, 0, nameLength);
70      } catch (java.io.UnsupportedEncodingException e) {
71        Log.e(TAG, "Can't include name - encoding error!", e);
72      }
73    }
74
75    for (int i = 0; i < data.getManufacturerSpecificData().size(); i++) {
76      int manufacturerId = data.getManufacturerSpecificData().keyAt(i);
77
78      byte[] manufacturerData = data.getManufacturerSpecificData().get(manufacturerId);
79      int dataLen = 2 + (manufacturerData == null ? 0 : manufacturerData.length);
80      byte[] concated = new byte[dataLen];
81      // First two bytes are manufacturer id in little-endian.
82      concated[0] = (byte) (manufacturerId & 0xFF);
83      concated[1] = (byte) ((manufacturerId >> 8) & 0xFF);
84      if (manufacturerData != null) {
85        System.arraycopy(manufacturerData, 0, concated, 2, manufacturerData.length);
86      }
87
88      ret.write(concated.length + 1);
89      ret.write(MANUFACTURER_SPECIFIC_DATA);
90      ret.write(concated, 0, concated.length);
91    }
92
93    if (data.getIncludeTxPowerLevel()) {
94      ret.write(2 /* Length */);
95      ret.write(TX_POWER_LEVEL);
96      ret.write(0); // lower layers will fill this value.
97    }
98
99    if (data.getServiceUuids() != null) {
100      ByteArrayOutputStream serviceUuids16 = new ByteArrayOutputStream();
101      ByteArrayOutputStream serviceUuids32 = new ByteArrayOutputStream();
102      ByteArrayOutputStream serviceUuids128 = new ByteArrayOutputStream();
103
104      for (ParcelUuid parcelUuid : data.getServiceUuids()) {
105        byte[] uuid = BluetoothUuid.uuidToBytes(parcelUuid);
106
107        if (uuid.length == BluetoothUuid.UUID_BYTES_16_BIT) {
108          serviceUuids16.write(uuid, 0, uuid.length);
109        } else if (uuid.length == BluetoothUuid.UUID_BYTES_32_BIT) {
110          serviceUuids32.write(uuid, 0, uuid.length);
111        } else /*if (uuid.length == BluetoothUuid.UUID_BYTES_128_BIT)*/ {
112          serviceUuids128.write(uuid, 0, uuid.length);
113        }
114      }
115
116      if (serviceUuids16.size() != 0) {
117        ret.write(serviceUuids16.size() + 1);
118        ret.write(COMPLETE_LIST_16_BIT_SERVICE_UUIDS);
119        ret.write(serviceUuids16.toByteArray(), 0, serviceUuids16.size());
120      }
121
122      if (serviceUuids32.size() != 0) {
123        ret.write(serviceUuids32.size() + 1);
124        ret.write(COMPLETE_LIST_32_BIT_SERVICE_UUIDS);
125        ret.write(serviceUuids32.toByteArray(), 0, serviceUuids32.size());
126      }
127
128      if (serviceUuids128.size() != 0) {
129        ret.write(serviceUuids128.size() + 1);
130        ret.write(COMPLETE_LIST_128_BIT_SERVICE_UUIDS);
131        ret.write(serviceUuids128.toByteArray(), 0, serviceUuids128.size());
132      }
133    }
134
135    if (!data.getServiceData().isEmpty()) {
136      for (ParcelUuid parcelUuid : data.getServiceData().keySet()) {
137        byte[] serviceData = data.getServiceData().get(parcelUuid);
138
139        byte[] uuid = BluetoothUuid.uuidToBytes(parcelUuid);
140        int uuidLen = uuid.length;
141
142        int dataLen = uuidLen + (serviceData == null ? 0 : serviceData.length);
143        byte[] concated = new byte[dataLen];
144
145        System.arraycopy(uuid, 0, concated, 0, uuidLen);
146
147        if (serviceData != null) {
148          System.arraycopy(serviceData, 0, concated, uuidLen, serviceData.length);
149        }
150
151        if (uuid.length == BluetoothUuid.UUID_BYTES_16_BIT) {
152          ret.write(concated.length + 1);
153          ret.write(SERVICE_DATA_16_BIT_UUID);
154          ret.write(concated, 0, concated.length);
155        } else if (uuid.length == BluetoothUuid.UUID_BYTES_32_BIT) {
156          ret.write(concated.length + 1);
157          ret.write(SERVICE_DATA_32_BIT_UUID);
158          ret.write(concated, 0, concated.length);
159        } else /*if (uuid.length == BluetoothUuid.UUID_BYTES_128_BIT)*/ {
160          ret.write(concated.length + 1);
161          ret.write(SERVICE_DATA_128_BIT_UUID);
162          ret.write(concated, 0, concated.length);
163        }
164      }
165    }
166
167    return ret.toByteArray();
168  }
169}
170