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