Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf2a0b20 authored by xiaowang's avatar xiaowang Committed by Jakub Rotkiewicz
Browse files

SnoopLogger: Debug enhancements

Snapshot as of:
bdb2b726307531646e80f97c7fffe71bc9314a75
3463820ca67cd9dfe51f04c88f9cfcf2d0ca3c7a
7531074b80288b1eeab531d0f649068f7c284f1f
2bea657d9244680d61eacbf6239416aad699f5c0
255ceb658b3c68b571ac0af590afca5efdb921f5
b80618fdf9ef49dbd8ea2be78fe0b652810d40a0
0e8818d1c6af5657c98554cb046afa01013c9a73
41bdbdf4f62af772c676ea938da55e803988ef0d
e7f961fd5183f9112fb51778ad1af182e881460d
0b893d9c0f6b54e8caa8bf2e3e33748ba20263ed
e8621e3f7939f195fe51ccc45aa85f91214b8a39
01c139248823fbbff887c270c7e8b5a05817463a
97d3fda2b1e8dee36e2db57254eae20e6a0f4e12
f17960b9cc27b1467720b962a7048cdaba11ddbc

 - added snoop logging modes: full, filtered and disabled
 - rfcommchannelsfiltered option allows to filter out not-acceptlisted
   RFCOMM and L2CAP channels
 - removed is_enabled and is_filtered flags, the snoop mode information
   is now present in btsnoop_mode_ variable
 - added option to filter out media packets
 - cache media channel info in snoop logger
 - added option to filter pbap, map and hfp profiles
 - added option to filter acl packets leaving only headers

Sponsor: rotkiewicz@
Bug: 247859568
Tag: #feature
Test: atest BluetoothInstrumentationTests
Test: atest bluetooth_test_gd_unit
BYPASS_LONG_LINES_REASON: Bluetooth likes 120 lines

