unplugged-system/packages/modules/Bluetooth/system/gd/hal/snoop_logger.cc

1476 lines
51 KiB
C++
Raw Normal View History

/*
* Copyright 2019 The Android Open Source Project
*
* 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 "hal/snoop_logger.h"
#include <arpa/inet.h>
#include <sys/stat.h>
#include <algorithm>
#include <bitset>
#include <chrono>
#include <sstream>
#include "common/circular_buffer.h"
#include "common/init_flags.h"
#include "common/strings.h"
#include "hal/snoop_logger_common.h"
#include "hal/syscall_wrapper_impl.h"
#include "os/fake_timer/fake_timerfd.h"
#include "os/files.h"
#include "os/log.h"
#include "os/parameter_provider.h"
#include "os/system_properties.h"
namespace bluetooth {
#ifdef USE_FAKE_TIMERS
using os::fake_timer::fake_timerfd_get_clock;
#endif
namespace hal {
// Adds L2CAP channel to acceptlist.
void FilterTracker::AddL2capCid(uint16_t local_cid, uint16_t remote_cid) {
l2c_local_cid.insert(local_cid);
l2c_remote_cid.insert(remote_cid);
}
// Sets L2CAP channel that RFCOMM uses.
void FilterTracker::SetRfcommCid(uint16_t local_cid, uint16_t remote_cid) {
rfcomm_local_cid = local_cid;
rfcomm_remote_cid = remote_cid;
}
// Remove L2CAP channel from acceptlist.
void FilterTracker::RemoveL2capCid(uint16_t local_cid, uint16_t remote_cid) {
if (rfcomm_local_cid == local_cid) {
rfcomm_channels.clear();
rfcomm_channels.insert(0);
rfcomm_local_cid = 0;
rfcomm_remote_cid = 0;
}
l2c_local_cid.erase(local_cid);
l2c_remote_cid.erase(remote_cid);
}
void FilterTracker::AddRfcommDlci(uint8_t channel) {
rfcomm_channels.insert(channel);
}
bool FilterTracker::IsAcceptlistedL2cap(bool local, uint16_t cid) {
const auto& set = local ? l2c_local_cid : l2c_remote_cid;
return (set.find(cid) != set.end());
}
bool FilterTracker::IsRfcommChannel(bool local, uint16_t cid) {
const auto& channel = local ? rfcomm_local_cid : rfcomm_remote_cid;
return cid == channel;
}
bool FilterTracker::IsAcceptlistedDlci(uint8_t dlci) {
return rfcomm_channels.find(dlci) != rfcomm_channels.end();
}
void ProfilesFilter::SetupProfilesFilter(bool pbap_filtered, bool map_filtered) {
if (setup_done_flag) {
return;
}
setup_done_flag = true;
LOG_DEBUG("SetupProfilesFilter: pbap=%d, map=%d", pbap_filtered, map_filtered);
for (int i = 0; i < FILTER_PROFILE_MAX; i++) {
profiles[i].type = (profile_type_t)i;
profiles[i].enabled = false;
profiles[i].rfcomm_opened = false;
profiles[i].l2cap_opened = false;
}
if (pbap_filtered) {
profiles[FILTER_PROFILE_PBAP].enabled = profiles[FILTER_PROFILE_HFP_HS].enabled =
profiles[FILTER_PROFILE_HFP_HF].enabled = true;
}
if (map_filtered) {
profiles[FILTER_PROFILE_MAP].enabled = true;
}
ch_rfc_l = ch_rfc_r = ch_last = 0;
PrintProfilesConfig();
}
bool ProfilesFilter::IsHfpProfile(bool local, uint16_t cid, uint8_t dlci) {
profile_type_t profile = DlciToProfile(local, cid, dlci);
return profile == FILTER_PROFILE_HFP_HS || profile == FILTER_PROFILE_HFP_HF;
}
bool ProfilesFilter::IsL2capFlowExt(bool local, uint16_t cid) {
profile_type_t profile = CidToProfile(local, cid);
if (profile >= 0) return profiles[profile].flow_ext_l2cap;
return false;
}
bool ProfilesFilter::IsRfcommFlowExt(bool local, uint16_t cid, uint8_t dlci) {
profile_type_t profile = DlciToProfile(local, cid, dlci);
if (profile >= 0) current_profile = profile;
return profiles[profile].flow_ext_rfcomm;
}
profile_type_t ProfilesFilter::CidToProfile(bool local, uint16_t cid) {
uint16_t ch;
for (int i = 0; i < FILTER_PROFILE_MAX; i++) {
if (profiles[i].enabled && profiles[i].l2cap_opened) {
ch = local ? profiles[i].lcid : profiles[i].rcid;
if (ch == cid) {
return (profile_type_t)i;
}
}
}
return FILTER_PROFILE_NONE;
}
profile_type_t ProfilesFilter::DlciToProfile(bool local, uint16_t cid, uint8_t dlci) {
if (!IsRfcommChannel(local, cid)) return FILTER_PROFILE_NONE;
for (int i = 0; i < FILTER_PROFILE_MAX; i++) {
if (profiles[i].enabled && profiles[i].l2cap_opened && profiles[i].rfcomm_opened &&
profiles[i].scn == (dlci >> 1)) {
return (profile_type_t)i;
}
}
return FILTER_PROFILE_NONE;
}
void ProfilesFilter::ProfileL2capOpen(
profile_type_t profile, uint16_t lcid, uint16_t rcid, uint16_t psm, bool flow_ext) {
if (profiles[profile].l2cap_opened == true) {
LOG_DEBUG("l2cap for %d was already opened. Override it", profile);
}
LOG_DEBUG(
"lcid:=%d, rcid=%d, psm=%d, flow_ext=%d, filter profile=%s",
lcid,
rcid,
psm,
flow_ext,
ProfilesFilter::ProfileToString(profile).c_str());
profiles[profile].lcid = lcid;
profiles[profile].rcid = rcid;
profiles[profile].psm = psm;
profiles[profile].flow_ext_l2cap = flow_ext;
profiles[profile].l2cap_opened = true;
PrintProfilesConfig();
}
void ProfilesFilter::ProfileL2capClose(profile_type_t profile) {
if (profile < 0 || profile >= FILTER_PROFILE_MAX) return;
profiles[profile].l2cap_opened = false;
}
void ProfilesFilter::ProfileRfcommOpen(
profile_type_t profile, uint16_t lcid, uint8_t dlci, uint16_t uuid, bool flow_ext) {
if (profiles[profile].rfcomm_opened == true) {
LOG_DEBUG("rfcomm for %d was already opened. Override it", profile);
}
LOG_DEBUG(
"lcid:=%d, dlci=%d, uuid=%d, flow_ext=%d, filter profile=%s",
lcid,
dlci,
uuid,
flow_ext,
ProfilesFilter::ProfileToString(profile).c_str());
profiles[profile].rfcomm_uuid = uuid;
profiles[profile].scn = (dlci >> 1);
profiles[profile].flow_ext_rfcomm = flow_ext;
profiles[profile].l2cap_opened = true;
profiles[profile].rfcomm_opened = true;
PrintProfilesConfig();
}
void ProfilesFilter::ProfileRfcommClose(profile_type_t profile) {
if (profile < 0 || profile >= FILTER_PROFILE_MAX) return;
profiles[profile].rfcomm_opened = false;
}
bool ProfilesFilter::IsRfcommChannel(bool local, uint16_t cid) {
uint16_t channel = local ? ch_rfc_l : ch_rfc_r;
return cid == channel;
}
void ProfilesFilter::PrintProfilesConfig() {
for (int i = 0; i < FILTER_PROFILE_MAX; i++) {
if (profiles[i].enabled) {
LOG_DEBUG(
"\ntype: %s \
\nenabled: %d, l2cap_opened: %d, rfcomm_opened: %d\
\nflow_ext_l2cap: %d, flow_ext_rfcomm: %d\
\nlcid: %d, rcid: %d, rfcomm_uuid: %d, psm: %d\
\nscn: %d \n",
ProfilesFilter::ProfileToString(profiles[i].type).c_str(),
profiles[i].enabled,
profiles[i].l2cap_opened,
profiles[i].rfcomm_opened,
profiles[i].flow_ext_l2cap,
profiles[i].flow_ext_rfcomm,
profiles[i].lcid,
profiles[i].rcid,
profiles[i].rfcomm_uuid,
profiles[i].psm,
profiles[i].psm);
}
}
}
namespace {
// Epoch in microseconds since 01/01/0000.
constexpr uint64_t kBtSnoopEpochDelta = 0x00dcddb30f2f8000ULL;
// Qualcomm debug logs handle
constexpr uint16_t kQualcommDebugLogHandle = 0xedc;
// Number of bytes into a packet where you can find the value for a channel.
constexpr size_t ACL_CHANNEL_OFFSET = 0;
constexpr size_t ACL_LENGTH_OFFSET = 2;
constexpr size_t L2CAP_PDU_LENGTH_OFFSET = 4;
constexpr size_t L2CAP_CHANNEL_OFFSET = 6;
constexpr size_t L2CAP_CONTROL_OFFSET = 8;
constexpr size_t RFCOMM_CHANNEL_OFFSET = 8;
constexpr size_t RFCOMM_EVENT_OFFSET = 9;
// RFCOMM filtering consts
constexpr uint8_t RFCOMM_SABME = 0x2F; // RFCOMM: Start Asynchronous Balanced Mode (startup cmd)
constexpr uint8_t RFCOMM_UA = 0x63; // RFCOMM: Unnumbered Acknowledgement (rsp when connected)
constexpr uint8_t RFCOMM_UIH = 0xEF; // RFCOMM: Unnumbered Information with Header check
constexpr uint8_t START_PACKET_BOUNDARY = 0x02;
constexpr uint8_t CONTINUATION_PACKET_BOUNDARY = 0x01;
constexpr uint16_t HANDLE_MASK = 0x0FFF;
auto GetBoundaryFlag = [](auto handle) { return (((handle) >> 12) & 0x0003); };
// ProfilesFilter consts
constexpr size_t ACL_HEADER_LENGTH = 4;
constexpr size_t BASIC_L2CAP_HEADER_LENGTH = 4;
constexpr uint8_t EXTRA_BUF_SIZE = 0x40;
constexpr uint16_t DEFAULT_PACKET_SIZE = 0x800;
constexpr uint8_t PROFILE_SCN_PBAP = 19;
constexpr uint8_t PROFILE_SCN_MAP = 26;
constexpr uint16_t PROFILE_PSM_PBAP = 0x1025;
constexpr uint16_t PROFILE_PSM_MAP = 0x1029;
constexpr uint16_t PROFILE_PSM_RFCOMM = 0x0003;
constexpr uint16_t PROFILE_UUID_PBAP = 0x112f;
constexpr uint16_t PROFILE_UUID_MAP = 0x1132;
constexpr uint16_t PROFILE_UUID_HFP_HS = 0x1112;
constexpr uint16_t PROFILE_UUID_HFP_HF = 0x111f;
uint64_t htonll(uint64_t ll) {
if constexpr (isLittleEndian) {
return static_cast<uint64_t>(htonl(ll & 0xffffffff)) << 32 | htonl(ll >> 32);
} else {
return ll;
}
}
// The number of packets per btsnoop file before we rotate to the next file. As of right now there
// are two snoop files that are rotated through. The size can be dynamically configured by setting
// the relevant system property
constexpr size_t kDefaultBtSnoopMaxPacketsPerFile = 0xffff;
// We restrict the maximum packet size to 150 bytes
constexpr size_t kDefaultBtSnoozMaxBytesPerPacket = 150;
constexpr size_t kDefaultBtSnoozMaxPayloadBytesPerPacket =
kDefaultBtSnoozMaxBytesPerPacket - sizeof(SnoopLogger::PacketHeaderType);
using namespace std::chrono_literals;
constexpr std::chrono::hours kBtSnoozLogLifeTime = 12h;
constexpr std::chrono::hours kBtSnoozLogDeleteRepeatingAlarmInterval = 1h;
std::mutex filter_tracker_list_mutex;
std::unordered_map<uint16_t, FilterTracker> filter_tracker_list;
std::unordered_map<uint16_t, uint16_t> local_cid_to_acl;
std::mutex a2dpMediaChannels_mutex;
std::vector<SnoopLogger::A2dpMediaChannel> a2dpMediaChannels;
std::mutex snoop_log_filters_mutex;
std::mutex profiles_filter_mutex;
std::unordered_map<int16_t, ProfilesFilter> profiles_filter_table;
constexpr const char* payload_fill_magic = "PROHIBITED";
constexpr const char* cpbr_pattern = "\x0d\x0a+CPBR:";
constexpr const char* clcc_pattern = "\x0d\x0a+CLCC:";
const uint32_t magic_pat_len = strlen(payload_fill_magic);
const uint32_t cpbr_pat_len = strlen(cpbr_pattern);
const uint32_t clcc_pat_len = strlen(clcc_pattern);
std::string get_btsnoop_log_path(std::string log_dir, bool filtered) {
if (filtered) {
log_dir.append(".filtered");
}
return log_dir;
}
std::string get_last_log_path(std::string log_file_path) {
return log_file_path.append(".last");
}
void delete_btsnoop_files(const std::string& log_path) {
LOG_INFO("Deleting logs if they exist");
if (os::FileExists(log_path)) {
if (!os::RemoveFile(log_path)) {
LOG_ERROR("Failed to remove main log file at \"%s\"", log_path.c_str());
}
} else {
LOG_INFO("Main log file does not exist at \"%s\"", log_path.c_str());
}
auto last_log_path = get_last_log_path(log_path);
if (os::FileExists(last_log_path)) {
if (!os::RemoveFile(last_log_path)) {
LOG_ERROR("Failed to remove last log file at \"%s\"", log_path.c_str());
}
} else {
LOG_INFO("Last log file does not exist at \"%s\"", log_path.c_str());
}
}
void delete_old_btsnooz_files(const std::string& log_path, const std::chrono::milliseconds log_life_time) {
auto opt_created_ts = os::FileCreatedTime(log_path);
if (!opt_created_ts) return;
#ifdef USE_FAKE_TIMERS
auto diff = fake_timerfd_get_clock() - file_creation_time;
uint64_t log_lifetime = log_life_time.count();
if (diff >= log_lifetime) {
#else
using namespace std::chrono;
auto created_tp = opt_created_ts.value();
auto current_tp = std::chrono::system_clock::now();
auto diff = duration_cast<milliseconds>(current_tp - created_tp);
if (diff >= log_life_time) {
#endif
delete_btsnoop_files(log_path);
}
}
size_t get_btsnooz_packet_length_to_write(
const HciPacket& packet, SnoopLogger::PacketType type, bool qualcomm_debug_log_enabled) {
static const size_t kAclHeaderSize = 4;
static const size_t kL2capHeaderSize = 4;
static const size_t kL2capCidOffset = (kAclHeaderSize + 2);
static const uint16_t kL2capSignalingCid = 0x0001;
static const size_t kHciAclHandleOffset = 0;
// Maximum amount of ACL data to log.
// Enough for an RFCOMM frame up to the frame check;
// not enough for a HID report or audio data.
static const size_t kMaxBtsnoozAclSize = 14;
// Calculate packet length to be included
size_t included_length = 0;
switch (type) {
case SnoopLogger::PacketType::CMD:
case SnoopLogger::PacketType::EVT:
included_length = packet.size();
break;
case SnoopLogger::PacketType::ACL: {
// Log ACL and L2CAP header by default
size_t len_hci_acl = kAclHeaderSize + kL2capHeaderSize;
// Check if we have enough data for an L2CAP header
if (packet.size() > len_hci_acl) {
uint16_t l2cap_cid =
static_cast<uint16_t>(packet[kL2capCidOffset]) |
static_cast<uint16_t>((static_cast<uint16_t>(packet[kL2capCidOffset + 1]) << static_cast<uint16_t>(8)));
uint16_t hci_acl_packet_handle =
static_cast<uint16_t>(packet[kHciAclHandleOffset]) |
static_cast<uint16_t>((static_cast<uint16_t>(packet[kHciAclHandleOffset + 1]) << static_cast<uint16_t>(8)));
hci_acl_packet_handle &= 0x0fff;
if (l2cap_cid == kL2capSignalingCid) {
// For the signaling CID, take the full packet.
// That way, the PSM setup is captured, allowing decoding of PSMs down
// the road.
return packet.size();
} else if (qualcomm_debug_log_enabled && hci_acl_packet_handle == kQualcommDebugLogHandle) {
return packet.size();
} else {
// Otherwise, return as much as we reasonably can
len_hci_acl = kMaxBtsnoozAclSize;
}
}
included_length = std::min(len_hci_acl, packet.size());
break;
}
case SnoopLogger::PacketType::ISO:
case SnoopLogger::PacketType::SCO:
default:
// We are not logging SCO and ISO packets in snooz log as they may contain voice data
break;
}
return std::min(included_length, kDefaultBtSnoozMaxPayloadBytesPerPacket);
}
} // namespace
// system properties
const std::string SnoopLogger::kBtSnoopMaxPacketsPerFileProperty = "persist.bluetooth.btsnoopsize";
const std::string SnoopLogger::kIsDebuggableProperty = "ro.debuggable";
const std::string SnoopLogger::kBtSnoopLogModeProperty = "persist.bluetooth.btsnooplogmode";
const std::string SnoopLogger::kBtSnoopDefaultLogModeProperty = "persist.bluetooth.btsnoopdefaultmode";
const std::string SnoopLogger::kBtSnoopLogPersists = "persist.bluetooth.btsnooplogpersists";
// Truncates ACL packets (non-fragment) to fixed (MAX_HCI_ACL_LEN) number of bytes
const std::string SnoopLogger::kBtSnoopLogFilterHeadersProperty =
"persist.bluetooth.snooplogfilter.headers.enabled";
// Discards A2DP media packets (non-split mode)
const std::string SnoopLogger::kBtSnoopLogFilterProfileA2dpProperty =
"persist.bluetooth.snooplogfilter.profiles.a2dp.enabled";
// Filters MAP packets based on the filter mode
const std::string SnoopLogger::kBtSnoopLogFilterProfileMapModeProperty =
"persist.bluetooth.snooplogfilter.profiles.map";
// Filters PBAP and HFP packets (CPBR, CLCC) based on the filter mode
const std::string SnoopLogger::kBtSnoopLogFilterProfilePbapModeProperty =
"persist.bluetooth.snooplogfilter.profiles.pbap";
// Truncates RFCOMM UIH packet to fixed (L2CAP_HEADER_SIZE) number of bytes
const std::string SnoopLogger::kBtSnoopLogFilterProfileRfcommProperty =
"persist.bluetooth.snooplogfilter.profiles.rfcomm.enabled";
const std::string SnoopLogger::kSoCManufacturerProperty = "ro.soc.manufacturer";
// persist.bluetooth.btsnooplogmode
const std::string SnoopLogger::kBtSnoopLogModeDisabled = "disabled";
const std::string SnoopLogger::kBtSnoopLogModeFiltered = "filtered";
const std::string SnoopLogger::kBtSnoopLogModeFull = "full";
// ro.soc.manufacturer
const std::string SnoopLogger::kSoCManufacturerQualcomm = "Qualcomm";
// PBAP, MAP and HFP packets filter mode - discard whole packet
const std::string SnoopLogger::kBtSnoopLogFilterProfileModeFullfillter = "fullfilter";
// PBAP, MAP and HFP packets filter mode - truncate to fixed length
const std::string SnoopLogger::kBtSnoopLogFilterProfileModeHeader = "header";
// PBAP, MAP and HFP packets filter mode - fill with a magic string, such as: "PROHIBITED"
const std::string SnoopLogger::kBtSnoopLogFilterProfileModeMagic = "magic";
// PBAP, MAP and HFP packets filter mode - disabled
const std::string SnoopLogger::kBtSnoopLogFilterProfileModeDisabled = "disabled";
std::string SnoopLogger::btsnoop_mode_;
// Consts accessible in unit tests
const size_t SnoopLogger::PACKET_TYPE_LENGTH = 1;
const size_t SnoopLogger::MAX_HCI_ACL_LEN = 14;
const uint32_t SnoopLogger::L2CAP_HEADER_SIZE = 8;
SnoopLogger::SnoopLogger(
std::string snoop_log_path,
std::string snooz_log_path,
size_t max_packets_per_file,
size_t max_packets_per_buffer,
const std::string& btsnoop_mode,
bool qualcomm_debug_log_enabled,
const std::chrono::milliseconds snooz_log_life_time,
const std::chrono::milliseconds snooz_log_delete_alarm_interval,
bool snoop_log_persists)
: snoop_log_path_(std::move(snoop_log_path)),
snooz_log_path_(std::move(snooz_log_path)),
max_packets_per_file_(max_packets_per_file),
btsnooz_buffer_(max_packets_per_buffer),
qualcomm_debug_log_enabled_(qualcomm_debug_log_enabled),
snooz_log_life_time_(snooz_log_life_time),
snooz_log_delete_alarm_interval_(snooz_log_delete_alarm_interval),
snoop_log_persists(snoop_log_persists) {
btsnoop_mode_ = btsnoop_mode;
if (btsnoop_mode_ == kBtSnoopLogModeFiltered &&
!bluetooth::common::InitFlags::IsSnoopLoggerFilteringEnabled()) {
btsnoop_mode_ = kBtSnoopLogModeDisabled;
}
if (btsnoop_mode_ == kBtSnoopLogModeFiltered) {
LOG_INFO("Snoop Logs filtered mode enabled");
EnableFilters();
// delete unfiltered logs
delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, false));
// delete snooz logs
delete_btsnoop_files(snooz_log_path_);
} else if (btsnoop_mode_ == kBtSnoopLogModeFull) {
LOG_INFO("Snoop Logs full mode enabled");
if (!snoop_log_persists) {
// delete filtered logs
delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, true));
// delete snooz logs
delete_btsnoop_files(snooz_log_path_);
}
} else {
LOG_INFO("Snoop Logs disabled");
// delete both filtered and unfiltered logs
delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, true));
delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, false));
}
snoop_logger_socket_thread_ = nullptr;
socket_ = nullptr;
// Add ".filtered" extension if necessary
snoop_log_path_ = get_btsnoop_log_path(snoop_log_path_, btsnoop_mode_ == kBtSnoopLogModeFiltered);
}
void SnoopLogger::CloseCurrentSnoopLogFile() {
std::lock_guard<std::recursive_mutex> lock(file_mutex_);
if (btsnoop_ostream_.is_open()) {
btsnoop_ostream_.flush();
btsnoop_ostream_.close();
}
packet_counter_ = 0;
}
void SnoopLogger::OpenNextSnoopLogFile() {
std::lock_guard<std::recursive_mutex> lock(file_mutex_);
CloseCurrentSnoopLogFile();
auto last_file_path = get_last_log_path(snoop_log_path_);
if (os::FileExists(snoop_log_path_)) {
if (!os::RenameFile(snoop_log_path_, last_file_path)) {
LOG_ERROR(
"Unabled to rename existing snoop log from \"%s\" to \"%s\"",
snoop_log_path_.c_str(),
last_file_path.c_str());
}
} else {
LOG_INFO("Previous log file \"%s\" does not exist, skip renaming", snoop_log_path_.c_str());
}
mode_t prevmask = umask(0);
// do not use std::ios::app as we want override the existing file
btsnoop_ostream_.open(snoop_log_path_, std::ios::binary | std::ios::out);
#ifdef USE_FAKE_TIMERS
file_creation_time = fake_timerfd_get_clock();
#endif
if (!btsnoop_ostream_.good()) {
LOG_ALWAYS_FATAL("Unable to open snoop log at \"%s\", error: \"%s\"", snoop_log_path_.c_str(), strerror(errno));
}
umask(prevmask);
if (!btsnoop_ostream_.write(
reinterpret_cast<const char*>(&SnoopLoggerCommon::kBtSnoopFileHeader),
sizeof(SnoopLoggerCommon::FileHeaderType))) {
LOG_ALWAYS_FATAL("Unable to write file header to \"%s\", error: \"%s\"", snoop_log_path_.c_str(), strerror(errno));
}
if (!btsnoop_ostream_.flush()) {
LOG_ERROR("Failed to flush, error: \"%s\"", strerror(errno));
}
}
void SnoopLogger::EnableFilters() {
if (btsnoop_mode_ != kBtSnoopLogModeFiltered) {
return;
}
std::lock_guard<std::mutex> lock(snoop_log_filters_mutex);
for (auto itr = kBtSnoopLogFilterState.begin(); itr != kBtSnoopLogFilterState.end(); itr++) {
auto filter_enabled_property = os::GetSystemProperty(itr->first);
if (filter_enabled_property) {
itr->second = filter_enabled_property.value() == "true";
}
LOG_INFO("%s: %d", itr->first.c_str(), itr->second);
}
for (auto itr = kBtSnoopLogFilterMode.begin(); itr != kBtSnoopLogFilterMode.end(); itr++) {
auto filter_mode_property = os::GetSystemProperty(itr->first);
if (filter_mode_property) {
itr->second = filter_mode_property.value();
} else {
itr->second = SnoopLogger::kBtSnoopLogFilterProfileModeDisabled;
}
LOG_INFO("%s: %s", itr->first.c_str(), itr->second.c_str());
}
}
void SnoopLogger::DisableFilters() {
std::lock_guard<std::mutex> lock(snoop_log_filters_mutex);
for (auto itr = kBtSnoopLogFilterState.begin(); itr != kBtSnoopLogFilterState.end(); itr++) {
itr->second = false;
LOG_INFO("%s, %d", itr->first.c_str(), itr->second);
}
for (auto itr = kBtSnoopLogFilterMode.begin(); itr != kBtSnoopLogFilterMode.end(); itr++) {
itr->second = SnoopLogger::kBtSnoopLogFilterProfileModeDisabled;
LOG_INFO("%s, %s", itr->first.c_str(), itr->second.c_str());
}
}
bool SnoopLogger::IsFilterEnabled(std::string filter_name) {
std::lock_guard<std::mutex> lock(snoop_log_filters_mutex);
for (auto itr = kBtSnoopLogFilterState.begin(); itr != kBtSnoopLogFilterState.end(); itr++) {
if (filter_name == itr->first) {
return itr->second == true;
}
}
for (auto itr = kBtSnoopLogFilterMode.begin(); itr != kBtSnoopLogFilterMode.end(); itr++) {
if (filter_name == itr->first) {
return itr->second != SnoopLogger::kBtSnoopLogFilterProfileModeDisabled;
}
}
return false;
}
bool SnoopLogger::ShouldFilterLog(bool is_received, uint8_t* packet) {
uint16_t conn_handle =
((((uint16_t)packet[ACL_CHANNEL_OFFSET + 1]) << 8) + packet[ACL_CHANNEL_OFFSET]) & 0x0fff;
std::lock_guard<std::mutex> lock(filter_tracker_list_mutex);
auto& filters = filter_tracker_list[conn_handle];
uint16_t cid = (packet[L2CAP_CHANNEL_OFFSET + 1] << 8) + packet[L2CAP_CHANNEL_OFFSET];
if (filters.IsRfcommChannel(is_received, cid)) {
uint8_t rfcomm_event = packet[RFCOMM_EVENT_OFFSET] & 0b11101111;
if (rfcomm_event == RFCOMM_SABME || rfcomm_event == RFCOMM_UA) {
return false;
}
uint8_t rfcomm_dlci = packet[RFCOMM_CHANNEL_OFFSET] >> 2;
if (!filters.IsAcceptlistedDlci(rfcomm_dlci)) {
return true;
}
} else if (!filters.IsAcceptlistedL2cap(is_received, cid)) {
return true;
}
return false;
}
void SnoopLogger::CalculateAclPacketLength(uint32_t& length, uint8_t* packet, bool is_received) {
uint32_t def_len =
((((uint16_t)packet[ACL_LENGTH_OFFSET + 1]) << 8) + packet[ACL_LENGTH_OFFSET]) +
ACL_HEADER_LENGTH + PACKET_TYPE_LENGTH;
constexpr uint16_t L2CAP_SIGNALING_CID = 0x0001;
if (length == 0) {
return;
}
uint16_t handle =
((((uint16_t)packet[ACL_CHANNEL_OFFSET + 1]) << 8) + packet[ACL_CHANNEL_OFFSET]);
uint8_t boundary_flag = GetBoundaryFlag(handle);
handle = handle & HANDLE_MASK;
if (boundary_flag == START_PACKET_BOUNDARY) {
uint16_t l2cap_cid = packet[L2CAP_CHANNEL_OFFSET] | (packet[L2CAP_CHANNEL_OFFSET + 1] << 8);
if (l2cap_cid == L2CAP_SIGNALING_CID || handle == kQualcommDebugLogHandle) {
length = def_len;
} else {
if (def_len < MAX_HCI_ACL_LEN) {
length = def_len;
} else {
// Otherwise, return as much as we reasonably can
length = MAX_HCI_ACL_LEN;
}
}
}
}
uint32_t SnoopLogger::PayloadStrip(
profile_type_t current_profile, uint8_t* packet, uint32_t hdr_len, uint32_t pl_len) {
uint32_t len = 0;
std::string profile_filter_mode = "";
LOG_DEBUG(
"current_profile=%s, hdr len=%d, total len=%d",
ProfilesFilter::ProfileToString(current_profile).c_str(),
hdr_len,
pl_len);
std::lock_guard<std::mutex> lock(snoop_log_filters_mutex);
switch (current_profile) {
case FILTER_PROFILE_PBAP:
case FILTER_PROFILE_HFP_HF:
case FILTER_PROFILE_HFP_HS:
profile_filter_mode =
kBtSnoopLogFilterMode[SnoopLogger::kBtSnoopLogFilterProfilePbapModeProperty];
break;
case FILTER_PROFILE_MAP:
profile_filter_mode =
kBtSnoopLogFilterMode[SnoopLogger::kBtSnoopLogFilterProfileMapModeProperty];
break;
default:
profile_filter_mode = kBtSnoopLogFilterProfileModeDisabled;
}
if (profile_filter_mode == SnoopLogger::kBtSnoopLogFilterProfileModeFullfillter) {
return 0;
} else if (profile_filter_mode == SnoopLogger::kBtSnoopLogFilterProfileModeHeader) {
len = hdr_len;
packet[ACL_LENGTH_OFFSET] = static_cast<uint8_t>(hdr_len - BASIC_L2CAP_HEADER_LENGTH);
packet[ACL_LENGTH_OFFSET + 1] =
static_cast<uint8_t>((hdr_len - BASIC_L2CAP_HEADER_LENGTH) >> 8);
packet[L2CAP_PDU_LENGTH_OFFSET] =
static_cast<uint8_t>(hdr_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH));
packet[L2CAP_PDU_LENGTH_OFFSET + 1] =
static_cast<uint8_t>((hdr_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH)) >> 8);
} else if (profile_filter_mode == SnoopLogger::kBtSnoopLogFilterProfileModeMagic) {
strcpy(reinterpret_cast<char*>(&packet[hdr_len]), payload_fill_magic);
packet[ACL_LENGTH_OFFSET] =
static_cast<uint8_t>(hdr_len + magic_pat_len - BASIC_L2CAP_HEADER_LENGTH);
packet[ACL_LENGTH_OFFSET + 1] =
static_cast<uint8_t>((hdr_len + magic_pat_len - BASIC_L2CAP_HEADER_LENGTH) >> 8);
packet[L2CAP_PDU_LENGTH_OFFSET] = static_cast<uint8_t>(
hdr_len + magic_pat_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH));
packet[L2CAP_PDU_LENGTH_OFFSET + 1] = static_cast<uint8_t>(
(hdr_len + magic_pat_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH)) >> 8);
len = hdr_len + magic_pat_len;
} else {
// Return unchanged
len = hdr_len + pl_len;
}
return len + PACKET_TYPE_LENGTH; // including packet type byte
}
uint32_t SnoopLogger::FilterProfilesHandleHfp(
uint8_t* packet, uint32_t length, uint32_t totlen, uint32_t offset) {
if ((totlen - offset) > cpbr_pat_len) {
if (memcmp(&packet[offset], cpbr_pattern, cpbr_pat_len) == 0) {
length = offset + cpbr_pat_len + 1;
packet[L2CAP_PDU_LENGTH_OFFSET] = offset + cpbr_pat_len - BASIC_L2CAP_HEADER_LENGTH;
packet[L2CAP_PDU_LENGTH_OFFSET] =
offset + cpbr_pat_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH);
return length;
}
if (memcmp(&packet[offset], clcc_pattern, clcc_pat_len) == 0) {
length = offset + cpbr_pat_len + 1;
packet[L2CAP_PDU_LENGTH_OFFSET] = offset + clcc_pat_len - BASIC_L2CAP_HEADER_LENGTH;
packet[L2CAP_PDU_LENGTH_OFFSET] =
offset + clcc_pat_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH);
}
}
return length;
}
void SnoopLogger::FilterProfilesRfcommChannel(
uint8_t* packet,
uint8_t& current_offset,
uint32_t& length,
profile_type_t& current_profile,
bluetooth::hal::ProfilesFilter& filters,
bool is_received,
uint16_t l2cap_channel,
uint32_t& offset,
uint32_t total_length) {
uint8_t addr, ctrl, pf;
addr = packet[current_offset];
current_offset += 1;
ctrl = packet[RFCOMM_EVENT_OFFSET];
current_offset += 1;
pf = ctrl & 0x10;
ctrl = ctrl & 0xef;
addr >>= 2;
if (ctrl != RFCOMM_UIH) {
return;
}
current_profile = filters.DlciToProfile(is_received, l2cap_channel, addr);
if (current_profile != FILTER_PROFILE_NONE) {
uint16_t len;
uint8_t ea;
len = packet[current_offset];
current_offset += 1;
ea = len & 1;
if (!ea) {
current_offset += 1;
}
if (filters.IsRfcommFlowExt(is_received, l2cap_channel, addr) && pf) {
current_offset += 1; // credit byte
}
offset = current_offset;
if ((filters).IsHfpProfile(is_received, l2cap_channel, addr)) {
length = FilterProfilesHandleHfp(packet, length, total_length, offset);
} else {
length = PayloadStrip(current_profile, packet, offset, total_length - offset);
}
}
}
uint32_t SnoopLogger::FilterProfiles(bool is_received, uint8_t* packet) {
bool frag;
uint16_t handle, l2c_chan, l2c_ctl;
uint32_t length, totlen, offset;
uint8_t current_offset = 0;
profile_type_t current_profile = FILTER_PROFILE_NONE;
constexpr uint16_t L2CAP_SIGNALING_CID = 0x0001;
std::lock_guard<std::mutex> lock(profiles_filter_mutex);
handle = ((((uint16_t)packet[ACL_CHANNEL_OFFSET + 1]) << 8) + packet[ACL_CHANNEL_OFFSET]);
frag = (GetBoundaryFlag(handle) == CONTINUATION_PACKET_BOUNDARY);
handle = handle & HANDLE_MASK;
current_offset += 2;
length = (((uint16_t)packet[ACL_LENGTH_OFFSET + 1]) << 8) + packet[ACL_LENGTH_OFFSET];
current_offset += 2;
totlen = length + ACL_HEADER_LENGTH;
length += PACKET_TYPE_LENGTH + ACL_HEADER_LENGTH; // Additional byte is added for packet type
l2c_chan = ((uint16_t)packet[L2CAP_CHANNEL_OFFSET + 1] << 8) + packet[L2CAP_CHANNEL_OFFSET];
current_offset += 4;
auto& filters = profiles_filter_table[handle];
if (frag) {
l2c_chan = filters.ch_last;
} else {
filters.ch_last = l2c_chan;
}
if (l2c_chan != L2CAP_SIGNALING_CID && handle != kQualcommDebugLogHandle) {
if (filters.IsL2capFlowExt(is_received, l2c_chan)) {
l2c_ctl = ((uint16_t)packet[L2CAP_CONTROL_OFFSET + 1] << 8) + packet[L2CAP_CONTROL_OFFSET];
if (!(l2c_ctl & 1)) { // I-Frame
if (((l2c_ctl >> 14) & 0x3) == 0x01) { // Start of L2CAP SDU
current_offset += 2;
}
}
}
offset = current_offset;
current_profile = filters.CidToProfile(is_received, l2c_chan);
if (current_profile != FILTER_PROFILE_NONE) {
if (frag) {
return PACKET_TYPE_LENGTH + ACL_HEADER_LENGTH;
}
return PayloadStrip(current_profile, packet, offset, totlen - offset);
}
if (filters.IsRfcommChannel(is_received, l2c_chan)) {
FilterProfilesRfcommChannel(
packet,
current_offset,
length,
current_profile,
filters,
is_received,
l2c_chan,
offset,
totlen);
}
}
return length;
}
void SnoopLogger::AcceptlistL2capChannel(
uint16_t conn_handle, uint16_t local_cid, uint16_t remote_cid) {
if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
!IsFilterEnabled(kBtSnoopLogFilterProfileRfcommProperty)) {
return;
}
LOG_DEBUG(
"Acceptlisting l2cap channel: conn_handle=%d, local cid=%d, remote cid=%d",
conn_handle,
local_cid,
remote_cid);
std::lock_guard<std::mutex> lock(filter_tracker_list_mutex);
// This will create the entry if there is no associated filter with the
// connection.
filter_tracker_list[conn_handle].AddL2capCid(local_cid, remote_cid);
}
void SnoopLogger::AcceptlistRfcommDlci(uint16_t conn_handle, uint16_t local_cid, uint8_t dlci) {
if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
!IsFilterEnabled(kBtSnoopLogFilterProfileRfcommProperty)) {
return;
}
LOG_DEBUG("Acceptlisting rfcomm channel: local cid=%d, dlci=%d", local_cid, dlci);
std::lock_guard<std::mutex> lock(filter_tracker_list_mutex);
filter_tracker_list[conn_handle].AddRfcommDlci(dlci);
}
void SnoopLogger::AddRfcommL2capChannel(
uint16_t conn_handle, uint16_t local_cid, uint16_t remote_cid) {
if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
!IsFilterEnabled(kBtSnoopLogFilterProfileRfcommProperty)) {
return;
}
LOG_DEBUG(
"Rfcomm data going over l2cap channel: conn_handle=%d local cid=%d remote cid=%d",
conn_handle,
local_cid,
remote_cid);
std::lock_guard<std::mutex> lock(filter_tracker_list_mutex);
filter_tracker_list[conn_handle].SetRfcommCid(local_cid, remote_cid);
local_cid_to_acl.insert({local_cid, conn_handle});
}
void SnoopLogger::ClearL2capAcceptlist(
uint16_t conn_handle, uint16_t local_cid, uint16_t remote_cid) {
if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
!IsFilterEnabled(kBtSnoopLogFilterProfileRfcommProperty)) {
return;
}
LOG_DEBUG(
"Clearing acceptlist from l2cap channel. conn_handle=%d local cid=%d remote cid=%d",
conn_handle,
local_cid,
remote_cid);
std::lock_guard<std::mutex> lock(filter_tracker_list_mutex);
filter_tracker_list[conn_handle].RemoveL2capCid(local_cid, remote_cid);
}
bool SnoopLogger::IsA2dpMediaChannel(uint16_t conn_handle, uint16_t cid, bool is_local_cid) {
if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
!IsFilterEnabled(kBtSnoopLogFilterProfileA2dpProperty)) {
return false;
}
std::lock_guard<std::mutex> lock(a2dpMediaChannels_mutex);
auto iter = std::find_if(
a2dpMediaChannels.begin(),
a2dpMediaChannels.end(),
[conn_handle, cid, is_local_cid](auto& el) {
if (el.conn_handle != conn_handle) return false;
if (is_local_cid) return el.local_cid == cid;
return el.remote_cid == cid;
});
return iter != a2dpMediaChannels.end();
}
bool SnoopLogger::IsA2dpMediaPacket(bool is_received, uint8_t* packet) {
uint16_t cid, conn_handle;
bool is_local_cid = is_received;
/*is_received signifies Rx packet so packet will have local_cid at offset 6
* Tx packet with is_received as false and have remote_cid at the offset*/
conn_handle = (uint16_t)((packet[0] + (packet[1] << 8)) & 0x0FFF);
cid = (uint16_t)(packet[6] + (packet[7] << 8));
return IsA2dpMediaChannel(conn_handle, cid, is_local_cid);
}
void SnoopLogger::AddA2dpMediaChannel(
uint16_t conn_handle, uint16_t local_cid, uint16_t remote_cid) {
if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
!IsFilterEnabled(kBtSnoopLogFilterProfileA2dpProperty)) {
return;
}
if (!SnoopLogger::IsA2dpMediaChannel(conn_handle, local_cid, true)) {
LOG_INFO(
"Add A2DP media channel filtering. conn_handle=%d local cid=%d remote cid=%d",
conn_handle,
local_cid,
remote_cid);
std::lock_guard<std::mutex> lock(a2dpMediaChannels_mutex);
a2dpMediaChannels.push_back({conn_handle, local_cid, remote_cid});
}
}
void SnoopLogger::RemoveA2dpMediaChannel(uint16_t conn_handle, uint16_t local_cid) {
if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
!IsFilterEnabled(kBtSnoopLogFilterProfileA2dpProperty)) {
return;
}
std::lock_guard<std::mutex> lock(a2dpMediaChannels_mutex);
a2dpMediaChannels.erase(
std::remove_if(
a2dpMediaChannels.begin(),
a2dpMediaChannels.end(),
[conn_handle, local_cid](auto& el) {
return (el.conn_handle == conn_handle && el.local_cid == local_cid);
}),
a2dpMediaChannels.end());
}
void SnoopLogger::SetRfcommPortOpen(
uint16_t conn_handle, uint16_t local_cid, uint8_t dlci, uint16_t uuid, bool flow) {
if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
(!IsFilterEnabled(kBtSnoopLogFilterProfilePbapModeProperty) &&
!IsFilterEnabled(kBtSnoopLogFilterProfileMapModeProperty))) {
return;
}
std::lock_guard<std::mutex> lock(profiles_filter_mutex);
profile_type_t profile = FILTER_PROFILE_NONE;
auto& filters = profiles_filter_table[conn_handle];
{
filters.SetupProfilesFilter(
IsFilterEnabled(kBtSnoopLogFilterProfilePbapModeProperty),
IsFilterEnabled(kBtSnoopLogFilterProfileMapModeProperty));
}
LOG_INFO(
"RFCOMM port is opened: handle=%d(0x%x),"
" lcid=%d(0x%x), dlci=%d(0x%x), uuid=%d(0x%x)%s",
conn_handle,
conn_handle,
local_cid,
local_cid,
dlci,
dlci,
uuid,
uuid,
flow ? " Credit Based Flow Control enabled" : "");
if (uuid == PROFILE_UUID_PBAP || (dlci >> 1) == PROFILE_SCN_PBAP) {
profile = FILTER_PROFILE_PBAP;
} else if (uuid == PROFILE_UUID_MAP || (dlci >> 1) == PROFILE_SCN_MAP) {
profile = FILTER_PROFILE_MAP;
} else if (uuid == PROFILE_UUID_HFP_HS) {
profile = FILTER_PROFILE_HFP_HS;
} else if (uuid == PROFILE_UUID_HFP_HF) {
profile = FILTER_PROFILE_HFP_HF;
}
if (profile >= 0) {
filters.ProfileRfcommOpen(profile, local_cid, dlci, uuid, flow);
}
}
void SnoopLogger::SetRfcommPortClose(
uint16_t handle, uint16_t local_cid, uint8_t dlci, uint16_t uuid) {
if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
(!IsFilterEnabled(kBtSnoopLogFilterProfilePbapModeProperty) &&
!IsFilterEnabled(kBtSnoopLogFilterProfileMapModeProperty))) {
return;
}
std::lock_guard<std::mutex> lock(profiles_filter_mutex);
auto& filters = profiles_filter_table[handle];
LOG_INFO(
"RFCOMM port is closed: handle=%d(0x%x),"
" lcid=%d(0x%x), dlci=%d(0x%x), uuid=%d(0x%x)",
handle,
handle,
local_cid,
local_cid,
dlci,
dlci,
uuid,
uuid);
filters.ProfileRfcommClose(filters.DlciToProfile(true, local_cid, dlci));
}
void SnoopLogger::SetL2capChannelOpen(
uint16_t handle, uint16_t local_cid, uint16_t remote_cid, uint16_t psm, bool flow) {
if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
(!IsFilterEnabled(kBtSnoopLogFilterProfilePbapModeProperty) &&
!IsFilterEnabled(kBtSnoopLogFilterProfileMapModeProperty))) {
return;
}
std::lock_guard<std::mutex> lock(profiles_filter_mutex);
profile_type_t profile = FILTER_PROFILE_NONE;
auto& filters = profiles_filter_table[handle];
{
filters.SetupProfilesFilter(
IsFilterEnabled(kBtSnoopLogFilterProfilePbapModeProperty),
IsFilterEnabled(kBtSnoopLogFilterProfileMapModeProperty));
}
LOG_INFO(
"L2CAP channel is opened: handle=%d(0x%x), lcid=%d(0x%x),"
" rcid=%d(0x%x), psm=0x%x%s",
handle,
handle,
local_cid,
local_cid,
remote_cid,
remote_cid,
psm,
flow ? " Standard or Enhanced Control enabled" : "");
if (psm == PROFILE_PSM_RFCOMM) {
filters.ch_rfc_l = local_cid;
filters.ch_rfc_r = remote_cid;
} else if (psm == PROFILE_PSM_PBAP) {
profile = FILTER_PROFILE_PBAP;
} else if (psm == PROFILE_PSM_MAP) {
profile = FILTER_PROFILE_MAP;
}
if (profile >= 0) {
filters.ProfileL2capOpen(profile, local_cid, remote_cid, psm, flow);
}
}
void SnoopLogger::SetL2capChannelClose(uint16_t handle, uint16_t local_cid, uint16_t remote_cid) {
if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
(!IsFilterEnabled(kBtSnoopLogFilterProfilePbapModeProperty) &&
!IsFilterEnabled(kBtSnoopLogFilterProfileMapModeProperty))) {
return;
}
std::lock_guard<std::mutex> lock(profiles_filter_mutex);
auto& filters = profiles_filter_table[handle];
LOG_INFO(
"L2CAP channel is closed: handle=%d(0x%x), lcid=%d(0x%x),"
" rcid=%d(0x%x)",
handle,
handle,
local_cid,
local_cid,
remote_cid,
remote_cid);
filters.ProfileL2capClose(filters.CidToProfile(true, local_cid));
}
void SnoopLogger::FilterCapturedPacket(
HciPacket& packet,
Direction direction,
PacketType type,
uint32_t& length,
PacketHeaderType header) {
if (btsnoop_mode_ != kBtSnoopLogModeFiltered || type != PacketType::ACL) {
return;
}
if (IsFilterEnabled(kBtSnoopLogFilterProfileA2dpProperty)) {
if (IsA2dpMediaPacket(direction == Direction::INCOMING, (uint8_t*)packet.data())) {
length = 0;
return;
}
}
if (IsFilterEnabled(kBtSnoopLogFilterHeadersProperty)) {
CalculateAclPacketLength(length, (uint8_t*)packet.data(), direction == Direction::INCOMING);
}
if (IsFilterEnabled(kBtSnoopLogFilterProfilePbapModeProperty) ||
IsFilterEnabled(kBtSnoopLogFilterProfileMapModeProperty)) {
// If HeadersFiltered applied, do not use ProfilesFiltered
if (length == ntohl(header.length_original)) {
if (packet.size() + EXTRA_BUF_SIZE > DEFAULT_PACKET_SIZE) {
// Add additional bytes for magic string in case
// payload length is less than the length of magic string.
packet.resize((size_t)(packet.size() + EXTRA_BUF_SIZE));
}
length = FilterProfiles(direction == Direction::INCOMING, (uint8_t*)packet.data());
if (length == 0) return;
}
}
if (IsFilterEnabled(kBtSnoopLogFilterProfileRfcommProperty)) {
bool shouldFilter =
SnoopLogger::ShouldFilterLog(direction == Direction::INCOMING, (uint8_t*)packet.data());
if (shouldFilter) {
length = L2CAP_HEADER_SIZE + PACKET_TYPE_LENGTH;
}
}
}
void SnoopLogger::Capture(HciPacket& packet, Direction direction, PacketType type) {
uint64_t timestamp_us =
std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch())
.count();
std::bitset<32> flags = 0;
switch (type) {
case PacketType::CMD:
flags.set(0, false);
flags.set(1, true);
break;
case PacketType::ACL:
case PacketType::ISO:
case PacketType::SCO:
flags.set(0, direction == Direction::INCOMING);
flags.set(1, false);
break;
case PacketType::EVT:
flags.set(0, true);
flags.set(1, true);
break;
}
uint32_t length = packet.size() + /* type byte */ PACKET_TYPE_LENGTH;
PacketHeaderType header = {.length_original = htonl(length),
.length_captured = htonl(length),
.flags = htonl(static_cast<uint32_t>(flags.to_ulong())),
.dropped_packets = 0,
.timestamp = htonll(timestamp_us + kBtSnoopEpochDelta),
.type = static_cast<uint8_t>(type)};
{
std::lock_guard<std::recursive_mutex> lock(file_mutex_);
if (btsnoop_mode_ == kBtSnoopLogModeDisabled) {
// btsnoop disabled, log in-memory btsnooz log only
std::stringstream ss;
size_t included_length = get_btsnooz_packet_length_to_write(packet, type, qualcomm_debug_log_enabled_);
header.length_captured = htonl(included_length + /* type byte */ PACKET_TYPE_LENGTH);
if (!ss.write(reinterpret_cast<const char*>(&header), sizeof(PacketHeaderType))) {
LOG_ERROR("Failed to write packet header for btsnooz, error: \"%s\"", strerror(errno));
}
if (!ss.write(reinterpret_cast<const char*>(packet.data()), included_length)) {
LOG_ERROR("Failed to write packet payload for btsnooz, error: \"%s\"", strerror(errno));
}
btsnooz_buffer_.Push(ss.str());
return;
}
FilterCapturedPacket(packet, direction, type, length, header);
if (length == 0) {
return;
} else if (length != ntohl(header.length_original)) {
header.length_captured = htonl(length);
}
packet_counter_++;
if (packet_counter_ > max_packets_per_file_) {
OpenNextSnoopLogFile();
}
if (!btsnoop_ostream_.write(reinterpret_cast<const char*>(&header), sizeof(PacketHeaderType))) {
LOG_ERROR("Failed to write packet header for btsnoop, error: \"%s\"", strerror(errno));
}
if (!btsnoop_ostream_.write(reinterpret_cast<const char*>(packet.data()), length - 1)) {
LOG_ERROR("Failed to write packet payload for btsnoop, error: \"%s\"", strerror(errno));
}
if (socket_ != nullptr) {
socket_->Write(&header, sizeof(PacketHeaderType));
socket_->Write(packet.data(), packet.size());
}
// std::ofstream::flush() pushes user data into kernel memory. The data will be written even if this process
// crashes. However, data will be lost if there is a kernel panic, which is out of scope of BT snoop log.
// NOTE: std::ofstream::write() followed by std::ofstream::flush() has similar effect as UNIX write(fd, data, len)
// as write() syscall dumps data into kernel memory directly
if (!btsnoop_ostream_.flush()) {
LOG_ERROR("Failed to flush, error: \"%s\"", strerror(errno));
}
}
}
void SnoopLogger::DumpSnoozLogToFile(const std::vector<std::string>& data) const {
std::lock_guard<std::recursive_mutex> lock(file_mutex_);
if (btsnoop_mode_ != kBtSnoopLogModeDisabled) {
LOG_DEBUG("btsnoop log is enabled, skip dumping btsnooz log");
return;
}
auto last_file_path = get_last_log_path(snooz_log_path_);
if (os::FileExists(snooz_log_path_)) {
if (!os::RenameFile(snooz_log_path_, last_file_path)) {
LOG_ERROR(
"Unabled to rename existing snooz log from \"%s\" to \"%s\"",
snooz_log_path_.c_str(),
last_file_path.c_str());
}
} else {
LOG_INFO("Previous log file \"%s\" does not exist, skip renaming", snooz_log_path_.c_str());
}
mode_t prevmask = umask(0);
// do not use std::ios::app as we want override the existing file
std::ofstream btsnooz_ostream(snooz_log_path_, std::ios::binary | std::ios::out);
if (!btsnooz_ostream.good()) {
LOG_ALWAYS_FATAL("Unable to open snoop log at \"%s\", error: \"%s\"", snooz_log_path_.c_str(), strerror(errno));
}
umask(prevmask);
if (!btsnooz_ostream.write(
reinterpret_cast<const char*>(&SnoopLoggerCommon::kBtSnoopFileHeader),
sizeof(SnoopLoggerCommon::FileHeaderType))) {
LOG_ALWAYS_FATAL("Unable to write file header to \"%s\", error: \"%s\"", snooz_log_path_.c_str(), strerror(errno));
}
for (const auto& packet : data) {
if (!btsnooz_ostream.write(packet.data(), packet.size())) {
LOG_ERROR("Failed to write packet payload for btsnooz, error: \"%s\"", strerror(errno));
}
}
if (!btsnooz_ostream.flush()) {
LOG_ERROR("Failed to flush, error: \"%s\"", strerror(errno));
}
}
void SnoopLogger::ListDependencies(ModuleList* list) const {
// We have no dependencies
}
void SnoopLogger::Start() {
std::lock_guard<std::recursive_mutex> lock(file_mutex_);
if (btsnoop_mode_ != kBtSnoopLogModeDisabled) {
OpenNextSnoopLogFile();
if (btsnoop_mode_ == kBtSnoopLogModeFiltered) {
EnableFilters();
}
if (bluetooth::common::InitFlags::IsSnoopLoggerSocketEnabled()) {
auto snoop_logger_socket = std::make_unique<SnoopLoggerSocket>(&syscall_if);
snoop_logger_socket_thread_ = std::make_unique<SnoopLoggerSocketThread>(std::move(snoop_logger_socket));
auto thread_started_future = snoop_logger_socket_thread_->Start();
thread_started_future.wait();
if (thread_started_future.get()) {
RegisterSocket(snoop_logger_socket_thread_.get());
} else {
snoop_logger_socket_thread_->Stop();
snoop_logger_socket_thread_.reset();
snoop_logger_socket_thread_ = nullptr;
}
}
}
alarm_ = std::make_unique<os::RepeatingAlarm>(GetHandler());
alarm_->Schedule(
common::Bind(&delete_old_btsnooz_files, snooz_log_path_, snooz_log_life_time_), snooz_log_delete_alarm_interval_);
}
void SnoopLogger::Stop() {
std::lock_guard<std::recursive_mutex> lock(file_mutex_);
LOG_DEBUG("Closing btsnoop log data at %s", snoop_log_path_.c_str());
CloseCurrentSnoopLogFile();
if (snoop_logger_socket_thread_ != nullptr) {
snoop_logger_socket_thread_->Stop();
snoop_logger_socket_thread_.reset();
snoop_logger_socket_thread_ = nullptr;
socket_ = nullptr;
}
btsnoop_mode_.clear();
// Disable all filters
DisableFilters();
// Cancel the alarm
alarm_->Cancel();
alarm_.reset();
// delete any existing snooz logs
if (!snoop_log_persists) {
delete_btsnoop_files(snooz_log_path_);
}
}
DumpsysDataFinisher SnoopLogger::GetDumpsysData(flatbuffers::FlatBufferBuilder* builder) const {
LOG_DEBUG("Dumping btsnooz log data to %s", snooz_log_path_.c_str());
DumpSnoozLogToFile(btsnooz_buffer_.Pull());
return Module::GetDumpsysData(builder);
}
size_t SnoopLogger::GetMaxPacketsPerFile() {
// Allow override max packet per file via system property
auto max_packets_per_file = kDefaultBtSnoopMaxPacketsPerFile;
{
auto max_packets_per_file_prop = os::GetSystemProperty(kBtSnoopMaxPacketsPerFileProperty);
if (max_packets_per_file_prop) {
auto max_packets_per_file_number = common::Uint64FromString(max_packets_per_file_prop.value());
if (max_packets_per_file_number) {
max_packets_per_file = max_packets_per_file_number.value();
}
}
}
return max_packets_per_file;
}
size_t SnoopLogger::GetMaxPacketsPerBuffer() {
// We want to use at most 256 KB memory for btsnooz log for release builds
// and 512 KB memory for userdebug/eng builds
auto is_debuggable = os::GetSystemPropertyBool(kIsDebuggableProperty, false);
size_t btsnooz_max_memory_usage_bytes = (is_debuggable ? 1024 : 256) * 1024;
// Calculate max number of packets based on max memory usage and max packet size
return btsnooz_max_memory_usage_bytes / kDefaultBtSnoozMaxBytesPerPacket;
}
std::string SnoopLogger::GetBtSnoopMode() {
// Default mode is FILTERED on userdebug/eng build, DISABLED on user build.
// In userdebug/eng build, it can also be overwritten by modifying the global setting
std::string default_mode = kBtSnoopLogModeDisabled;
{
auto is_debuggable = os::GetSystemPropertyBool(kIsDebuggableProperty, false);
if (is_debuggable) {
auto default_mode_property = os::GetSystemProperty(kBtSnoopDefaultLogModeProperty);
if (default_mode_property) {
default_mode = std::move(default_mode_property.value());
} else {
default_mode = kBtSnoopLogModeFiltered;
}
}
}
// Get the actual mode if exist
std::string btsnoop_mode = default_mode;
{
auto btsnoop_mode_prop = os::GetSystemProperty(kBtSnoopLogModeProperty);
if (btsnoop_mode_prop) {
btsnoop_mode = std::move(btsnoop_mode_prop.value());
}
}
// If Snoop Logger already set up, return current mode
bool btsnoop_mode_empty = btsnoop_mode_.empty();
LOG_INFO("btsnoop_mode_empty: %d", btsnoop_mode_empty);
if (!btsnoop_mode_empty) {
return btsnoop_mode_;
}
return btsnoop_mode;
}
void SnoopLogger::RegisterSocket(SnoopLoggerSocketInterface* socket) {
std::lock_guard<std::recursive_mutex> lock(file_mutex_);
socket_ = socket;
}
bool SnoopLogger::IsBtSnoopLogPersisted() {
auto is_debuggable = os::GetSystemPropertyBool(kIsDebuggableProperty, false);
return is_debuggable && os::GetSystemPropertyBool(kBtSnoopLogPersists, false);
}
bool SnoopLogger::IsQualcommDebugLogEnabled() {
// Check system prop if the soc manufacturer is Qualcomm
bool qualcomm_debug_log_enabled = false;
{
auto soc_manufacturer_prop = os::GetSystemProperty(kSoCManufacturerProperty);
qualcomm_debug_log_enabled = soc_manufacturer_prop.has_value() &&
common::StringTrim(soc_manufacturer_prop.value()) == kSoCManufacturerQualcomm;
}
return qualcomm_debug_log_enabled;
}
const ModuleFactory SnoopLogger::Factory = ModuleFactory([]() {
return new SnoopLogger(
os::ParameterProvider::SnoopLogFilePath(),
os::ParameterProvider::SnoozLogFilePath(),
GetMaxPacketsPerFile(),
GetMaxPacketsPerBuffer(),
GetBtSnoopMode(),
IsQualcommDebugLogEnabled(),
kBtSnoozLogLifeTime,
kBtSnoozLogDeleteRepeatingAlarmInterval,
IsBtSnoopLogPersisted());
});
} // namespace hal
} // namespace bluetooth