/****************************************************************************** * * Copyright (C) 2015 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ #include <iostream> #include <iomanip> #include <fstream> #include <sstream> #include <string> #include <string.h> // for memcpy #include <vector> #include <resolv.h> #include <zlib.h> extern "C" { #include "btif/include/btif_debug_btsnoop.h" #include "hci/include/bt_hci_bdroid.h" #include "stack/include/bt_types.h" #include "stack/include/hcidefs.h" } // Epoch in microseconds since 01/01/0000. #define BTSNOOP_EPOCH_DELTA 0x00dcddb30f2f8000ULL #define INITIAL_BUFFER_SIZE 131072 #define INFLATE_BUFFER 16384 #define LOG_PREFIX "--- BEGIN:BTSNOOP_LOG_SUMMARY" #define LOG_POSTFIX "--- END:BTSNOOP_LOG_SUMMARY" #define H4_DIRECTION_SENT 0 #define H4_DIRECTION_RECEIVED 1 static uint8_t packetTypeToFlags(const uint8_t type) { switch (type << 8) { case MSG_HC_TO_STACK_HCI_ERR: case MSG_HC_TO_STACK_HCI_ACL: case MSG_HC_TO_STACK_HCI_SCO: case MSG_HC_TO_STACK_HCI_EVT: case MSG_HC_TO_STACK_L2C_SEG_XMIT: return H4_DIRECTION_RECEIVED; case MSG_STACK_TO_HC_HCI_ACL: case MSG_STACK_TO_HC_HCI_SCO: case MSG_STACK_TO_HC_HCI_CMD: return H4_DIRECTION_SENT; default: break; } return 0; } static uint8_t packetTypeToHciType(const uint8_t type) { switch (type << 8 & 0xFF00) { case MSG_STACK_TO_HC_HCI_CMD: return HCIT_TYPE_COMMAND; case MSG_HC_TO_STACK_HCI_EVT: return HCIT_TYPE_EVENT; case MSG_STACK_TO_HC_HCI_ACL: case MSG_HC_TO_STACK_HCI_ACL: return HCIT_TYPE_ACL_DATA; case MSG_STACK_TO_HC_HCI_SCO: case MSG_HC_TO_STACK_HCI_SCO: return HCIT_TYPE_SCO_DATA; default: break; } return 0; } size_t writeBtSnoop(std::ostream &out, std::vector<uint8_t> &in) { if (in.size() < sizeof(btsnooz_preamble_t)) return 0; // Get preamble uint8_t *p = in.data(); btsnooz_preamble_t *preamble = reinterpret_cast<btsnooz_preamble_t*>(p); if (preamble->version != BTSNOOZ_CURRENT_VERSION) return 0; // Write header const uint8_t header[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xea }; out.write(reinterpret_cast<const char*>(header), sizeof(header)); // Calculate first timestamp uint64_t first_ts = preamble->last_timestamp_ms + BTSNOOP_EPOCH_DELTA; size_t left = in.size() - sizeof(btsnooz_preamble_t); p = in.data() + sizeof(btsnooz_preamble_t); while (left > sizeof(btsnooz_header_t)) { btsnooz_header_t *p_hdr = reinterpret_cast<btsnooz_header_t*>(p); p += sizeof(btsnooz_header_t) + (p_hdr->length - 1); left -= sizeof(btsnooz_header_t) + (p_hdr->length - 1); first_ts -= p_hdr->delta_time_ms; } // Process packets size_t packets = 0; left = in.size() - sizeof(btsnooz_preamble_t); p = in.data() + sizeof(btsnooz_preamble_t); while (left > sizeof(btsnooz_header_t)) { btsnooz_header_t *p_hdr = reinterpret_cast<btsnooz_header_t*>(p); p += sizeof(btsnooz_header_t); left -= sizeof(btsnooz_header_t); const uint32_t h_length = htonl(p_hdr->length); out.write(reinterpret_cast<const char*>(&h_length), 4); out.write(reinterpret_cast<const char*>(&h_length), 4); const uint32_t h_flags = htonl(packetTypeToFlags(p_hdr->type)); out.write(reinterpret_cast<const char*>(&h_flags), 4); const uint32_t h_dropped = 0; out.write(reinterpret_cast<const char*>(&h_dropped), 4); first_ts += p_hdr->delta_time_ms; const uint32_t h_time_hi = htonl(first_ts >> 32); const uint32_t h_time_lo = htonl(first_ts & 0xFFFFFFFF); out.write(reinterpret_cast<const char*>(&h_time_hi), 4); out.write(reinterpret_cast<const char*>(&h_time_lo), 4); const uint8_t type = packetTypeToHciType(p_hdr->type); out.write(reinterpret_cast<const char*>(&type), 1); out.write(reinterpret_cast<const char*>(p), p_hdr->length - 1); p += p_hdr->length - 1; left -= p_hdr->length - 1; ++packets; } return packets; } int readLog(std::istream &in, std::vector<char> &buffer) { buffer.reserve(INITIAL_BUFFER_SIZE); std::string line; const std::string log_prefix(LOG_PREFIX); const std::string log_postfix(LOG_POSTFIX); bool in_block = false; while (std::getline(in, line)) { // Ensure line endings aren't wonky... if (!line.empty() && line[line.size() - 1] == '\r') line.erase(line.end() - 1); // Detect block if (!in_block) { if (line.compare(0, log_prefix.length(), log_prefix) == 0) in_block = true; continue; } if (line.compare(0, log_postfix.length(), log_postfix) == 0) break; // Process data buffer.insert(buffer.end(), line.begin(), line.end()); } if (buffer.size() != 0) buffer.push_back(0); return buffer.size(); } int base64Decode(std::vector<char> &buffer) { char *p = buffer.data(); return b64_pton(p, reinterpret_cast<uint8_t*>(p), buffer.size()); } int inflate(std::vector<char> &in, std::vector<uint8_t> &out) { out.reserve(in.size()); uint8_t buffer[INFLATE_BUFFER]; z_stream zs; int ret = inflateInit(&zs); if (Z_OK != ret) return -1; // Copy preamble as-is for (size_t i = 0; i != sizeof(btsnooz_preamble_t); ++i) { out.push_back(in[i]); } // De-compress data zs.avail_in = in.size() - sizeof(btsnooz_preamble_t);; zs.next_in = reinterpret_cast<uint8_t*>(in.data()) + sizeof(btsnooz_preamble_t); do { zs.avail_out = INFLATE_BUFFER; zs.next_out = buffer; ret = inflate(&zs, Z_NO_FLUSH); size_t read = INFLATE_BUFFER - zs.avail_out; uint8_t *p = buffer; while (read--) out.push_back(*p++); } while (zs.avail_out == 0); inflateEnd(&zs); return out.size(); }