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