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.os.ParcelUuid; 22 import android.util.Log; 23 24 import java.io.ByteArrayOutputStream; 25 26 class AdvertiseHelper { 27 28 private static final String TAG = "AdvertiseHelper"; 29 30 private static final int DEVICE_NAME_MAX = 26; 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 LIST_16_BIT_SERVICE_SOLICITATION_UUIDS = 0X14; 39 private static final int LIST_128_BIT_SERVICE_SOLICITATION_UUIDS = 0X15; 40 private static final int SERVICE_DATA_16_BIT_UUID = 0X16; 41 private static final int LIST_32_BIT_SERVICE_SOLICITATION_UUIDS = 0x1F; 42 private static final int SERVICE_DATA_32_BIT_UUID = 0X20; 43 private static final int SERVICE_DATA_128_BIT_UUID = 0X21; 44 private static final int MANUFACTURER_SPECIFIC_DATA = 0XFF; 45 advertiseDataToBytes(AdvertiseData data, String name)46 public static byte[] advertiseDataToBytes(AdvertiseData data, String name) { 47 48 if (data == null) { 49 return new byte[0]; 50 } 51 52 // Flags are added by lower layers of the stack, only if needed; 53 // no need to add them here. 54 55 ByteArrayOutputStream ret = new ByteArrayOutputStream(); 56 57 if (data.getIncludeDeviceName()) { 58 try { 59 byte[] nameBytes = name.getBytes("UTF-8"); 60 61 int nameLength = nameBytes.length; 62 byte type; 63 64 // TODO(jpawlowski) put a better limit on device name! 65 if (nameLength > DEVICE_NAME_MAX) { 66 nameLength = DEVICE_NAME_MAX; 67 type = SHORTENED_LOCAL_NAME; 68 } else { 69 type = COMPLETE_LOCAL_NAME; 70 } 71 72 ret.write(nameLength + 1); 73 ret.write(type); 74 ret.write(nameBytes, 0, nameLength); 75 } catch (java.io.UnsupportedEncodingException e) { 76 Log.e(TAG, "Can't include name - encoding error!", e); 77 } 78 } 79 80 for (int i = 0; i < data.getManufacturerSpecificData().size(); i++) { 81 int manufacturerId = data.getManufacturerSpecificData().keyAt(i); 82 83 byte[] manufacturerData = data.getManufacturerSpecificData().get(manufacturerId); 84 int dataLen = 2 + (manufacturerData == null ? 0 : manufacturerData.length); 85 byte[] concated = new byte[dataLen]; 86 // First two bytes are manufacturer id in little-endian. 87 concated[0] = (byte) (manufacturerId & 0xFF); 88 concated[1] = (byte) ((manufacturerId >> 8) & 0xFF); 89 if (manufacturerData != null) { 90 System.arraycopy(manufacturerData, 0, concated, 2, manufacturerData.length); 91 } 92 93 ret.write(concated.length + 1); 94 ret.write(MANUFACTURER_SPECIFIC_DATA); 95 ret.write(concated, 0, concated.length); 96 } 97 98 if (data.getIncludeTxPowerLevel()) { 99 ret.write(2 /* Length */); 100 ret.write(TX_POWER_LEVEL); 101 ret.write(0); // lower layers will fill this value. 102 } 103 104 if (data.getServiceUuids() != null) { 105 ByteArrayOutputStream serviceUuids16 = new ByteArrayOutputStream(); 106 ByteArrayOutputStream serviceUuids32 = new ByteArrayOutputStream(); 107 ByteArrayOutputStream serviceUuids128 = new ByteArrayOutputStream(); 108 109 for (ParcelUuid parcelUuid : data.getServiceUuids()) { 110 byte[] uuid = BluetoothUuid.uuidToBytes(parcelUuid); 111 112 if (uuid.length == BluetoothUuid.UUID_BYTES_16_BIT) { 113 serviceUuids16.write(uuid, 0, uuid.length); 114 } else if (uuid.length == BluetoothUuid.UUID_BYTES_32_BIT) { 115 serviceUuids32.write(uuid, 0, uuid.length); 116 } else /*if (uuid.length == BluetoothUuid.UUID_BYTES_128_BIT)*/ { 117 serviceUuids128.write(uuid, 0, uuid.length); 118 } 119 } 120 121 if (serviceUuids16.size() != 0) { 122 ret.write(serviceUuids16.size() + 1); 123 ret.write(COMPLETE_LIST_16_BIT_SERVICE_UUIDS); 124 ret.write(serviceUuids16.toByteArray(), 0, serviceUuids16.size()); 125 } 126 127 if (serviceUuids32.size() != 0) { 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 ret.write(serviceUuids128.size() + 1); 135 ret.write(COMPLETE_LIST_128_BIT_SERVICE_UUIDS); 136 ret.write(serviceUuids128.toByteArray(), 0, serviceUuids128.size()); 137 } 138 } 139 140 if (!data.getServiceData().isEmpty()) { 141 for (ParcelUuid parcelUuid : data.getServiceData().keySet()) { 142 byte[] serviceData = data.getServiceData().get(parcelUuid); 143 144 byte[] uuid = BluetoothUuid.uuidToBytes(parcelUuid); 145 int uuidLen = uuid.length; 146 147 int dataLen = uuidLen + (serviceData == null ? 0 : serviceData.length); 148 byte[] concated = new byte[dataLen]; 149 150 System.arraycopy(uuid, 0, concated, 0, uuidLen); 151 152 if (serviceData != null) { 153 System.arraycopy(serviceData, 0, concated, uuidLen, serviceData.length); 154 } 155 156 if (uuid.length == BluetoothUuid.UUID_BYTES_16_BIT) { 157 ret.write(concated.length + 1); 158 ret.write(SERVICE_DATA_16_BIT_UUID); 159 ret.write(concated, 0, concated.length); 160 } else if (uuid.length == BluetoothUuid.UUID_BYTES_32_BIT) { 161 ret.write(concated.length + 1); 162 ret.write(SERVICE_DATA_32_BIT_UUID); 163 ret.write(concated, 0, concated.length); 164 } else /*if (uuid.length == BluetoothUuid.UUID_BYTES_128_BIT)*/ { 165 ret.write(concated.length + 1); 166 ret.write(SERVICE_DATA_128_BIT_UUID); 167 ret.write(concated, 0, concated.length); 168 } 169 } 170 } 171 172 173 if (data.getServiceSolicitationUuids() != null) { 174 ByteArrayOutputStream serviceUuids16 = new ByteArrayOutputStream(); 175 ByteArrayOutputStream serviceUuids32 = new ByteArrayOutputStream(); 176 ByteArrayOutputStream serviceUuids128 = new ByteArrayOutputStream(); 177 178 for (ParcelUuid parcelUuid : data.getServiceSolicitationUuids()) { 179 byte[] uuid = BluetoothUuid.uuidToBytes(parcelUuid); 180 181 if (uuid.length == BluetoothUuid.UUID_BYTES_16_BIT) { 182 serviceUuids16.write(uuid, 0, uuid.length); 183 } else if (uuid.length == BluetoothUuid.UUID_BYTES_32_BIT) { 184 serviceUuids32.write(uuid, 0, uuid.length); 185 } else /*if (uuid.length == BluetoothUuid.UUID_BYTES_128_BIT)*/ { 186 serviceUuids128.write(uuid, 0, uuid.length); 187 } 188 } 189 190 if (serviceUuids16.size() != 0) { 191 ret.write(serviceUuids16.size() + 1); 192 ret.write(LIST_16_BIT_SERVICE_SOLICITATION_UUIDS); 193 ret.write(serviceUuids16.toByteArray(), 0, serviceUuids16.size()); 194 } 195 196 if (serviceUuids32.size() != 0) { 197 ret.write(serviceUuids32.size() + 1); 198 ret.write(LIST_32_BIT_SERVICE_SOLICITATION_UUIDS); 199 ret.write(serviceUuids32.toByteArray(), 0, serviceUuids32.size()); 200 } 201 202 if (serviceUuids128.size() != 0) { 203 ret.write(serviceUuids128.size() + 1); 204 ret.write(LIST_128_BIT_SERVICE_SOLICITATION_UUIDS); 205 ret.write(serviceUuids128.toByteArray(), 0, serviceUuids128.size()); 206 } 207 } 208 return ret.toByteArray(); 209 } 210 } 211