Coverage Report

Created: 2021-08-28 18:14

D:\git\skunkworks\herald-for-cpp\herald\src\ble\filter\ble_advert_parser.cpp
Line
Count
Source (jump to first uncovered line)
1
//  Copyright 2020-2021 Herald Project Contributors
2
//  SPDX-License-Identifier: Apache-2.0
3
//
4
5
#include "herald/ble/filter/ble_advert_types.h"
6
#include "herald/ble/filter/ble_advert_parser.h"
7
8
#include <string>
9
#include <vector>
10
11
namespace herald {
12
namespace ble {
13
namespace filter {
14
namespace BLEAdvertParser {
15
16
using namespace herald::datatype;
17
18
std::vector<BLEAdvertSegment>
19
extractSegments(const Data& raw, std::size_t offset) noexcept
20
2
{
21
2
  std::size_t position = offset;
22
2
  std::vector<BLEAdvertSegment> segments;
23
2
  std::uint8_t segmentLength;
24
2
  std::uint8_t segmentType;
25
2
26
8
  while (position < raw.size()) {
27
6
    if ((position + 2) <= raw.size()) {
28
6
      segmentLength = 0;
29
6
      bool ok = raw.uint8(position++, segmentLength);
30
6
      if (ok) {
31
6
        segmentType = 0;
32
6
        ok = raw.uint8(position++, segmentType);
33
6
        if (ok) {
34
6
          // Note: Unsupported types are handled as 'unknown'
35
6
          // check reported length with actual remaining data length
36
6
          if ((position + segmentLength - 1) <= raw.size()) {
37
6
            segments.emplace_back(
38
6
              typeFor(segmentType), 
39
6
              subDataBigEndian(raw, position, segmentLength - 1)
40
6
            );
41
6
            position += segmentLength - 1;
42
6
          } else {
43
0
            // error in data length - advance to end
44
0
            position = raw.size();
45
0
          }
46
6
        }
47
6
      }
48
6
    } else {
49
0
      // invalid segment - advance to end
50
0
      position = raw.size();
51
0
    }
52
6
  }
53
2
54
2
  return segments;
55
2
}
56
57
58
// Parse result extraction functions
59
60
bool
61
extractTxPower(const std::vector<BLEAdvertSegment>& segments, std::uint8_t& into) noexcept
62
0
{
63
0
  // find the txPower code segment in the list
64
0
  for (auto& segment : segments) {
65
0
    if (segment.type == BLEAdvertSegmentType::txPowerLevel && segment.data.size() > 0) {
66
0
      bool ok = segment.data.uint8(0, into);
67
0
      if (ok) {
68
0
        return ok;
69
0
      } // else see if theres a non malformed one...
70
0
    }
71
0
  }
72
0
  return false;
73
0
}
74
75
std::vector<BLEAdvertManufacturerData>
76
extractManufacturerData(const std::vector<BLEAdvertSegment>& segments) noexcept
77
2
{
78
2
  // find the manufacturerData code segment in the list
79
2
  std::vector<BLEAdvertManufacturerData> manufacturerData;
80
6
  for (auto& segment : segments) {
81
6
    if (segment.type == BLEAdvertSegmentType::manufacturerData) {
82
2
      // Ensure that the data area is long enough
83
2
      if (segment.data.size() < 2) {
84
0
        continue; // there may be a valid segment of same type... Happens for manufacturer data
85
0
      }
86
2
      // Create a manufacturer data segment
87
2
      std::uint16_t intValue = 0;
88
2
      bool ok = segment.data.uint16(0, intValue);
89
2
      if (ok) {
90
2
        manufacturerData.emplace_back(
91
2
          intValue,
92
2
          subDataBigEndian(segment.data,2,segment.data.size() - 2)
93
2
        );
94
2
      }
95
2
    }
96
6
  }
97
2
  return manufacturerData;
98
2
}
99
100
std::vector<Data>
101
extractHeraldManufacturerData(const std::vector<BLEAdvertManufacturerData>& manuData) noexcept
102
1
{
103
1
  std::vector<Data> heraldSegments;
104
1
  for (auto& manu : manuData) {
105
1
    if (manu.manufacturer != to_integral(BLEAdvertManufacturers::heraldUnregistered)) {
106
0
      continue;
107
0
    }
108
1
    heraldSegments.push_back(manu.data);
109
1
  }
110
1
  return heraldSegments;
111
1
}
112
113
std::vector<BLEAdvertAppleManufacturerSegment>
114
extractAppleManufacturerSegments(const std::vector<BLEAdvertManufacturerData>& manuData) noexcept
115
1
{
116
1
  std::vector<BLEAdvertAppleManufacturerSegment> appleSegments;
117
1
  std::size_t bytePos;
118
1
  for (auto& manu : manuData) {
119
1
    if (manu.manufacturer != to_integral(BLEAdvertManufacturers::apple)) {
120
0
      continue;
121
0
    }
122
1
    bytePos = 0;
123
2
    while (bytePos < manu.data.size()) {
124
1
      std::uint8_t typeValue = 0;
125
1
      bool ok = manu.data.uint8(bytePos, typeValue);
126
1
      if (ok) {
127
1
        // "01" marks legacy service UUID encoding without length data
128
1
        if (typeValue == 0x01) {
129
0
          std::size_t length = manu.data.size() - bytePos - 1;
130
0
          std::size_t maxLength = (length < manu.data.size() - bytePos - 1 ? length : manu.data.size() - bytePos - 1);
131
0
          appleSegments.emplace_back(
132
0
            typeValue,
133
0
            subDataBigEndian(manu.data, bytePos + 1, maxLength)
134
0
          );
135
0
          bytePos += maxLength + 1;
136
0
        }
137
1
        // Parse according to Type-Length-Data
138
1
        else {
139
1
          std::uint8_t length = 0;
140
1
          bool lok = manu.data.uint8(bytePos + 1, length);
141
1
          if (lok) {
142
1
            std::size_t maxLength = (length < manu.data.size() - bytePos - 2 ? 
length0
: manu.data.size() - bytePos - 2);
143
1
            appleSegments.emplace_back(
144
1
              typeValue, subDataBigEndian(manu.data, bytePos + 2, maxLength)
145
1
            );
146
1
            bytePos += (maxLength + 2);
147
1
          }
148
1
        }
149
1
      }
150
1
    }
151
1
  }
152
1
  return appleSegments;
153
1
}
154
155
// std::vector<BLEAdvertServiceData>
156
// extractServiceUUID128Data(std::vector<BLEAdvertSegment> segments) noexcept
157
// {
158
//   std::vector<BLEAdvertServiceData> serviceData;
159
//   for (BLEAdvertSegment segment : segments) {
160
//     if (segment.type == BLEAdvertSegmentType::serviceUUID128CompleteList) {
161
//       // Ensure that the data area is long enough
162
//       if (segment.data.size() < 16) { // 128 bits == 16 bytes
163
//         continue; // there may be a valid segment of same type... 
164
//       }
165
//       // Create a manufacturer data segment
166
//       if (ok) {
167
//         serviceData.emplace_back(
168
//           subDataLittleEndian(segment.data,0,16),
169
//           subDataBigEndian(segment.data,16,segment.data.size() - 2)
170
//         );
171
//       }
172
//     }
173
//   }
174
//   return serviceData;
175
176
// }
177
178
// Low level utility functions
179
// Exposed in API to allow others to use them
180
181
Data
182
subDataBigEndian(const Data& data, std::size_t offset, std::size_t length) noexcept
183
15
{
184
15
  return data.subdata(offset, length);
185
15
}
186
187
Data 
188
subDataLittleEndian(const Data& data, std::size_t offset, std::size_t length) noexcept
189
6
{
190
6
  Data d;
191
6
  if (offset < 0 || length <= 0) {
192
0
    return d;
193
0
  }
194
6
  // If offset passed as -1 it will be MAX_LONG_LONG by itself, so test on its own first
195
6
  if (offset > data.size()) {
196
1
    return d;
197
1
  }
198
5
  d.appendReversed(data, offset, length);
199
5
  return d;
200
5
}
201
202
}
203
}
204
}
205
}