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