1 /* 2 * Copyright 2021 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.libraries.testing.deviceshadower.internal.utils; 18 19 import android.bluetooth.le.AdvertiseData; 20 import android.os.ParcelUuid; 21 import android.util.SparseArray; 22 23 import com.android.libraries.testing.deviceshadower.internal.bluetooth.BluetoothConstants; 24 25 import com.google.common.io.ByteArrayDataOutput; 26 import com.google.common.io.ByteStreams; 27 28 import java.nio.ByteBuffer; 29 import java.nio.ByteOrder; 30 import java.nio.charset.Charset; 31 import java.util.Map; 32 import java.util.Map.Entry; 33 import java.util.UUID; 34 35 /** 36 * Helper class for Gatt functionality. 37 */ 38 public class GattHelper { 39 convertAdvertiseData( AdvertiseData data, int txPowerLevel, String localName, boolean isConnectable)40 public static byte[] convertAdvertiseData( 41 AdvertiseData data, int txPowerLevel, String localName, boolean isConnectable) { 42 if (data == null) { 43 return new byte[0]; 44 } 45 ByteArrayDataOutput result = ByteStreams.newDataOutput(); 46 if (isConnectable) { 47 writeDataUnit( 48 result, 49 BluetoothConstants.DATA_TYPE_FLAGS, 50 new byte[]{BluetoothConstants.FLAGS_IN_CONNECTABLE_PACKETS}); 51 } 52 // tx power level is signed 8-bit int, range -100 to 20. 53 if (data.getIncludeTxPowerLevel()) { 54 writeDataUnit( 55 result, 56 BluetoothConstants.DATA_TYPE_TX_POWER_LEVEL, 57 new byte[]{(byte) txPowerLevel}); 58 } 59 // Local name 60 if (data.getIncludeDeviceName()) { 61 writeDataUnit( 62 result, 63 BluetoothConstants.DATA_TYPE_LOCAL_NAME_COMPLETE, 64 localName.getBytes(Charset.defaultCharset())); 65 } 66 // Manufacturer data 67 SparseArray<byte[]> manufacturerData = data.getManufacturerSpecificData(); 68 for (int i = 0; i < manufacturerData.size(); i++) { 69 int manufacturerId = manufacturerData.keyAt(i); 70 writeDataUnit( 71 result, 72 BluetoothConstants.DATA_TYPE_MANUFACTURER_SPECIFIC_DATA, 73 parseManufacturerData(manufacturerId, manufacturerData.get(manufacturerId)) 74 ); 75 } 76 // Service data 77 Map<ParcelUuid, byte[]> serviceData = data.getServiceData(); 78 for (Entry<ParcelUuid, byte[]> entry : serviceData.entrySet()) { 79 writeDataUnit( 80 result, 81 BluetoothConstants.DATA_TYPE_SERVICE_DATA, 82 parseServiceData(entry.getKey().getUuid(), entry.getValue()) 83 ); 84 } 85 // Service UUID, 128-bit UUID in little endian 86 if (data.getServiceUuids() != null && !data.getServiceUuids().isEmpty()) { 87 ByteBuffer uuidBytes = 88 ByteBuffer.allocate(data.getServiceUuids().size() * 16) 89 .order(ByteOrder.LITTLE_ENDIAN); 90 for (ParcelUuid parcelUuid : data.getServiceUuids()) { 91 UUID uuid = parcelUuid.getUuid(); 92 uuidBytes.putLong(uuid.getLeastSignificantBits()) 93 .putLong(uuid.getMostSignificantBits()); 94 } 95 writeDataUnit( 96 result, 97 BluetoothConstants.DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE, 98 uuidBytes.array() 99 ); 100 } 101 return result.toByteArray(); 102 } 103 parseServiceData(UUID uuid, byte[] serviceData)104 private static byte[] parseServiceData(UUID uuid, byte[] serviceData) { 105 // First two bytes of the data are data UUID in little endian 106 int length = 2 + serviceData.length; 107 byte[] result = new byte[length]; 108 // extract 16-bit UUID value 109 int uuidValue = (int) ((uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >>> 32); 110 result[0] = (byte) (uuidValue & 0xFF); 111 result[1] = (byte) ((uuidValue >> 8) & 0xFF); 112 System.arraycopy(serviceData, 0, result, 2, serviceData.length); 113 return result; 114 115 } 116 parseManufacturerData(int manufacturerId, byte[] manufacturerData)117 private static byte[] parseManufacturerData(int manufacturerId, byte[] manufacturerData) { 118 // First two bytes are manufacturer id in little endian. 119 int length = 2 + manufacturerData.length; 120 byte[] result = new byte[length]; 121 result[0] = (byte) (manufacturerId & 0xFF); 122 result[1] = (byte) ((manufacturerId >> 8) & 0xFF); 123 System.arraycopy(manufacturerData, 0, result, 2, manufacturerData.length); 124 return result; 125 } 126 writeDataUnit(ByteArrayDataOutput output, int type, byte[] data)127 private static void writeDataUnit(ByteArrayDataOutput output, int type, byte[] data) { 128 // Length includes the length of the field type, which is 1 byte. 129 int length = 1 + data.length; 130 // Length and type are unsigned 8-bit int. Assume the values are valid. 131 output.write(length); 132 output.write(type); 133 output.write(data); 134 } 135 GattHelper()136 private GattHelper() { 137 } 138 } 139