OSDN Git Service

Btsnoop write from external process
authorMatadeen Mishra <matade@codeaurora.org>
Fri, 14 Nov 2014 14:16:19 +0000 (19:46 +0530)
committerLinux Build Service Account <lnxbuild@localhost>
Wed, 24 Aug 2016 14:09:36 +0000 (08:09 -0600)
- Write btsnoop from external process
  Introduced a new proces to dump snoop packets into file.
  Snoop packet can be send to the remote process using local
  socket as writing to file from BT process can cause A2DP
  choppyness.

- Avoid btsnoop file write if btsnoop client is connected
  Existing implementation writes it to file though the packet is
  sent over network socket. This chnage will make sure to avoid
  writing it to file if any of the network/local socket is
  connected.

- Generalized the mechanism to use same code irrespective of
  what type of socket it is. Otherwise having thread for each
  socket is not preferrable.

- Adjust the GMT offset to match with logcat logs.

- Limit the block of snoop write to 10ms and drop the packet.

- Memcpy issue single write to eliminate multiple I/O.

- Enable snoop by default on userdebug build.

- Option to override snoop config for userdebug build.

Change-Id: I13749dc348baf88af57e3ebec1ec7acd5e999c04

conf/bt_stack.conf
hci/Android.mk
hci/src/btsnoop.c
hci/src/btsnoop_net.c
include/stack_config.h
main/stack_config.c
tools/btsnoop_dump/Android.mk [new file with mode: 0644]
tools/btsnoop_dump/btsnoop_dump.c [new file with mode: 0644]

index 3a7f48b..25050d8 100644 (file)
@@ -1,6 +1,12 @@
+# Enable BtSnoop configuration from this config file
+# Snoop is enabled by default on userdebug builds, below configuration
+# enables to override it and take effect from the configurations here.
+BtSnoopConfigFromFile=false
+
 # Enable BtSnoop logging function
 # valid value : true, false
 BtSnoopLogOutput=false
+BtSnoopExtDump=false
 
 # BtSnoop log output file
 BtSnoopFileName=/sdcard/btsnoop_hci.log
index cabb0e6..8734a5b 100644 (file)
@@ -40,6 +40,11 @@ LOCAL_MODULE := libbt-hci
 ifeq ($(BLUETOOTH_HCI_USE_MCT),true)
 LOCAL_CFLAGS += -DHCI_USE_MCT
 endif
+
+ifeq ($(TARGET_BUILD_VARIANT),userdebug)
+    LOCAL_CFLAGS += -DBTSNOOP_DEFAULT=TRUE
+endif
+
 LOCAL_CFLAGS += $(bluetooth_CFLAGS)
 LOCAL_CONLYFLAGS += $(bluetooth_CONLYFLAGS)
 LOCAL_CPPFLAGS += $(bluetooth_CPPFLAGS)
index 50df910..edffa20 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <arpa/inet.h>
 #include <assert.h>
+#include <cutils/properties.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <sys/stat.h>
 #include <sys/time.h>
+#include <sys/poll.h>
 #include <unistd.h>
 
 #include "bt_types.h"
