1/******************************************************************************
2 *
3 *  Copyright (C) 2016 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
20#define LOG_TAG "bt_osi_metrics"
21
22extern "C" {
23#include "osi/include/metrics.h"
24
25#include <errno.h>
26
27#include "osi/include/log.h"
28#include "osi/include/osi.h"
29}
30
31#include "osi/src/protos/bluetooth.pb.h"
32
33#include <base/base64.h>
34#include <google/protobuf/text_format.h>
35#include <mutex>
36
37using clearcut::connectivity::A2DPSession;
38using clearcut::connectivity::BluetoothLog;
39using clearcut::connectivity::BluetoothSession;
40using clearcut::connectivity::DeviceInfo;
41using clearcut::connectivity::DeviceInfo_DeviceType;
42using clearcut::connectivity::PairEvent;
43using clearcut::connectivity::ScanEvent;
44using clearcut::connectivity::ScanEvent_ScanTechnologyType;
45using clearcut::connectivity::ScanEvent_ScanEventType;
46using clearcut::connectivity::WakeEvent;
47using clearcut::connectivity::WakeEvent_WakeEventType;
48
49BluetoothLog *pending;
50std::mutex log_lock;
51
52static void lazy_initialize(void) {
53  if (pending == nullptr) {
54    pending = BluetoothLog::default_instance().New();
55  }
56}
57
58void metrics_pair_event(uint32_t disconnect_reason, uint64_t timestamp_ms,
59                        uint32_t device_class, device_type_t device_type) {
60  std::lock_guard<std::mutex> lock(log_lock);
61  lazy_initialize();
62
63  PairEvent *event = pending->add_pair_event();
64
65  DeviceInfo *info = event->mutable_device_paired_with();
66
67  info->set_device_class(device_class);
68
69  DeviceInfo_DeviceType type = DeviceInfo::DEVICE_TYPE_UNKNOWN;
70
71  if (device_type == DEVICE_TYPE_BREDR)
72    type = DeviceInfo::DEVICE_TYPE_BREDR;
73  if (device_type == DEVICE_TYPE_LE)
74    type = DeviceInfo::DEVICE_TYPE_LE;
75  if (device_type == DEVICE_TYPE_DUMO)
76    type = DeviceInfo::DEVICE_TYPE_DUMO;
77
78  info->set_device_type(type);
79
80  event->set_disconnect_reason(disconnect_reason);
81
82  event->set_event_time_millis(timestamp_ms);
83}
84
85void metrics_wake_event(wake_event_type_t type, const char *requestor,
86                        const char *name, uint64_t timestamp_ms) {
87  std::lock_guard<std::mutex> lock(log_lock);
88  lazy_initialize();
89
90  WakeEvent *event = pending->add_wake_event();
91
92  WakeEvent_WakeEventType waketype = WakeEvent::UNKNOWN;
93
94  if (type == WAKE_EVENT_ACQUIRED)
95    waketype = WakeEvent::ACQUIRED;
96  if (type == WAKE_EVENT_RELEASED)
97    waketype = WakeEvent::RELEASED;
98
99  event->set_wake_event_type(waketype);
100
101  if (requestor)
102    event->set_requestor(requestor);
103
104  if (name)
105    event->set_name(name);
106
107  event->set_event_time_millis(timestamp_ms);
108}
109
110void metrics_scan_event(bool start, const char *initator, scan_tech_t type,
111                        uint32_t results, uint64_t timestamp_ms) {
112  std::lock_guard<std::mutex> lock(log_lock);
113  lazy_initialize();
114
115  ScanEvent *event = pending->add_scan_event();
116
117  if (start)
118    event->set_scan_event_type(ScanEvent::SCAN_EVENT_START);
119  else
120    event->set_scan_event_type(ScanEvent::SCAN_EVENT_STOP);
121
122  if (initator)
123    event->set_initiator(initator);
124
125  ScanEvent::ScanTechnologyType scantype = ScanEvent::SCAN_TYPE_UNKNOWN;
126
127  if (type == SCAN_TECH_TYPE_LE)
128    scantype = ScanEvent::SCAN_TECH_TYPE_LE;
129  if (type == SCAN_TECH_TYPE_BREDR)
130    scantype = ScanEvent::SCAN_TECH_TYPE_BREDR;
131  if (type == SCAN_TECH_TYPE_BOTH)
132    scantype = ScanEvent::SCAN_TECH_TYPE_BOTH;
133
134  event->set_scan_technology_type(scantype);
135
136  event->set_number_results(results);
137
138  event->set_event_time_millis(timestamp_ms);
139}
140
141void metrics_a2dp_session(int64_t session_duration_sec,
142                          const char *disconnect_reason,
143                          uint32_t device_class,
144                          int32_t media_timer_min_ms,
145                          int32_t media_timer_max_ms,
146                          int32_t media_timer_avg_ms,
147                          int32_t buffer_overruns_max_count,
148                          int32_t buffer_overruns_total,
149                          float buffer_underruns_average,
150                          int32_t buffer_underruns_count) {
151  std::lock_guard<std::mutex> lock(log_lock);
152  lazy_initialize();
153
154  BluetoothSession *bt_session = pending->add_session();
155
156  // Set connection type: for A2DP it is always BR/EDR
157  BluetoothSession::ConnectionTechnologyType conn_type =
158    BluetoothSession::CONNECTION_TECHNOLOGY_TYPE_BREDR;
159  bt_session->set_connection_technology_type(conn_type);
160
161  bt_session->set_session_duration_sec(session_duration_sec);
162  if (disconnect_reason != NULL)
163    bt_session->set_disconnect_reason(disconnect_reason);
164
165  // Set device: class and type are pre-defined
166  DeviceInfo *info = bt_session->mutable_device_connected_to();
167  info->set_device_class(device_class);
168  info->set_device_type(DeviceInfo::DEVICE_TYPE_BREDR);
169
170  A2DPSession *a2dp_session = bt_session->mutable_a2dp_session();
171  a2dp_session->set_media_timer_min_millis(media_timer_min_ms);
172  a2dp_session->set_media_timer_max_millis(media_timer_max_ms);
173  a2dp_session->set_media_timer_avg_millis(media_timer_avg_ms);
174  a2dp_session->set_buffer_overruns_max_count(buffer_overruns_max_count);
175  a2dp_session->set_buffer_overruns_total(buffer_overruns_total);
176  a2dp_session->set_buffer_underruns_average(buffer_underruns_average);
177  a2dp_session->set_buffer_underruns_count(buffer_underruns_count);
178}
179
180void metrics_write(int fd, bool clear) {
181  log_lock.lock();
182  LOG_DEBUG(LOG_TAG, "%s serializing metrics", __func__);
183  lazy_initialize();
184
185  std::string serialized;
186  if (!pending->SerializeToString(&serialized)) {
187    LOG_ERROR(LOG_TAG, "%s: error serializing metrics", __func__);
188    return;
189  }
190
191  if (clear) {
192    pending->Clear();
193  }
194  log_lock.unlock();
195
196  std::string protoBase64;
197  base::Base64Encode(serialized, &protoBase64);
198
199  ssize_t ret;
200  OSI_NO_INTR(ret = write(fd, protoBase64.c_str(), protoBase64.size()));
201  if (ret == -1) {
202    LOG_ERROR(LOG_TAG, "%s: error writing to dumpsys fd: %s (%d)", __func__,
203              strerror(errno), errno);
204  }
205}
206
207void metrics_print(int fd, bool clear) {
208  log_lock.lock();
209  LOG_DEBUG(LOG_TAG, "%s printing metrics", __func__);
210  lazy_initialize();
211
212  std::string pretty_output;
213  google::protobuf::TextFormat::PrintToString(*pending, &pretty_output);
214
215  if (clear) {
216    pending->Clear();
217  }
218  log_lock.unlock();
219
220  ssize_t ret;
221  OSI_NO_INTR(ret = write(fd, pretty_output.c_str(), pretty_output.size()));
222  if (ret == -1) {
223    LOG_ERROR(LOG_TAG, "%s: error writing to dumpsys fd: %s (%d)", __func__,
224              strerror(errno), errno);
225  }
226}
227