1 /******************************************************************************
2 *
3 * Copyright (C) 2015 Google Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 ******************************************************************************/
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <time.h>
22
23 #include "btcore/include/bdaddr.h"
24 #include "btif/include/btif_debug_conn.h"
25 #include "osi/include/time.h"
26
27 #define NUM_CONNECTION_EVENTS 16
28 #define TEMP_BUFFER_SIZE 30
29
30 typedef struct conn_event_t {
31 uint64_t ts;
32 btif_debug_conn_state_t state;
33 bt_bdaddr_t bda;
34 tGATT_DISCONN_REASON disconnect_reason;
35 } conn_event_t;
36
37 static conn_event_t connection_events[NUM_CONNECTION_EVENTS];
38 static uint8_t current_event = 0;
39
format_ts(const uint64_t ts,char * buffer,int len)40 static char* format_ts(const uint64_t ts, char* buffer, int len) {
41 const uint64_t ms = ts / 1000;
42 const time_t secs = ms / 1000;
43 struct tm* ptm = localtime(&secs);
44
45 char tempbuff[20];
46 strftime(tempbuff, sizeof(tempbuff), "%m-%d %H:%M:%S", ptm);
47 snprintf(buffer, len, "%s.%03u", tempbuff, (uint16_t)(ms % 1000));
48
49 return buffer;
50 }
51
format_state(const btif_debug_conn_state_t state)52 static const char* format_state(const btif_debug_conn_state_t state) {
53 switch (state) {
54 case BTIF_DEBUG_CONNECTED:
55 return "CONNECTED ";
56 case BTIF_DEBUG_DISCONNECTED:
57 return "DISCONNECTED";
58 }
59 return "UNKNOWN";
60 }
61
next_event()62 static void next_event() {
63 ++current_event;
64 if (current_event == NUM_CONNECTION_EVENTS) current_event = 0;
65 }
66
btif_debug_conn_state(const bt_bdaddr_t bda,const btif_debug_conn_state_t state,const tGATT_DISCONN_REASON disconnect_reason)67 void btif_debug_conn_state(const bt_bdaddr_t bda,
68 const btif_debug_conn_state_t state,
69 const tGATT_DISCONN_REASON disconnect_reason) {
70 next_event();
71
72 conn_event_t* evt = &connection_events[current_event];
73 evt->ts = time_gettimeofday_us();
74 evt->state = state;
75 evt->disconnect_reason = disconnect_reason;
76 memcpy(&evt->bda, &bda, sizeof(bt_bdaddr_t));
77 }
78
btif_debug_conn_dump(int fd)79 void btif_debug_conn_dump(int fd) {
80 const uint8_t current_event_local =
81 current_event; // Cache to avoid threading issues
82 uint8_t dump_event = current_event_local;
83 char ts_buffer[TEMP_BUFFER_SIZE] = {0};
84 char name_buffer[TEMP_BUFFER_SIZE] = {0};
85
86 dprintf(fd, "\nConnection Events:\n");
87 if (connection_events[dump_event].ts == 0) dprintf(fd, " None\n");
88
89 while (connection_events[dump_event].ts) {
90 conn_event_t* evt = &connection_events[dump_event];
91 dprintf(fd, " %s %s %s", format_ts(evt->ts, ts_buffer, sizeof(ts_buffer)),
92 format_state(evt->state),
93 bdaddr_to_string(&evt->bda, name_buffer, sizeof(name_buffer)));
94 if (evt->state == BTIF_DEBUG_DISCONNECTED)
95 dprintf(fd, " reason=%d", evt->disconnect_reason);
96 dprintf(fd, "\n");
97
98 // Go to previous event; wrap if needed
99 if (dump_event > 0)
100 --dump_event;
101 else
102 dump_event = NUM_CONNECTION_EVENTS - 1;
103
104 // Check if we dumped all events
105 if (dump_event == current_event_local) break;
106 }
107 }
108