• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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