]> git.basschouten.com Git - openhab-addons.git/blob
40ffc87606612ae923c70d95f2e4c8e638e971c9
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.bluetooth.bluegiga.internal.eir;
14
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collections;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.UUID;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24
25 /**
26  * Defines an EIR record used in the BLE advertisement packets.
27  *
28  * @author Chris Jackson - Initial contribution
29  *
30  */
31 @NonNullByDefault
32 public class EirRecord {
33     private EirDataType type = EirDataType.UNKNOWN;
34     private Object record = new Object();
35
36     EirRecord(int @Nullable [] data) {
37         if (data == null || data.length == 0) {
38             return;
39         }
40
41         type = EirDataType.getEirPacketType(data[0]);
42         switch (type) {
43             case EIR_FLAGS:
44                 record = processFlags(data);
45                 break;
46             case EIR_MANUFACTURER_SPECIFIC:
47                 record = processManufacturer(data);
48                 break;
49             case EIR_SVC_UUID16_COMPLETE:
50             case EIR_SVC_UUID16_INCOMPLETE:
51                 record = processUuid16(data);
52                 break;
53             case EIR_SVC_UUID32_COMPLETE:
54             case EIR_SVC_UUID32_INCOMPLETE:
55                 record = processUuid32(data);
56                 break;
57             case EIR_SVC_UUID128_COMPLETE:
58             case EIR_SVC_UUID128_INCOMPLETE:
59                 record = processUuid128(data);
60                 break;
61             case EIR_NAME_LONG:
62             case EIR_NAME_SHORT:
63                 record = processString(data);
64                 break;
65             case EIR_TXPOWER:
66                 record = processUInt8(data);
67                 break;
68             case EIR_SLAVEINTERVALRANGE:
69                 record = processUInt16List(data);
70                 break;
71             case EIR_DEVICE_CLASS:
72                 record = processUInt8(data);
73                 break;
74             case EIR_SVC_DATA_UUID16:
75                 record = processUUID16ServiceData(data);
76                 break;
77             case EIR_SVC_DATA_UUID32:
78                 record = processUUID32ServiceData(data);
79                 break;
80             case EIR_SVC_DATA_UUID128:
81                 record = processUUID128ServiceData(data);
82                 break;
83             default:
84                 record = processUnknown(data);
85                 break;
86         }
87     }
88
89     private Map<Short, int[]> processManufacturer(int[] data) {
90         short manufacturer = (short) (((data[2] & 0xFFFF) << 8) | (data[1] & 0xFFFF));
91         return Collections.singletonMap(manufacturer, Arrays.copyOfRange(data, 3, data.length));
92     }
93
94     private Map<UUID, int[]> processUUID16ServiceData(int[] data) {
95         return Collections.singletonMap(process16BitUUID(data, 1), Arrays.copyOfRange(data, 3, data.length));
96     }
97
98     private Map<UUID, int[]> processUUID32ServiceData(int[] data) {
99         return Collections.singletonMap(process32BitUUID(data, 1), Arrays.copyOfRange(data, 5, data.length));
100     }
101
102     private Map<UUID, int[]> processUUID128ServiceData(int[] data) {
103         return Collections.singletonMap(process128BitUUID(data, 1), Arrays.copyOfRange(data, 17, data.length));
104     }
105
106     private List<UUID> processUuid16(int[] data) {
107         List<UUID> uuidList = new ArrayList<>();
108
109         for (int cnt = 1; cnt < data.length - 1; cnt += 2) {
110             uuidList.add(process16BitUUID(data, cnt));
111         }
112
113         return uuidList;
114     }
115
116     private List<UUID> processUuid32(int[] data) {
117         List<UUID> uuidList = new ArrayList<>();
118
119         for (int cnt = 1; cnt < data.length - 1; cnt += 4) {
120             uuidList.add(process32BitUUID(data, cnt));
121         }
122
123         return uuidList;
124     }
125
126     private List<UUID> processUuid128(int[] data) {
127         List<UUID> uuidList = new ArrayList<>();
128
129         for (int cnt = 1; cnt < data.length - 1; cnt += 16) {
130             uuidList.add(process128BitUUID(data, cnt));
131         }
132
133         return uuidList;
134     }
135
136     private UUID process16BitUUID(int[] data, int index) {
137         // 0000xxxx-0000-1000-8000-00805F9B34FB
138         long high = ((long) data[index] << 32) + ((long) data[index + 1] << 40) + 0x00001000L;
139         return new UUID(high, 0x800000805f9b34fbL);
140     }
141
142     private UUID process32BitUUID(int[] data, int index) {
143         // xxxxxxxx-0000-1000-8000-00805F9B34FB
144         long high = ((long) data[index] << 32) + ((long) data[index + 1] << 40) + ((long) data[index + 2] << 48)
145                 + ((long) data[index + 3] << 56) + 0x00001000L;
146         return new UUID(high, 0x800000805f9b34fbL);
147     }
148
149     private UUID process128BitUUID(int[] data, int index) {
150         long low = (data[index]) + ((long) data[index + 1] << 8) + ((long) data[index + 2] << 16)
151                 + ((long) data[index + 3] << 24) + ((long) data[index + 4] << 32) + ((long) data[index + 5] << 40)
152                 + ((long) data[index + 6] << 48) + ((long) data[index + 7] << 56);
153         long high = (data[index + 8]) + ((long) data[index + 9] << 8) + ((long) data[index + 10] << 16)
154                 + ((long) data[index + 11] << 24) + ((long) data[index + 12] << 32) + ((long) data[index + 13] << 40)
155                 + ((long) data[index + 14] << 48) + ((long) data[index + 15] << 56);
156         return new UUID(high, low);
157     }
158
159     private List<Integer> processUInt16List(int[] data) {
160         List<Integer> intList = new ArrayList<>();
161
162         for (int cnt = 1; cnt < data.length - 1; cnt += 2) {
163             intList.add(Integer.valueOf(data[cnt] + (data[cnt + 1] << 8)));
164         }
165
166         return intList;
167     }
168
169     private List<EirFlags> processFlags(int[] data) {
170         List<EirFlags> flags = new ArrayList<>();
171         int flagBit = 0;
172         for (int cnt = 1; cnt < data.length; cnt++) {
173             for (int bitcnt = 0; bitcnt < 8; bitcnt++) {
174                 if ((data[cnt] & (1 << bitcnt)) != 0) {
175                     flags.add(EirFlags.getEirFlag(flagBit));
176                 }
177                 flagBit++;
178             }
179         }
180
181         return flags;
182     }
183
184     private String processString(int[] data) {
185         StringBuilder builder = new StringBuilder();
186         for (int cnt = 1; cnt < data.length; cnt++) {
187             builder.append((char) data[cnt]);
188         }
189         return builder.toString();
190     }
191
192     private int processUInt8(int[] data) {
193         if (data[1] > 127) {
194             return data[1] - 256;
195         } else {
196             return data[1];
197         }
198     }
199
200     private String processUnknown(int[] data) {
201         StringBuilder builder = new StringBuilder();
202         for (int cnt = 0; cnt < data.length; cnt++) {
203             builder.append(String.format("%02X", data[cnt]));
204         }
205         return builder.toString();
206     }
207
208     public EirDataType getType() {
209         return type;
210     }
211
212     public Object getRecord() {
213         return record;
214     }
215
216     @Override
217     public String toString() {
218         final StringBuilder builder = new StringBuilder();
219         builder.append("EirRecord [type=");
220         builder.append(type);
221         builder.append(", record=");
222         builder.append(record);
223         builder.append(']');
224         return builder.toString();
225     }
226 }