• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 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 "hal/snoop_logger.h"
18 
19 #include <arpa/inet.h>
20 #include <sys/stat.h>
21 
22 #include <algorithm>
23 #include <bitset>
24 #include <chrono>
25 #include <sstream>
26 
27 #include "common/circular_buffer.h"
28 #include "common/init_flags.h"
29 #include "common/strings.h"
30 #include "os/files.h"
31 #include "os/log.h"
32 #include "os/parameter_provider.h"
33 #include "os/system_properties.h"
34 
35 namespace bluetooth {
36 namespace hal {
37 
38 namespace {
39 
40 // Epoch in microseconds since 01/01/0000.
41 constexpr uint64_t kBtSnoopEpochDelta = 0x00dcddb30f2f8000ULL;
42 
43 constexpr uint32_t kBytesToTest = 0x12345678;
44 constexpr uint8_t kFirstByte = (const uint8_t&)kBytesToTest;
45 constexpr bool isLittleEndian = kFirstByte == 0x78;
46 constexpr bool isBigEndian = kFirstByte == 0x12;
47 static_assert(isLittleEndian || isBigEndian && isLittleEndian != isBigEndian);
48 
49 constexpr uint32_t BTSNOOP_VERSION_NUMBER = isLittleEndian ? 0x01000000 : 1;
50 constexpr uint32_t BTSNOOP_DATALINK_TYPE =
51     isLittleEndian ? 0xea030000 : 0x03ea;  // Datalink Type code for HCI UART (H4) is 1002
htonll(uint64_t ll)52 uint64_t htonll(uint64_t ll) {
53   if constexpr (isLittleEndian) {
54     return static_cast<uint64_t>(htonl(ll & 0xffffffff)) << 32 | htonl(ll >> 32);
55   } else {
56     return ll;
57   }
58 }
59 
60 constexpr SnoopLogger::FileHeaderType kBtSnoopFileHeader = {
61     .identification_pattern = {'b', 't', 's', 'n', 'o', 'o', 'p', 0x00},
62     .version_number = BTSNOOP_VERSION_NUMBER,
63     .datalink_type = BTSNOOP_DATALINK_TYPE};
64 
65 // The number of packets per btsnoop file before we rotate to the next file. As of right now there
66 // are two snoop files that are rotated through. The size can be dynamically configured by setting
67 // the relevant system property
68 constexpr size_t kDefaultBtSnoopMaxPacketsPerFile = 0xffff;
69 
70 // We want to use at most 256 KB memory for btsnooz log
71 constexpr size_t kDefaultBtsnoozMaxMemoryUsageBytes = 256 * 1024;
72 // We restrict the maximum packet size to 150 bytes
73 constexpr size_t kDefaultBtSnoozMaxBytesPerPacket = 150;
74 constexpr size_t kDefaultBtSnoozMaxPayloadBytesPerPacket =
75     kDefaultBtSnoozMaxBytesPerPacket - sizeof(SnoopLogger::PacketHeaderType);
76 // Calculate max number of packets based on max memory usage and max packet size
77 constexpr size_t kDefaultBtSnoozMaxPacketsPerBuffer =
78     kDefaultBtsnoozMaxMemoryUsageBytes / kDefaultBtSnoozMaxBytesPerPacket;
79 
get_btsnoop_log_path(std::string log_dir,bool filtered)80 std::string get_btsnoop_log_path(std::string log_dir, bool filtered) {
81   if (filtered) {
82     log_dir.append(".filtered");
83   }
84   return log_dir;
85 }
86 
get_last_log_path(std::string log_file_path)87 std::string get_last_log_path(std::string log_file_path) {
88   return log_file_path.append(".last");
89 }
90 
delete_btsnoop_files(const std::string & log_path)91 void delete_btsnoop_files(const std::string& log_path) {
92   LOG_INFO("Deleting logs if they exist");
93   if (os::FileExists(log_path)) {
94     if (!os::RemoveFile(log_path)) {
95       LOG_ERROR("Failed to remove main log file at \"%s\"", log_path.c_str());
96     }
97   } else {
98     LOG_INFO("Main log file does not exist at \"%s\"", log_path.c_str());
99   }
100   auto last_log_path = get_last_log_path(log_path);
101   if (os::FileExists(last_log_path)) {
102     if (!os::RemoveFile(last_log_path)) {
103       LOG_ERROR("Failed to remove last log file at \"%s\"", log_path.c_str());
104     }
105   } else {
106     LOG_INFO("Last log file does not exist at \"%s\"", log_path.c_str());
107   }
108 }
109 
get_btsnooz_packet_length_to_write(const HciPacket & packet,SnoopLogger::PacketType type)110 size_t get_btsnooz_packet_length_to_write(const HciPacket& packet, SnoopLogger::PacketType type) {
111   static const size_t kAclHeaderSize = 4;
112   static const size_t kL2capHeaderSize = 4;
113   static const size_t kL2capCidOffset = (kAclHeaderSize + 2);
114   static const uint16_t kL2capSignalingCid = 0x0001;
115 
116   // Maximum amount of ACL data to log.
117   // Enough for an RFCOMM frame up to the frame check;
118   // not enough for a HID report or audio data.
119   static const size_t kMaxBtsnoozAclSize = 14;
120 
121   // Calculate packet length to be included
122   size_t included_length = 0;
123   switch (type) {
124     case SnoopLogger::PacketType::CMD:
125     case SnoopLogger::PacketType::EVT:
126       included_length = packet.size();
127       break;
128 
129     case SnoopLogger::PacketType::ACL: {
130       // Log ACL and L2CAP header by default
131       size_t len_hci_acl = kAclHeaderSize + kL2capHeaderSize;
132       // Check if we have enough data for an L2CAP header
133       if (packet.size() > len_hci_acl) {
134         uint16_t l2cap_cid =
135             static_cast<uint16_t>(packet[kL2capCidOffset]) |
136             static_cast<uint16_t>((static_cast<uint16_t>(packet[kL2capCidOffset + 1]) << static_cast<uint16_t>(8)));
137         if (l2cap_cid == kL2capSignalingCid) {
138           // For the signaling CID, take the full packet.
139           // That way, the PSM setup is captured, allowing decoding of PSMs down
140           // the road.
141           return packet.size();
142         } else {
143           // Otherwise, return as much as we reasonably can
144           len_hci_acl = kMaxBtsnoozAclSize;
145         }
146       }
147       included_length = std::min(len_hci_acl, packet.size());
148       break;
149     }
150 
151     case SnoopLogger::PacketType::ISO:
152     case SnoopLogger::PacketType::SCO:
153     default:
154       // We are not logging SCO and ISO packets in snooz log as they may contain voice data
155       break;
156   }
157   return std::min(included_length, kDefaultBtSnoozMaxPayloadBytesPerPacket);
158 }
159 
160 }  // namespace
161 
162 const std::string SnoopLogger::kBtSnoopLogModeDisabled = "disabled";
163 const std::string SnoopLogger::kBtSnoopLogModeFiltered = "filtered";
164 const std::string SnoopLogger::kBtSnoopLogModeFull = "full";
165 
166 const std::string SnoopLogger::kBtSnoopMaxPacketsPerFileProperty = "persist.bluetooth.btsnoopsize";
167 const std::string SnoopLogger::kIsDebuggableProperty = "ro.debuggable";
168 const std::string SnoopLogger::kBtSnoopLogModeProperty = "persist.bluetooth.btsnooplogmode";
169 const std::string SnoopLogger::kBtSnoopDefaultLogModeProperty = "persist.bluetooth.btsnoopdefaultmode";
170 
SnoopLogger(std::string snoop_log_path,std::string snooz_log_path,size_t max_packets_per_file,const std::string & btsnoop_mode)171 SnoopLogger::SnoopLogger(
172     std::string snoop_log_path,
173     std::string snooz_log_path,
174     size_t max_packets_per_file,
175     const std::string& btsnoop_mode)
176     : snoop_log_path_(std::move(snoop_log_path)),
177       snooz_log_path_(std::move(snooz_log_path)),
178       max_packets_per_file_(max_packets_per_file),
179       btsnooz_buffer_(kDefaultBtSnoozMaxPacketsPerBuffer) {
180   if (false && btsnoop_mode == kBtSnoopLogModeFiltered) {
181     // TODO(b/163733538): implement filtered snoop log in GD, currently filtered == disabled
182     LOG_INFO("Filtered Snoop Logs enabled");
183     is_enabled_ = true;
184     is_filtered_ = true;
185     // delete unfiltered logs
186     delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, false));
187     // delete snooz logs
188     delete_btsnoop_files(snooz_log_path_);
189   } else if (btsnoop_mode == kBtSnoopLogModeFull) {
190     LOG_INFO("Snoop Logs fully enabled");
191     is_enabled_ = true;
192     is_filtered_ = false;
193     // delete filtered logs
194     delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, true));
195     // delete snooz logs
196     delete_btsnoop_files(snooz_log_path_);
197   } else {
198     LOG_INFO("Snoop Logs disabled");
199     is_enabled_ = false;
200     is_filtered_ = false;
201     // delete both filtered and unfiltered logs
202     delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, true));
203     delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, false));
204   }
205   // Add ".filtered" extension if necessary
206   snoop_log_path_ = get_btsnoop_log_path(snoop_log_path_, is_filtered_);
207 }
208 
CloseCurrentSnoopLogFile()209 void SnoopLogger::CloseCurrentSnoopLogFile() {
210   std::lock_guard<std::recursive_mutex> lock(file_mutex_);
211   if (btsnoop_ostream_.is_open()) {
212     btsnoop_ostream_.flush();
213     btsnoop_ostream_.close();
214   }
215   packet_counter_ = 0;
216 }
217 
OpenNextSnoopLogFile()218 void SnoopLogger::OpenNextSnoopLogFile() {
219   std::lock_guard<std::recursive_mutex> lock(file_mutex_);
220   CloseCurrentSnoopLogFile();
221 
222   auto last_file_path = get_last_log_path(snoop_log_path_);
223 
224   if (os::FileExists(snoop_log_path_)) {
225     if (!os::RenameFile(snoop_log_path_, last_file_path)) {
226       LOG_ERROR(
227           "Unabled to rename existing snoop log from \"%s\" to \"%s\"",
228           snoop_log_path_.c_str(),
229           last_file_path.c_str());
230     }
231   } else {
232     LOG_INFO("Previous log file \"%s\" does not exist, skip renaming", snoop_log_path_.c_str());
233   }
234 
235   mode_t prevmask = umask(0);
236   // do not use std::ios::app as we want override the existing file
237   btsnoop_ostream_.open(snoop_log_path_, std::ios::binary | std::ios::out);
238   if (!btsnoop_ostream_.good()) {
239     LOG_ALWAYS_FATAL("Unable to open snoop log at \"%s\", error: \"%s\"", snoop_log_path_.c_str(), strerror(errno));
240   }
241   umask(prevmask);
242   if (!btsnoop_ostream_.write(reinterpret_cast<const char*>(&kBtSnoopFileHeader), sizeof(FileHeaderType))) {
243     LOG_ALWAYS_FATAL("Unable to write file header to \"%s\", error: \"%s\"", snoop_log_path_.c_str(), strerror(errno));
244   }
245   if (!btsnoop_ostream_.flush()) {
246     LOG_ERROR("Failed to flush, error: \"%s\"", strerror(errno));
247   }
248 }
249 
Capture(const HciPacket & packet,Direction direction,PacketType type)250 void SnoopLogger::Capture(const HciPacket& packet, Direction direction, PacketType type) {
251   uint64_t timestamp_us =
252       std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch())
253           .count();
254   std::bitset<32> flags = 0;
255   switch (type) {
256     case PacketType::CMD:
257       flags.set(0, false);
258       flags.set(1, true);
259       break;
260     case PacketType::ACL:
261     case PacketType::SCO:
262       flags.set(0, direction == Direction::INCOMING);
263       flags.set(1, false);
264       break;
265     case PacketType::EVT:
266       flags.set(0, true);
267       flags.set(1, true);
268       break;
269     case PacketType::ISO:
270       flags.set(0, direction == Direction::INCOMING);
271       flags.set(1, true);
272       break;
273   }
274   uint32_t length = packet.size() + /* type byte */ 1;
275   PacketHeaderType header = {.length_original = htonl(length),
276                              .length_captured = htonl(length),
277                              .flags = htonl(static_cast<uint32_t>(flags.to_ulong())),
278                              .dropped_packets = 0,
279                              .timestamp = htonll(timestamp_us + kBtSnoopEpochDelta),
280                              .type = static_cast<uint8_t>(type)};
281   {
282     std::lock_guard<std::recursive_mutex> lock(file_mutex_);
283     if (!is_enabled_) {
284       // btsnoop disabled, log in-memory btsnooz log only
285       std::stringstream ss;
286       size_t included_length = get_btsnooz_packet_length_to_write(packet, type);
287       header.length_captured = htonl(included_length + /* type byte */ 1);
288       if (!ss.write(reinterpret_cast<const char*>(&header), sizeof(PacketHeaderType))) {
289         LOG_ERROR("Failed to write packet header for btsnooz, error: \"%s\"", strerror(errno));
290       }
291       if (!ss.write(reinterpret_cast<const char*>(packet.data()), included_length)) {
292         LOG_ERROR("Failed to write packet payload for btsnooz, error: \"%s\"", strerror(errno));
293       }
294       btsnooz_buffer_.Push(ss.str());
295       return;
296     }
297     packet_counter_++;
298     if (packet_counter_ > max_packets_per_file_) {
299       OpenNextSnoopLogFile();
300     }
301     if (!btsnoop_ostream_.write(reinterpret_cast<const char*>(&header), sizeof(PacketHeaderType))) {
302       LOG_ERROR("Failed to write packet header for btsnoop, error: \"%s\"", strerror(errno));
303     }
304     if (!btsnoop_ostream_.write(reinterpret_cast<const char*>(packet.data()), packet.size())) {
305       LOG_ERROR("Failed to write packet payload for btsnoop, error: \"%s\"", strerror(errno));
306     }
307     // std::ofstream::flush() pushes user data into kernel memory. The data will be written even if this process
308     // crashes. However, data will be lost if there is a kernel panic, which is out of scope of BT snoop log.
309     // NOTE: std::ofstream::write() followed by std::ofstream::flush() has similar effect as UNIX write(fd, data, len)
310     //       as write() syscall dumps data into kernel memory directly
311     if (!btsnoop_ostream_.flush()) {
312       LOG_ERROR("Failed to flush, error: \"%s\"", strerror(errno));
313     }
314   }
315 }
316 
DumpSnoozLogToFile(const std::vector<std::string> & data) const317 void SnoopLogger::DumpSnoozLogToFile(const std::vector<std::string>& data) const {
318   std::lock_guard<std::recursive_mutex> lock(file_mutex_);
319   if (is_enabled_) {
320     LOG_DEBUG("btsnoop log is enabled, skip dumping btsnooz log");
321     return;
322   }
323 
324   auto last_file_path = get_last_log_path(snooz_log_path_);
325 
326   if (os::FileExists(snooz_log_path_)) {
327     if (!os::RenameFile(snooz_log_path_, last_file_path)) {
328       LOG_ERROR(
329           "Unabled to rename existing snooz log from \"%s\" to \"%s\"",
330           snooz_log_path_.c_str(),
331           last_file_path.c_str());
332     }
333   } else {
334     LOG_INFO("Previous log file \"%s\" does not exist, skip renaming", snooz_log_path_.c_str());
335   }
336 
337   mode_t prevmask = umask(0);
338   // do not use std::ios::app as we want override the existing file
339   std::ofstream btsnooz_ostream(snooz_log_path_, std::ios::binary | std::ios::out);
340   if (!btsnooz_ostream.good()) {
341     LOG_ALWAYS_FATAL("Unable to open snoop log at \"%s\", error: \"%s\"", snooz_log_path_.c_str(), strerror(errno));
342   }
343   umask(prevmask);
344   if (!btsnooz_ostream.write(reinterpret_cast<const char*>(&kBtSnoopFileHeader), sizeof(FileHeaderType))) {
345     LOG_ALWAYS_FATAL("Unable to write file header to \"%s\", error: \"%s\"", snooz_log_path_.c_str(), strerror(errno));
346   }
347   for (const auto& packet : data) {
348     if (!btsnooz_ostream.write(packet.data(), packet.size())) {
349       LOG_ERROR("Failed to write packet payload for btsnooz, error: \"%s\"", strerror(errno));
350     }
351   }
352   if (!btsnooz_ostream.flush()) {
353     LOG_ERROR("Failed to flush, error: \"%s\"", strerror(errno));
354   }
355 }
356 
ListDependencies(ModuleList * list)357 void SnoopLogger::ListDependencies(ModuleList* list) {
358   // We have no dependencies
359 }
360 
Start()361 void SnoopLogger::Start() {
362   std::lock_guard<std::recursive_mutex> lock(file_mutex_);
363   if (is_enabled_) {
364     OpenNextSnoopLogFile();
365   }
366 }
367 
Stop()368 void SnoopLogger::Stop() {
369   std::lock_guard<std::recursive_mutex> lock(file_mutex_);
370   LOG_DEBUG("Dumping btsnooz log data to %s", snooz_log_path_.c_str());
371   DumpSnoozLogToFile(btsnooz_buffer_.Drain());
372   LOG_DEBUG("Closing btsnoop log data at %s", snoop_log_path_.c_str());
373   CloseCurrentSnoopLogFile();
374 }
375 
GetDumpsysData(flatbuffers::FlatBufferBuilder * builder) const376 DumpsysDataFinisher SnoopLogger::GetDumpsysData(flatbuffers::FlatBufferBuilder* builder) const {
377   LOG_DEBUG("Dumping btsnooz log data to %s", snooz_log_path_.c_str());
378   DumpSnoozLogToFile(btsnooz_buffer_.Pull());
379   return Module::GetDumpsysData(builder);
380 }
381 
GetMaxPacketsPerFile()382 size_t SnoopLogger::GetMaxPacketsPerFile() {
383   // Allow override max packet per file via system property
384   auto max_packets_per_file = kDefaultBtSnoopMaxPacketsPerFile;
385   {
386     auto max_packets_per_file_prop = os::GetSystemProperty(kBtSnoopMaxPacketsPerFileProperty);
387     if (max_packets_per_file_prop) {
388       auto max_packets_per_file_number = common::Uint64FromString(max_packets_per_file_prop.value());
389       if (max_packets_per_file_number) {
390         max_packets_per_file = max_packets_per_file_number.value();
391       }
392     }
393   }
394   return max_packets_per_file;
395 }
396 
GetBtSnoopMode()397 std::string SnoopLogger::GetBtSnoopMode() {
398   // Default mode is DISABLED on user build.
399   // In userdebug/eng build, it can also be overwritten by modifying the global setting
400   std::string default_mode = kBtSnoopLogModeDisabled;
401   {
402     auto is_debuggable = os::GetSystemProperty(kIsDebuggableProperty);
403     if (is_debuggable.has_value() && common::StringTrim(is_debuggable.value()) == "1") {
404       auto default_mode_property = os::GetSystemProperty(kBtSnoopDefaultLogModeProperty);
405       if (default_mode_property) {
406         default_mode = std::move(default_mode_property.value());
407       }
408     }
409   }
410 
411   // Get the actual mode if exist
412   std::string btsnoop_mode = default_mode;
413   {
414     auto btsnoop_mode_prop = os::GetSystemProperty(kBtSnoopLogModeProperty);
415     if (btsnoop_mode_prop) {
416       btsnoop_mode = std::move(btsnoop_mode_prop.value());
417     }
418   }
419   return btsnoop_mode;
420 }
421 
__anon5855be7f0202() 422 const ModuleFactory SnoopLogger::Factory = ModuleFactory([]() {
423   return new SnoopLogger(
424       os::ParameterProvider::SnoopLogFilePath(),
425       os::ParameterProvider::SnoozLogFilePath(),
426       GetMaxPacketsPerFile(),
427       GetBtSnoopMode());
428 });
429 
430 }  // namespace hal
431 }  // namespace bluetooth
432