xref: /aosp_15_r20/system/chre/host/common/bt_snoop_log_parser.cc (revision 84e339476a462649f82315436d70fd732297a399)
1 /*
2  * Copyright (C) 2023 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 #include "chre_host/bt_snoop_log_parser.h"
18 
19 #include <endian.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <fstream>
23 #include <optional>
24 
25 #include "chre/util/time.h"
26 #include "chre_host/daemon_base.h"
27 #include "chre_host/file_stream.h"
28 #include "chre_host/log.h"
29 #include "chre_host/log_message_parser.h"
30 
31 namespace android {
32 namespace chre {
33 
34 namespace {
35 
36 // Some code in this script are copied from the BT HAL snoop log implementation
37 // in packages/modules/Bluetooth/system/gd/hal. We didn't share the code
38 // directly because currently we only need a subset of the entire snoop log
39 // functionalities.
40 using HciPacket = std::vector<uint8_t>;
41 
42 constexpr char kSnoopLogFilePath[] = "/data/vendor/chre/chre_btsnoop_hci.log";
43 constexpr char kLastSnoopLogFilePath[] =
44     "/data/vendor/chre/chre_btsnoop_hci.log.last";
45 
46 constexpr size_t kDefaultBtSnoopMaxPacketsPerFile = 0xffff;
47 
48 const size_t PACKET_TYPE_LENGTH = 1;
49 
50 constexpr uint32_t kBytesToTest = 0x12345678;
51 constexpr uint8_t kFirstByte = (const uint8_t &)kBytesToTest;
52 constexpr bool isLittleEndian = kFirstByte == 0x78;
53 constexpr uint32_t BTSNOOP_VERSION_NUMBER = isLittleEndian ? 0x01000000 : 1;
54 constexpr uint32_t BTSNOOP_DATALINK_TYPE =
55     isLittleEndian ? 0xea030000
56                    : 0x03ea;  // Datalink Type code for HCI UART (H4) is 1002
57 
58 // Epoch in microseconds since 01/01/0000.
59 constexpr uint64_t kBtSnoopEpochDelta = 0x00dcddb30f2f8000ULL;
60 
61 // The number of bytes in bluetooth snoop log entry in addition to the log
62 // payload. The value indicate the size of the uint8_t direction and packetSize
63 // field.
64 constexpr size_t kBtSnoopLogOffset = 2;
65 
66 struct FileHeaderType {
67   uint8_t identification_pattern[8];
68   uint32_t version_number;
69   uint32_t datalink_type;
70 } __attribute__((packed));
71 
72 static constexpr FileHeaderType kBtSnoopFileHeader = {
73     .identification_pattern = {'b', 't', 's', 'n', 'o', 'o', 'p', 0x00},
74     .version_number = BTSNOOP_VERSION_NUMBER,
75     .datalink_type = BTSNOOP_DATALINK_TYPE};
76 
htonll(uint64_t ll)77 uint64_t htonll(uint64_t ll) {
78   if constexpr (isLittleEndian) {
79     return static_cast<uint64_t>(htonl(ll & 0xffffffff)) << 32 |
80            htonl(ll >> 32);
81   } else {
82     return ll;
83   }
84 }
85 
86 }  // namespace
87 
log(const char * buffer,size_t maxLogMessageLen)88 std::optional<size_t> BtSnoopLogParser::log(const char *buffer,
89                                             size_t maxLogMessageLen) {
90   const auto *message = reinterpret_cast<const BtSnoopLog *>(buffer);
91   size_t logMessageSize = message->packetSize + kBtSnoopLogOffset;
92   if (logMessageSize > maxLogMessageLen) {
93     LOGE(
94         "Dropping bt snoop log due to log message size exceeds the end of log "
95         "buffer");
96     return std::nullopt;
97   } else {
98     capture(message->packet, static_cast<size_t>(message->packetSize),
99             static_cast<BtSnoopDirection>(message->direction));
100   }
101   return logMessageSize;
102 }
103 
capture(const uint8_t * packet,size_t packetSize,BtSnoopDirection direction)104 void BtSnoopLogParser::capture(const uint8_t *packet, size_t packetSize,
105                                BtSnoopDirection direction) {
106   uint64_t timestamp = std::chrono::duration_cast<std::chrono::microseconds>(
107                            std::chrono::system_clock::now().time_since_epoch())
108                            .count();
109   std::bitset<32> flags = 0;
110   PacketType type = PacketType::CMD;
111   switch (direction) {
112     case BtSnoopDirection::OUTGOING_TO_ARBITER:
113       flags.set(0, false);
114       flags.set(1, true);
115       type = PacketType::CMD;
116       break;
117     case BtSnoopDirection::INCOMING_FROM_BT_CONTROLLER:
118       flags.set(0, true);
119       flags.set(1, true);
120       type = PacketType::EVT;
121       break;
122   }
123 
124   uint32_t length = packetSize + /* type byte */ PACKET_TYPE_LENGTH;
125   PacketHeaderType header = {
126       .length_original = htonl(length),
127       .length_captured = htonl(length),
128       .flags = htonl(static_cast<uint32_t>(flags.to_ulong())),
129       .dropped_packets = 0,
130       .timestamp = htonll(timestamp + kBtSnoopEpochDelta),
131       .type = type};
132 
133   if (length != ntohl(header.length_original)) {
134     header.length_captured = htonl(length);
135   }
136 
137   mPacketCounter++;
138   if (mPacketCounter > kDefaultBtSnoopMaxPacketsPerFile) {
139     openNextSnoopLogFile();
140     LOGW("Snoop Log file reached maximum size");
141   }
142   if (ensureSnoopLogFileIsOpen()) {
143     if (!mBtSnoopOstream.write(reinterpret_cast<const char *>(&header),
144                                sizeof(PacketHeaderType))) {
145       LOGE("Failed to write packet header for btsnoop, error: \"%s\"",
146            strerror(errno));
147     }
148     if (!mBtSnoopOstream.write(reinterpret_cast<const char *>(packet),
149                                length - 1)) {
150       LOGE("Failed to write packet payload for btsnoop, error: \"%s\"",
151            strerror(errno));
152     }
153   }
154 }
155 
ensureSnoopLogFileIsOpen()156 bool BtSnoopLogParser::ensureSnoopLogFileIsOpen() {
157   if (mBtSnoopOstream.is_open()) {
158     return true;
159   }
160   return openNextSnoopLogFile();
161 }
162 
openNextSnoopLogFile()163 bool BtSnoopLogParser::openNextSnoopLogFile() {
164   closeSnoopLogFile();
165   if (access(kSnoopLogFilePath, F_OK) == 0 &&
166       std::rename(kSnoopLogFilePath, kLastSnoopLogFilePath) != 0) {
167     LOGE("Unable to rename existing snoop log, error: \"%s\"", strerror(errno));
168   }
169 
170   bool success = false;
171   mBtSnoopOstream.open(kSnoopLogFilePath, std::ios::binary | std::ios::out);
172   mBtSnoopOstream.setf(std::ios::unitbuf);
173   if (mBtSnoopOstream.fail()) {
174     LOGE("Fail to create snoop log file, error: \"%s\"", strerror(errno));
175   } else if (!mBtSnoopOstream.write(
176                  reinterpret_cast<const char *>(&kBtSnoopFileHeader),
177                  sizeof(FileHeaderType))) {
178     LOGE("Unable to write file header to \"%s\", error: \"%s\"",
179          kSnoopLogFilePath, strerror(errno));
180   } else {
181     success = true;
182   }
183   return success;
184 }
185 
closeSnoopLogFile()186 void BtSnoopLogParser::closeSnoopLogFile() {
187   if (mBtSnoopOstream.is_open()) {
188     mBtSnoopOstream.close();
189   }
190   mPacketCounter = 0;
191 }
192 
193 }  // namespace chre
194 }  // namespace android
195