@@ -51,6 +54,19 @@ typedef enum {
 static const uint64_t BTSNOOP_EPOCH_DELTA = 0x00dcddb30f2f8000ULL;
 
 static const stack_config_t *stack_config;
+extern int client_socket_btsnoop;
+static long int gmt_offset;
+#define USEC_PER_SEC 1000000L
+#define MAX_SNOOP_BUF_SIZE 1200
+
+// External BT snoop
+bool hci_ext_dump_enabled = false;
+
+/* snoop config from the config file, required for userdebug
+   build where snoop is enabled by default.
+   power/perf measurements need the snoop to be disabled.
+*/
+bool btsnoop_conf_from_file = false;
 
 static int logfile_fd = INVALID_FD;
 static bool module_started;
@@ -68,7 +84,20 @@ static void update_logging();
 // Module lifecycle functions
 
 static future_t *start_up(void) {
+  time_t t = time(NULL);
+  struct tm tm_cur;
+
+  localtime_r (&t, &tm_cur);
+  LOG_INFO(LOG_TAG, "%s Time GMT offset %ld\n", __func__, tm_cur.tm_gmtoff);
+  gmt_offset = tm_cur.tm_gmtoff;
+
   module_started = true;
+  stack_config->get_btsnoop_ext_options(&hci_ext_dump_enabled, &btsnoop_conf_from_file);
+#if (BTSNOOP_DEFAULT == TRUE)
+  if (btsnoop_conf_from_file == false) {
+    hci_ext_dump_enabled = true;
+  }
+#endif
   update_logging();
 
   return NULL;
@@ -76,6 +105,9 @@ static future_t *start_up(void) {
 
 static future_t *shut_down(void) {
   module_started = false;
+  if (hci_ext_dump_enabled == true) {
+    property_set("bluetooth.startbtsnoop", "false");
+  }
   update_logging();
 
   return NULL;
@@ -141,6 +173,7 @@ const btsnoop_t *btsnoop_get_interface() {
 static uint64_t btsnoop_timestamp(void) {
   struct timeval tv;
   gettimeofday(&tv, NULL);
+  tv.tv_sec += gmt_offset;
 
   // Timestamp is in microseconds.
   uint64_t timestamp = tv.tv_sec * 1000 * 1000LL;
@@ -151,7 +184,7 @@ static uint64_t btsnoop_timestamp(void) {
 
 static void update_logging() {
   bool should_log = module_started &&
-    (logging_enabled_via_api || stack_config->get_btsnoop_turned_on());
+    (logging_enabled_via_api || stack_config->get_btsnoop_turned_on() || hci_ext_dump_enabled);
 
   if (should_log == is_logging)
     return;
@@ -160,6 +193,9 @@ static void update_logging() {
   if (should_log) {
     btsnoop_net_open();
 
+    if (hci_ext_dump_enabled == true) {
+      property_set("bluetooth.startbtsnoop", "true");
+    }
     const char *log_path = stack_config->get_btsnoop_log_path();
 
     // Save the old log if configured to do so
@@ -189,17 +225,37 @@ static void update_logging() {
 }
 
 static void btsnoop_write(const void *data, size_t length) {
+  if (client_socket_btsnoop != -1) {
+    btsnoop_net_write(data, length);
+    /* skip writing to file if external client is connected*/
+    return;
+  }
+
   if (logfile_fd != INVALID_FD)
     write(logfile_fd, data, length);
+}
 
-  btsnoop_net_write(data, length);
+#ifdef DEBUG_SNOOP
+static uint64_t time_now_us() {
+    struct timespec ts_now;
+    clock_gettime(CLOCK_BOOTTIME, &ts_now);
+    return ((uint64_t)ts_now.tv_sec * USEC_PER_SEC) + ((uint64_t)ts_now.tv_nsec / 1000);
 }
+#endif
 
 static void btsnoop_write_packet(packet_type_t type, const uint8_t *packet, bool is_received) {
   int length_he = 0;
   int length;
   int flags;
   int drops = 0;
+  struct pollfd pfd;
+#ifdef DEBUG_SNOOP
+  uint64_t ts_begin;
+  uint64_t ts_end, ts_diff;
+#endif
+  uint8_t snoop_buf[MAX_SNOOP_BUF_SIZE] = {0};
+  uint32_t offset = 0;
+
   switch (type) {
     case kCommandPacket:
       length_he = packet[2] + 4;
@@ -229,12 +285,73 @@ static void btsnoop_write_packet(packet_type_t type, const uint8_t *packet, bool
   time_hi = htonl(time_hi);
   time_lo = htonl(time_lo);
 
-  btsnoop_write(&length, 4);
-  btsnoop_write(&length, 4);
-  btsnoop_write(&flags, 4);
-  btsnoop_write(&drops, 4);
-  btsnoop_write(&time_hi, 4);
-  btsnoop_write(&time_lo, 4);
-  btsnoop_write(&type, 1);
-  btsnoop_write(packet, length_he - 1);
+  /* store the length in both original and included fields */
+  memcpy(snoop_buf + offset, &length, 4);
+  offset += 4;
+  memcpy(snoop_buf + offset, &length, 4);
+  offset += 4;
+
+  /* flags:  */
+  memcpy(snoop_buf + offset, &flags, 4);
+  offset += 4;
+
+  /* drops: none */
+  memcpy(snoop_buf + offset, &drops, 4);
+  offset += 4;
+
+  /* time */
+  memcpy(snoop_buf + offset, &time_hi, 4);
+  offset += 4;
+  memcpy(snoop_buf + offset, &time_lo, 4);
+  offset = offset + 4;
+
+  snoop_buf[offset] = type;
+  offset += 1;
+  if (offset + length_he + 1 > MAX_SNOOP_BUF_SIZE) {
+    LOG_ERROR(LOG_TAG, "Bad packet length, downgrading the length to %d from %d",
+                                      MAX_SNOOP_BUF_SIZE - offset - 1, length_he);
+    length_he = MAX_SNOOP_BUF_SIZE - offset - 1;
+  }
+  memcpy(snoop_buf + offset, packet, length_he - 1);
+
+  if (client_socket_btsnoop != -1) {
+    pfd.fd = client_socket_btsnoop;
+    pfd.events = POLLOUT;
+#ifdef DEBUG_SNOOP
+    ts_begin = time_now_us();
+#endif
+
+    if (poll(&pfd, 1, 10) == 0) {
+      LOG_ERROR(LOG_TAG, "btsnoop poll : Taking more than 10 ms : skip dump");
+#ifdef DEBUG_SNOOP
+      ts_end = time_now_us();
+      ts_diff = ts_end - ts_begin;
+      if (ts_diff > 10000) {
+        LOG_ERROR(LOG_TAG, "btsnoop poll T/O : took more time %08lld us", ts_diff);
+      }
+#endif
+      return;
+    }
+
+#ifdef DEBUG_SNOOP
+    ts_end = time_now_us();
+    ts_diff = ts_end - ts_begin;
+    if (ts_diff > 10000) {
+      LOG_ERROR(LOG_TAG, "btsnoop poll : took more time %08lld us", ts_diff);
+    }
+#endif
+  }
+#ifdef DEBUG_SNOOP
+  ts_begin = time_now_us();
+#endif
+
+  btsnoop_write(snoop_buf, offset + length_he - 1);
+
+#ifdef DEBUG_SNOOP
+  ts_end = time_now_us();
+  ts_diff = ts_end - ts_begin;
+  if (ts_diff > 10000) {
+    LOG_ERROR(LOG_TAG, "btsnoop write : Write took more time %08lld us", ts_diff);
+  }
+#endif
 }
index 0bfad4e..7789cf7 100644 (file)
@@ -19,6 +19,9 @@
 #define LOG_TAG "bt_snoop_net"
 
 #include <assert.h>
+#include <cutils/sockets.h>
+#include <sys/un.h>
+#include <sys/poll.h>
 #include <errno.h>
 #include <netinet/in.h>
 #include <pthread.h>
@@ -36,19 +39,46 @@ static void safe_close_(int *fd);
 static void *listen_fn_(void *context);
 
 static const char *LISTEN_THREAD_NAME_ = "btsnoop_net_listen";
+#if (defined(BT_NET_DEBUG) && (NET_DEBUG == TRUE))
 static const int LOCALHOST_ = 0x7F000001;
 static const int LISTEN_PORT_ = 8872;
+#endif
 
 static pthread_t listen_thread_;
 static bool listen_thread_valid_ = false;
 static pthread_mutex_t client_socket_lock_ = PTHREAD_MUTEX_INITIALIZER;
 static int listen_socket_ = -1;
-static int client_socket_ = -1;
+int client_socket_btsnoop = -1;
+
+/*
+    local socket for writing from different process
+    to limit blocking of HCI threads.
+*/
+#define LOCAL_SOCKET_NAME "bthcitraffic"
+static int listen_socket_local_ = -1;
+
+static int local_socket_create(void) {
+
+  listen_socket_local_ = socket(AF_LOCAL, SOCK_STREAM, 0);
+  if(listen_socket_local_ < 0) {
+    return -1;
+  }
+
+  if(socket_local_server_bind(listen_socket_local_, LOCAL_SOCKET_NAME,
+      ANDROID_SOCKET_NAMESPACE_ABSTRACT) < 0) {
+    LOG_ERROR(LOG_TAG, "Failed to create Local Socket (%s)", strerror(errno));
+    return -1;
+  }
+
+  if (listen(listen_socket_local_, 1) < 0) {
+    LOG_ERROR(LOG_TAG, "Local socket listen failed (%s)", strerror(errno));
+    close(listen_socket_local_);
+    return -1;
+  }
+  return listen_socket_local_;
+}
 
 void btsnoop_net_open() {
-#if (!defined(BT_NET_DEBUG) || (BT_NET_DEBUG != TRUE))
-  return;               // Disable using network sockets for security reasons
-#endif
 
   listen_thread_valid_ = (pthread_create(&listen_thread_, NULL, listen_fn_, NULL) == 0);
   if (!listen_thread_valid_) {
@@ -59,44 +89,56 @@ void btsnoop_net_open() {
 }
 
 void btsnoop_net_close() {
-#if (!defined(BT_NET_DEBUG) || (BT_NET_DEBUG != TRUE))
-  return;               // Disable using network sockets for security reasons
-#endif
 
   if (listen_thread_valid_) {
+#if (defined(BT_NET_DEBUG) && (NET_DEBUG == TRUE))
+    // Disable using network sockets for security reasons
     shutdown(listen_socket_, SHUT_RDWR);
+#endif
+    shutdown(listen_socket_local_, SHUT_RDWR);
     pthread_join(listen_thread_, NULL);
-    safe_close_(&client_socket_);
+    safe_close_(&client_socket_btsnoop);
     listen_thread_valid_ = false;
   }
 }
 
 void btsnoop_net_write(const void *data, size_t length) {
-#if (!defined(BT_NET_DEBUG) || (BT_NET_DEBUG != TRUE))
-  return;               // Disable using network sockets for security reasons
-#endif
+  ssize_t ret;
 
   pthread_mutex_lock(&client_socket_lock_);
-  if (client_socket_ != -1) {
-    ssize_t ret;
-    OSI_NO_INTR(ret = send(client_socket_, data, length, 0));
-
-    if (ret == -1 && errno == ECONNRESET) {
-      safe_close_(&client_socket_);
-    }
+  if (client_socket_btsnoop != -1) {
+    do {
+      if ((ret = send(client_socket_btsnoop, data, length, 0)) == -1 && errno == ECONNRESET) {
+        safe_close_(&client_socket_btsnoop);
+        LOG_INFO(LOG_TAG, "%s conn closed", __func__);
+      }
+      if ((size_t) ret < length) {
+        LOG_ERROR(LOG_TAG, "%s: send : not able to write complete packet", __func__);
+      }
+      length -= ret;
+    } while ((length > 0) && (ret != -1));
   }
+
   pthread_mutex_unlock(&client_socket_lock_);
 }
 
 static void *listen_fn_(UNUSED_ATTR void *context) {
+  fd_set sock_fds;
+  int fd_max = -1, retval;
 
   prctl(PR_SET_NAME, (unsigned long)LISTEN_THREAD_NAME_, 0, 0, 0);
 
+  FD_ZERO(&sock_fds);
+
+#if (defined(BT_NET_DEBUG) && (NET_DEBUG == TRUE))
+  // Disable using network sockets for security reasons
   listen_socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
   if (listen_socket_ == -1) {
     LOG_ERROR(LOG_TAG, "%s socket creation failed: %s", __func__, strerror(errno));
     goto cleanup;
   }
+  FD_SET(listen_socket_, &sock_fds);
+  fd_max = listen_socket_;
 
   int enable = 1;
   if (setsockopt(listen_socket_, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) == -1) {
@@ -117,30 +159,75 @@ static void *listen_fn_(UNUSED_ATTR void *context) {
     LOG_ERROR(LOG_TAG, "%s unable to listen: %s", __func__, strerror(errno));
     goto cleanup;
   }
+#endif
+
+  if (local_socket_create() != -1) {
+    if (listen_socket_local_ > fd_max) {
+      fd_max = listen_socket_local_;
+    }
+    FD_SET(listen_socket_local_, &sock_fds);
+  }
+
+  if (fd_max == -1) {
+    LOG_ERROR(LOG_TAG, "%s No sockets to wait for conn..", __func__);
+    return NULL;
+  }
 
   for (;;) {
-    int client_socket;
-    OSI_NO_INTR(client_socket = accept(listen_socket_, NULL, NULL));
-    if (client_socket == -1) {
-      if (errno == EINVAL || errno == EBADF) {
-        break;
+    int client_socket = -1;
+
+    LOG_DEBUG(LOG_TAG, "waiting for client connection");
+
+    if ((retval = select(fd_max + 1, &sock_fds, NULL, NULL, NULL)) == -1) {
+      LOG_ERROR(LOG_TAG, "%s select failed %s", __func__, strerror(errno));
+      goto cleanup;
+    }
+
+    if ((listen_socket_ != -1) && FD_ISSET(listen_socket_, &sock_fds)) {
+      client_socket = accept(listen_socket_, NULL, NULL);
+      if (client_socket == -1) {
+        if (errno == EINVAL || errno == EBADF) {
+          LOG_WARN(LOG_TAG, "%s error accepting TCP socket: %s", __func__, strerror(errno));
+          break;
+        }
+        LOG_WARN(LOG_TAG, "%s error accepting TCP socket: %s", __func__, strerror(errno));
+        continue;
+      }
+    } else if ((listen_socket_local_ != -1) && FD_ISSET(listen_socket_local_, &sock_fds)){
+      struct sockaddr_un cliaddr;
+      int length;
+
+      client_socket = accept(listen_socket_local_, (struct sockaddr *)&cliaddr, (socklen_t *)&length);
+      if (client_socket == -1) {
+        if (errno == EINVAL || errno == EBADF) {
+          LOG_WARN(LOG_TAG, "%s error accepting LOCAL socket: %s", __func__, strerror(errno));
+          break;
+        }
+        LOG_WARN(LOG_TAG, "%s error accepting LOCAL socket: %s", __func__, strerror(errno));
+        continue;
       }
-      LOG_WARN(LOG_TAG, "%s error accepting socket: %s", __func__, strerror(errno));
-      continue;
     }
 
     /* When a new client connects, we have to send the btsnoop file header. This allows
        a decoder to treat the session as a new, valid btsnoop file. */
     pthread_mutex_lock(&client_socket_lock_);
-    safe_close_(&client_socket_);
-    client_socket_ = client_socket;
-
-    OSI_NO_INTR(send(client_socket_, "btsnoop\0\0\0\0\1\0\0\x3\xea", 16, 0));
+    safe_close_(&client_socket_btsnoop);
+    client_socket_btsnoop = client_socket;
+    send(client_socket_btsnoop, "btsnoop\0\0\0\0\1\0\0\x3\xea", 16, 0);
     pthread_mutex_unlock(&client_socket_lock_);
+
+    FD_ZERO(&sock_fds);
+    if(listen_socket_ != -1) {
+      FD_SET(listen_socket_, &sock_fds);
+    }
+    if(listen_socket_local_ != -1) {
+        FD_SET(listen_socket_local_, &sock_fds);
+    }
   }
 
 cleanup:
   safe_close_(&listen_socket_);
+  safe_close_(&listen_socket_local_);
   return NULL;
 }
 
index b5278ce..a7bc110 100644 (file)
@@ -32,6 +32,7 @@ static const char STACK_CONFIG_MODULE[] = "stack_config_module";
 typedef struct {
   const char *(*get_btsnoop_log_path)(void);
   bool (*get_btsnoop_turned_on)(void);
+  void (*get_btsnoop_ext_options)(bool *hci_ext_dump_enabled, bool *btsnoop_conf_from_file);
   bool (*get_btsnoop_should_save_last)(void);
   bool (*get_trace_config_enabled)(void);
   bool (*get_pts_secure_only_mode)(void);
index 109042f..c7842b7 100644 (file)
@@ -27,6 +27,8 @@
 
 const char *BTSNOOP_LOG_PATH_KEY = "BtSnoopFileName";
 const char *BTSNOOP_TURNED_ON_KEY = "BtSnoopLogOutput";
+const char *BTSNOOP_EXT_DUMP_KEY = "BtSnoopExtDump";
+const char *BTSNOOP_CONFIG_FROM_FILE_KEY = "BtSnoopConfigFromFile";
 const char *BTSNOOP_SHOULD_SAVE_LAST_KEY = "BtSnoopSaveLog";
 const char *TRACE_CONFIG_ENABLED_KEY = "TraceConf";
 const char *PTS_SECURE_ONLY_MODE = "PTS_SecurePairOnly";
@@ -114,6 +116,11 @@ static int get_pts_smp_failure_case(void) {
   return config_get_int(config, CONFIG_DEFAULT_SECTION, PTS_SMP_FAILURE_CASE_KEY, 0);
 }
 
+static void get_btsnoop_ext_options(bool *hci_ext_dump_enabled, bool *btsnoop_conf_from_file) {
+  *hci_ext_dump_enabled = config_get_bool(config, CONFIG_DEFAULT_SECTION, BTSNOOP_EXT_DUMP_KEY, false);
+  *btsnoop_conf_from_file = config_get_bool(config, CONFIG_DEFAULT_SECTION, BTSNOOP_CONFIG_FROM_FILE_KEY, false);
+}
+
 static config_t *get_all(void) {
   return config;
 }
@@ -121,6 +128,7 @@ static config_t *get_all(void) {
 const stack_config_t interface = {
   get_btsnoop_log_path,
   get_btsnoop_turned_on,
+  get_btsnoop_ext_options,
   get_btsnoop_should_save_last,
   get_trace_config_enabled,
   get_pts_secure_only_mode,
diff --git a/tools/btsnoop_dump/Android.mk b/tools/btsnoop_dump/Android.mk
new file mode 100644 (file)
index 0000000..f114c3f
--- /dev/null
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=     \
+    btsnoop_dump.c
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE_TAGS := debug optional
+
+LOCAL_MODULE:= btsnoop
+
+LOCAL_SHARED_LIBRARIES += libcutils
+
+include $(BUILD_EXECUTABLE)
diff --git a/tools/btsnoop_dump/btsnoop_dump.c b/tools/btsnoop_dump/btsnoop_dump.c
new file mode 100644 (file)
index 0000000..0739462
--- /dev/null
@@ -0,0 +1,363 @@
+/******************************************************************************
+Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of The Linux Foundation nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include <private/android_filesystem_config.h>
+#include <android/log.h>
+
+#include <cutils/log.h>
+
+#define MAX_FILE_SIZE 1024*1024*20
+
+#define LOGD0(t,s) __android_log_write(ANDROID_LOG_DEBUG, t, s)
+
+static int file_descriptor = -1;
+uint32_t file_size = 0;
+
+#define LOCAL_SOCKET_NAME "bthcitraffic"
+#define BTSNOOP_PATH "/data/media/0"
+#define BTSOOP_PORT 8872
+
+//#define __SNOOP_DUMP_DBG__
+
+static void snoop_log(const char *fmt_str, ...)
+{
+    static char buffer[1024];
+    va_list ap;
+
+    va_start(ap, fmt_str);
+    vsnprintf(buffer, 1024, fmt_str, ap);
+    va_end(ap);
+
+    LOGD0("btsnoop_dump: ", buffer);
+}
+
+int btsnoop_file_name (char file_name[256])
+{
+    struct tm *tmp;
+    time_t t;
+    char time_string[64];
+
+    t = time(NULL);
+    tmp = localtime(&t);
+    if (tmp == NULL)
+    {
+        snoop_log("Error : get localtime");
+        return -1;
+    }
+
+    if (strftime(time_string, 64, "%Y%m%d%H%M%S", tmp) == 0)
+    {
+        snoop_log("Error : strftime :");
+        return -1;
+    }
+    snprintf(file_name, 256, BTSNOOP_PATH"/hci_snoop%s.cfa", time_string);
+    return 0;
+}
+
+int snoop_open_file (void)
+{
+    char file_name[2][256];
+    int snoop_files_found = 0;
+    struct DIR* p_dir;
+    struct dirent* p_dirent;
+
+    p_dir = opendir(BTSNOOP_PATH);
+    if(p_dir == NULL)
+    {
+        snoop_log("snoop_log_open: Unable to open the Dir entry\n");
+        file_descriptor = -1;
+        return -1;
+    }
+    while ((p_dirent = readdir(p_dir)) != NULL)
+    {
+        int ret;
+
+        if ((ret = strncmp(p_dirent->d_name, "hci_snoop", strlen("hci_snoop"))) == 0)
+        {
+            snoop_files_found++;
+        }
+        if (snoop_files_found > 2)
+        {
+            snoop_log("snoop_log_open: Error : More than two snoop files : Abort");
+            file_descriptor = -1;
+            return -1;
+        }
+        else if (ret == 0)
+        {
+            strlcpy(file_name[snoop_files_found - 1], p_dirent->d_name, 256);
+#ifdef __SNOOP_DUMP_DBG__
+            snoop_log("snoop_log_open: snoop file found : %s", file_name[snoop_files_found - 1]);
+#endif //__SNOOP_DUMP_DBG__
+        }
+    }
+    closedir(p_dir);
+    if (snoop_files_found == 2)
+    {
+        char del_file[256];
+
+        /* Delete the oldest File */
+        if (strncmp(file_name[0], file_name[1], 256) < 0)
+        {
+            snprintf(del_file, 256, BTSNOOP_PATH"/%s", file_name[0]);
+#ifdef __SNOOP_DUMP_DBG__
+            snoop_log("snoop_log_open: old file to delete : %s", del_file);
+#endif //__SNOOP_DUMP_DBG__
+            unlink(del_file);
+        }
+        else
+        {
+            snprintf(del_file, 256, BTSNOOP_PATH"/%s", file_name[1]);
+#ifdef __SNOOP_DUMP_DBG__
+            snoop_log("snoop_log_open: old file to delete : %s", del_file);
+#endif //__SNOOP_DUMP_DBG__
+            unlink(del_file);
+        }
+    }
+
+    if (btsnoop_file_name(file_name[0]) != 0)
+    {
+        snoop_log("snoop_log_open: error : could not get snoop file name !!");
+        return -1;
+    }
+
+    snoop_log("snoop_log_open: new file : %s", file_name[0]);
+    file_descriptor = open(file_name[0], \
+                              O_WRONLY|O_CREAT|O_TRUNC, \
+                              S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
+    if (file_descriptor == -1)
+    {
+        snoop_log("snoop_log_open: Unable to open snoop log file\n");
+        file_descriptor = -1;
+        return -1;
+    }
+
+    file_size = 0;
+    write(file_descriptor, "btsnoop\0\0\0\0\1\0\0\x3\xea", 16);
+    return 0;
+}
+
+int snoop_connect_to_source (void)
+{
+    struct sockaddr_un serv_addr;
+
+    int sk, ret, retry_count = 0, addr_len;
+
+    snoop_log("snoop_connect_to_source :");
+    /* Create Socket to connect to BT Traffic source*/
+    sk = socket(AF_LOCAL, SOCK_STREAM, 0);
+    if (sk < 0)
+    {
+        snoop_log("Can't create client socket : %s\n", strerror(errno));
+        return -1;
+    }
+    else
+    {
+        memset(&serv_addr, 0, sizeof(serv_addr));
+        serv_addr.sun_family = AF_LOCAL;
+        memcpy(&serv_addr.sun_path[1], LOCAL_SOCKET_NAME, strlen(LOCAL_SOCKET_NAME));
+        addr_len =  strlen(LOCAL_SOCKET_NAME) + 1;
+        addr_len += sizeof(serv_addr.sun_family);
+        do
+        {
+            ret = connect(sk, &serv_addr, addr_len);
+            if (ret < 0)
+            {
+                snoop_log("Can't connect to BT traffic source : %s\n",strerror(errno));
+                retry_count++;
+                sleep (1);
+            }
+        } while((ret < 0) && (retry_count < 10));
+
+        if (ret < 0)
+        {
+            close(sk);
+            return -1;
+        }
+
+        snoop_log("Connected to bthcitraffic : sock fd : %d", sk);
+        return sk;
+    }
+}
+
+int read_block (int sock, unsigned char *pBuf, int len)
+{
+    int bytes_recv = 0, ret;
+    do
+    {
+#ifdef __SNOOP_DUMP_DBG__
+        snoop_log("read_block : waiting to read");
+#endif //__SNOOP_DUMP_DBG__
+
+        ret = recv(sock, &pBuf[bytes_recv], len - bytes_recv, 0);
+#ifdef __SNOOP_DUMP_DBG__
+        snoop_log("read_block : read returned %d", ret);
+#endif //__SNOOP_DUMP_DBG__
+        if ( (ret == -1) && (errno != EAGAIN) )
+        {
+            bytes_recv = ret;
+            snoop_log("Error Packet header : Connection Closed : %s\n", strerror(errno));
+            break;
+        }
+        else if (ret == 0)
+        {
+            snoop_log("Disconnected from bthcitraffic : Exiting...");
+            close (sock);
+            break;
+        }
+        bytes_recv += ret;
+    } while(bytes_recv < len);
+
+#ifdef __SNOOP_DUMP_DBG__
+    snoop_log("bytes read = %d", bytes_recv);
+#endif //__SNOOP_DUMP_DBG__
+    return bytes_recv;
+}
+
+static unsigned char read_buf[1200];
+
+int snoop_process (int sk)
+{
+    int bytes_recv = 0;
+    struct stat st;
+    uint32_t sizeoffile = 0, length;
+
+    if (file_descriptor == -1)
+    {
+        if (snoop_open_file() != 0)
+        {
+            return -1;
+        }
+    }
+
+/*
+    24 Bytes snoop Header
+    Initial 4 bytes have the length of the HCI packet
+    Read 8 bytes which have orignal length and included length
+*/
+    bytes_recv = read_block (sk, &read_buf[0], 8);
+    if ((bytes_recv == 0) || (bytes_recv == -1))
+    {
+        snoop_log("Error in reading the Header : ");
+        return -1;
+    }
+
+    length = read_buf[0] << 24 | read_buf[1] << 16 | read_buf[2] << 8 | read_buf[3];
+
+#if 1
+#ifdef __SNOOP_DUMP_DBG__
+    snoop_log("Length of Frame %ld : byte %0x %0x %0x %0x", length,
+        read_buf[0], read_buf[1], read_buf[2], read_buf[3]);
+
+    snoop_log("File Size = %d", file_size);
+#endif //__SNOOP_DUMP_DBG__
+
+    if (file_size > MAX_FILE_SIZE)
+    {
+        if (file_descriptor != -1)
+        {
+            close(file_descriptor);
+            file_descriptor = -1;
+            if (snoop_open_file() != 0)
+            {
+                return -1;
+            }
+        }
+    }
+#endif
+
+/*
+    Read rest of snoop header(16 Bytes) and HCI Packet
+*/
+    bytes_recv = read_block (sk, &read_buf[8], length + 16);
+    if ((bytes_recv == 0) || (bytes_recv == -1))
+    {
+        snoop_log("Error reading snoop packet : ");
+        return -1;
+    }
+
+    file_size += (bytes_recv + 8);
+
+    write(file_descriptor, read_buf, bytes_recv + 8);
+
+    return 0;
+}
+
+int main (int argc, char * argv[])
+{
+    int sk, ret, bytes_recv;
+
+    snoop_log ("btsnoop dump starting");
+
+    /* set the file creation mask to allow read/write */
+    umask(0111);
+
+    sk = snoop_connect_to_source();
+
+/*
+       16 Bytes : Read and discard snoop file header
+*/
+    bytes_recv = read_block (sk, &read_buf[0], 16);
+    if ((bytes_recv == 0) || (bytes_recv == -1))
+    {
+        snoop_log("Error in reading the snoop file Header : ");
+        return -1;
+    }
+
+    if (snoop_open_file() != 0)
+    {
+        return -1;
+    }
+
+    if (sk != -1)
+    {
+        do
+        {
+            ret = snoop_process(sk);
+        } while(ret != -1);
+    }
+
+    snoop_log("btsnoop dump terminated");
+    return 0;
+}
+