Change-Id: I4b7bb84ec2f14aff60388b10eae24d8896f61f5b
parent bff94427
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -1244,6 +1244,11 @@ void bta_av_str_opened(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
        .edr = 0,
        .sep = AVDT_TSEP_INVALID,
    };

    if (p_scb) {
      L2CA_SetMediaStreamChannel(p_scb->l2c_cid, true);
    }

    p = BTM_ReadRemoteFeatures(p_scb->PeerAddress());
    if (p != NULL) {
      if (HCI_EDR_ACL_2MPS_SUPPORTED(p)) open.edr |= BTA_AV_EDR_2MBPS;
@@ -2474,6 +2479,10 @@ void bta_av_str_closed(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
    BTM_default_unblock_role_switch();
  }

  if (p_scb) {
    L2CA_SetMediaStreamChannel(p_scb->l2c_cid, false);
  }

  if (p_scb->open_status != BTA_AV_SUCCESS) {
    /* must be failure when opening the stream */
    data.open.bd_addr = p_scb->PeerAddress();
+992 −26

File changed.

Preview size limit exceeded, changes collapsed.

+235 −9
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@
#include <iostream>
#include <mutex>
#include <string>
#include <unordered_map>
#include <unordered_set>

#include "common/circular_buffer.h"
#include "hal/hci_hal.h"
@@ -35,22 +37,154 @@ namespace hal {
static uint64_t file_creation_time;
#endif

class FilterTracker {
 public:
  // NOTE: 1 is used as a static CID for L2CAP signaling
  std::unordered_set<uint16_t> l2c_local_cid = {1};
  std::unordered_set<uint16_t> l2c_remote_cid = {1};
  uint16_t rfcomm_local_cid = 0;
  uint16_t rfcomm_remote_cid = 0;
  std::unordered_set<uint16_t> rfcomm_channels = {0};

  // Adds L2C channel to acceptlist.
  void AddL2capCid(uint16_t local_cid, uint16_t remote_cid);

  // Sets L2CAP channel that RFCOMM uses.
  void SetRfcommCid(uint16_t local_cid, uint16_t remote_cid);

  // Remove L2C channel from acceptlist.
  void RemoveL2capCid(uint16_t local_cid, uint16_t remote_cid);

  void AddRfcommDlci(uint8_t channel);

  bool IsAcceptlistedL2cap(bool local, uint16_t cid);

  bool IsRfcommChannel(bool local, uint16_t cid);

  bool IsAcceptlistedDlci(uint8_t dlci);
};

typedef enum {
  FILTER_PROFILE_NONE = -1,
  FILTER_PROFILE_PBAP = 0,
  FILTER_PROFILE_HFP_HS,
  FILTER_PROFILE_HFP_HF,
  FILTER_PROFILE_MAP,
  FILTER_PROFILE_MAX,
} profile_type_t;

class ProfilesFilter {
 public:
  void SetupProfilesFilter(bool pbap_filtered, bool map_filtered);

  bool IsHfpProfile(bool local, uint16_t cid, uint8_t dlci);

  bool IsL2capMatch(bool local, uint16_t cid);

  bool IsL2capFlowExt(bool local, uint16_t cid);

  bool IsRfcommMatch(bool local, uint16_t cid, uint8_t dlci);

  bool IsRfcommFlowExt(bool local, uint16_t cid, uint8_t dlci);

  profile_type_t CidToProfile(bool local, uint16_t cid);

  profile_type_t DlciToProfile(bool local, uint16_t cid, uint8_t dlci);

  void ProfileL2capOpen(
      profile_type_t profile, uint16_t lcid, uint16_t rcid, uint16_t psm, bool flow_ext);

  void ProfileL2capClose(profile_type_t profile);

  void ProfileRfcommOpen(
      profile_type_t profile, uint16_t lcid, uint8_t dlci, uint16_t uuid, bool flow_ext);

  void ProfileRfcommClose(profile_type_t profile);

  bool IsRfcommChannel(bool local, uint16_t cid);

  void PrintProfilesConfig();

  static inline std::string ProfileToString(profile_type_t profile) {
    switch (profile) {
      case FILTER_PROFILE_NONE:
        return "FILTER_PROFILE_NONE";
      case FILTER_PROFILE_PBAP:
        return "FILTER_PROFILE_PBAP";
      case FILTER_PROFILE_HFP_HS:
        return "FILTER_PROFILE_HFP_HS";
      case FILTER_PROFILE_HFP_HF:
        return "FILTER_PROFILE_HFP_HF";
      case FILTER_PROFILE_MAP:
        return "FILTER_PROFILE_MAP";
      default:
        return "[Unknown profile_type_t]";
    }
  }

  uint16_t ch_rfc_l, ch_rfc_r;  // local & remote L2CAP channel for RFCOMM
  uint16_t ch_last;             // last channel seen for fragment packet

 private:
  bool setup_done_flag = false;
  struct {
    profile_type_t type;
    bool enabled, l2cap_opened, rfcomm_opened;
    bool flow_ext_l2cap, flow_ext_rfcomm;
    uint16_t lcid, rcid, rfcomm_uuid, psm;
    uint8_t scn;
  } profiles[FILTER_PROFILE_MAX];
  profile_type_t current_profile;
};

class SnoopLogger : public ::bluetooth::Module {
 public:
  static const ModuleFactory Factory;

  static const std::string kBtSnoopLogModeDisabled;
  static const std::string kBtSnoopLogModeFiltered;
  static const std::string kBtSnoopLogModeFull;
  static const std::string kSoCManufacturerQualcomm;

  static const std::string kBtSnoopMaxPacketsPerFileProperty;
  static const std::string kIsDebuggableProperty;
  static const std::string kBtSnoopLogModeProperty;
  static const std::string kBtSnoopLogPersists;
  static const std::string kBtSnoopDefaultLogModeProperty;
  static const std::string kBtSnoopLogFilterTypesProperty;
  static const std::string kBtSnoopLogFilterProfilesPbapModeProperty;
  static const std::string kBtSnoopLogFilterProfilesMapModeProperty;
  static const std::string kSoCManufacturerProperty;

  static const std::string kBtSnoopLogModeDisabled;
  static const std::string kBtSnoopLogModeFiltered;
  static const std::string kBtSnoopLogModeFull;

  static const std::string kSoCManufacturerQualcomm;

  static const std::string kBtSnoopLogFilterTypeRfcommChannelFiltered;
  static const std::string kBtSnoopLogFilterTypeHeadersFiltered;
  static const std::string kBtSnoopLogFilterTypeA2dpPktsFiltered;
  static const std::string kBtSnoopLogFilterTypeProfilesFiltered;

  static const std::string kBtSnoopLogFilterProfilePbap;
  static const std::string kBtSnoopLogFilterProfileMap;

  static const std::string kBtSnoopLogFilterProfileModeFullfillter;
  static const std::string kBtSnoopLogFilterProfileModeHeader;
  static const std::string kBtSnoopLogFilterProfileModeMagic;
  static const std::string kBtSnoopLogFilterProfileModeDisabled;

  struct SnoopFilterType {
    const std::string name;
    bool enabled;
  };

  std::vector<SnoopFilterType> kBtSnoopLogFilterTypes = {
      {kBtSnoopLogFilterTypeRfcommChannelFiltered, false},
      {kBtSnoopLogFilterTypeHeadersFiltered, false},
      {kBtSnoopLogFilterTypeA2dpPktsFiltered, false},
      {kBtSnoopLogFilterTypeProfilesFiltered, false}};

  std::unordered_map<std::string, std::string> kBtSnoopLogFilterProfiles = {
      {kBtSnoopLogFilterProfilePbap, kBtSnoopLogFilterProfileModeDisabled},
      {kBtSnoopLogFilterProfileMap, kBtSnoopLogFilterProfileModeDisabled}};

  // Put in header for test
  struct PacketHeaderType {
    uint32_t length_original;
@@ -61,6 +195,13 @@ class SnoopLogger : public ::bluetooth::Module {
    uint8_t type;
  } __attribute__((__packed__));

  // Struct for caching info about L2CAP Media Channel
  struct A2dpMediaChannel {
    uint16_t conn_handle;
    uint16_t local_cid;
    uint16_t remote_cid;
  };

  // Returns the maximum number of packets per file
  // Changes to this value is only effective after restarting Bluetooth
  static size_t GetMaxPacketsPerFile();
@@ -92,11 +233,56 @@ class SnoopLogger : public ::bluetooth::Module {
    OUTGOING,
  };

  void Capture(const HciPacket& packet, Direction direction, PacketType type);
  void Capture(HciPacket& packet, Direction direction, PacketType type);

  // Set a L2CAP channel as acceptlisted, allowing packets with that L2CAP CID
  // to show up in the snoop logs.
  void AcceptlistL2capChannel(uint16_t conn_handle, uint16_t local_cid, uint16_t remote_cid);

  // Set a RFCOMM dlci as acceptlisted, allowing packets with that RFCOMM CID
  // to show up in the snoop logs. The local_cid is used to associate it with
  // its corrisponding ACL connection. The dlci is the channel with direction
  // so there is no chance of a collision if two services are using the same
  // channel but in different directions.
  void AcceptlistRfcommDlci(uint16_t conn_handle, uint16_t local_cid, uint8_t dlci);

  // Indicate that the provided L2CAP channel is being used for RFCOMM.
  // If packets with the provided L2CAP CID are encountered, they will be
  // filtered on RFCOMM based on channels provided to |filter_rfcomm_channel|.
  void AddRfcommL2capChannel(uint16_t conn_handle, uint16_t local_cid, uint16_t remote_cid);

  // Clear an L2CAP channel from being filtered.
  void ClearL2capAcceptlist(uint16_t conn_handle, uint16_t local_cid, uint16_t remote_cid);

  // Cache A2DP Media Channel info for filtering media packets.
  void AddA2dpMediaChannel(uint16_t conn_handle, uint16_t local_cid, uint16_t remote_cid);

  // Remove A2DP Media Channel cache
  void RemoveA2dpMediaChannel(uint16_t conn_handle, uint16_t local_cid);

  // New RFCOMM port is opened.
  void SetRfcommPortOpen(
      uint16_t conn_handle, uint16_t local_cid, uint8_t dlci, uint16_t uuid, bool flow);
  // RFCOMM port is closed.
  void SetRfcommPortClose(uint16_t handle, uint16_t local_cid, uint8_t dlci, uint16_t uuid);

  // New L2CAP channel is opened.
  void SetL2capChannelOpen(
      uint16_t handle, uint16_t local_cid, uint16_t remote_cid, uint16_t psm, bool flow);
  // L2CAP channel is closed.
  void SetL2capChannelClose(uint16_t handle, uint16_t local_cid, uint16_t remote_cid);

  void RegisterSocket(SnoopLoggerSocketInterface* socket);

 protected:
  // Packet type length
  static const size_t PACKET_TYPE_LENGTH;
  // The size of the L2CAP header. All information past this point is removed from
  // a filtered packet.
  static const uint32_t L2CAP_HEADER_SIZE;
  // Max packet data size when headersfiltered option enabled
  static const size_t MAX_HCI_ACL_LEN;

  void ListDependencies(ModuleList* list) const override;
  void Start() override;
  void Stop() override;
@@ -105,7 +291,6 @@ class SnoopLogger : public ::bluetooth::Module {
    return std::string("SnoopLogger");
  }

  // Visible for testing
  SnoopLogger(
      std::string snoop_log_path,
      std::string snooz_log_path,
@@ -119,15 +304,56 @@ class SnoopLogger : public ::bluetooth::Module {
  void CloseCurrentSnoopLogFile();
  void OpenNextSnoopLogFile();
  void DumpSnoozLogToFile(const std::vector<std::string>& data) const;
  // Enable filters according to their sysprops
  void EnableFilters();
  // Disable all filters
  void DisableFilters();
  // Check if the filter is enabled. Pass filter name as a string.
  bool IsFilterEnabled(std::string filter_name);
  // Enable filters for each profile according to their sysprops
  void EnableProfilesFilters();
  // Disable filters for each profile
  void DisableProfilesFilters();
  // Check if packet should be filtered (rfcommchannelfiltered mode)
  bool ShouldFilterLog(bool is_received, uint8_t* packet);
  // Calculate packet length (snoopheadersfiltered mode)
  void CalculateAclPacketLength(uint32_t& length, uint8_t* packet, bool is_received);
  // Strip packet's payload (profilesfiltered mode)
  uint32_t PayloadStrip(
      profile_type_t current_profile, uint8_t* packet, uint32_t hdr_len, uint32_t pl_len);
  // Filter profile packet according to its filtering mode
  uint32_t FilterProfiles(bool is_received, uint8_t* packet);
  // Check if packet is A2DP media packet (a2dppktsfiltered mode)
  bool IsA2dpMediaPacket(bool is_received, uint8_t* packet);
  // Chec if channel is cached in snoop logger for filtering (a2dppktsfiltered mode)
  bool IsA2dpMediaChannel(uint16_t conn_handle, uint16_t cid, bool is_local_cid);
  // Handle HFP filtering while profilesfiltered enabled
  uint32_t FilterProfilesHandleHfp(
      uint8_t* packet, uint32_t length, uint32_t totlen, uint32_t offset);
  void 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);
  void FilterCapturedPacket(
      HciPacket& packet,
      Direction direction,
      PacketType type,
      uint32_t& length,
      PacketHeaderType header);

  std::unique_ptr<SnoopLoggerSocketThread> snoop_logger_socket_thread_;

 private:
  static std::string btsnoop_mode_;
  std::string snoop_log_path_;
  std::string snooz_log_path_;
  std::ofstream btsnoop_ostream_;
  bool is_enabled_ = false;
  bool is_filtered_ = false;
  size_t max_packets_per_file_;
  common::CircularBuffer<std::string> btsnooz_buffer_;
  bool qualcomm_debug_log_enabled_ = false;
+826 −0

File changed.

Preview size limit exceeded, changes collapsed.

+4 −0
Original line number Diff line number Diff line
@@ -21,6 +21,10 @@ cc_library_static {
        "src/hci_packet_parser.cc",
        "src/packet_fragmenter.cc",
    ],
    generated_headers: [
        "BluetoothGeneratedDumpsysDataSchema_h",
        "BluetoothGeneratedPackets_h",
    ],
    target: {
        android: {
            srcs: ["src/hci_layer_android.cc"],
Loading