ce452ed66a030a851dee32f9dbf77547068b0c7a
[platform/core/connectivity/bluetooth-frwk.git] / bt-service-adaptation / services / bt-service-battery-monitor.c
1 /*
2  * Bluetooth-frwk
3  *
4  * Copyright (c) 2015 - 2016 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact:  Sudipto Bal <sudipto.bal@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *              http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21
22 #include <glib.h>
23 #include <gio/gio.h>
24 #include <string.h>
25 #include <dlog.h>
26 #include <time.h>
27 #include <sys/time.h>
28
29 #include <oal-event.h>
30
31 #include "bt-service-battery-monitor.h"
32 #include "bt-service-common.h"
33 #include "bt-service-event.h"
34 #include "bt-service-core-adapter.h"
35
36 static struct timeval scan_start;
37 static struct timeval connect_start;
38 static struct timeval app_scan_base;
39 static int scan_cnt = 0;
40 static int connect_cnt = 0;
41 static gboolean is_session_started = FALSE;
42
43 static GSList *scan_app_list = NULL;
44
45 typedef struct {
46         int type;
47         void *data;
48 } bt_service_oal_event_data_t;
49
50 _bt_battery_data_t *current_session_data = NULL;
51
52 static void __bt_bm_add_prev_time(uint32_t scan_time);
53
54 uint32_t static __bt_dm_time_diff_msec(struct timeval prev, struct timeval cur)
55 {
56     return (uint32_t)((cur.tv_sec - prev.tv_sec) * 1000.0f + (cur.tv_usec - prev.tv_usec) / 1000.0f);
57 }
58
59 static void __bt_display_session_data()
60 {
61         BT_DBG("Displaying session data...");
62         BT_DBG("session_start_time = %ld", current_session_data->session_start_time);
63         BT_DBG("session_end_time = %ld", current_session_data->session_end_time);
64         BT_DBG("session_scan_time = %d", current_session_data->session_scan_time);
65         BT_DBG("session_connected_time = %d", current_session_data->session_connected_time);
66 }
67
68 /*After reading data, the function resets it*/
69 int _bt_bm_read_data(_bt_battery_data_t *data)
70 {
71         struct timeval cur_time;
72         uint32_t tx_time = 0;
73         uint32_t rx_time = 0;
74         uint32_t idle_time = 0;
75         uint32_t energy_used = 0;
76         uint16_t scan_time_per_app = 0;
77         int scan_app_cnt = 0;
78
79         BT_DBG("");
80
81         if (data == NULL) {
82                 BT_ERR("Received NULL pointer in argument, returning...");
83                 return BLUETOOTH_ERROR_NO_DATA;
84         }
85
86         if (_bt_get_energy_info(&tx_time, &rx_time,
87                                 &idle_time, &energy_used) != BLUETOOTH_ERROR_NONE) {
88                 BT_ERR("Fail to get energy info");
89                 return BLUETOOTH_ERROR_NOT_SUPPORT;
90         }
91
92         gettimeofday(&cur_time, 0);
93
94         data->tx_time = tx_time;
95         data->rx_time = rx_time;
96         data->idle_time = idle_time;
97
98         data->session_start_time = current_session_data->session_start_time;
99         data->session_end_time = time(NULL);
100         current_session_data->session_start_time = time(NULL);
101         current_session_data->session_end_time = 0;
102
103         data->session_scan_time = current_session_data->session_scan_time;
104         if (scan_cnt) {
105                 data->session_scan_time += (uint16_t)__bt_dm_time_diff_msec(scan_start, cur_time);
106                 gettimeofday(&scan_start, 0);
107         }
108         current_session_data->session_scan_time = 0;
109
110         data->session_connected_time = current_session_data->session_connected_time;
111         if (connect_cnt) {
112                 data->session_connected_time += (uint16_t)__bt_dm_time_diff_msec(connect_start, cur_time);
113                 gettimeofday(&connect_start, 0);
114         }
115         current_session_data->session_connected_time = 0;
116
117         scan_app_cnt = g_slist_length(scan_app_list);
118
119         if (scan_app_cnt > 0) {
120                 scan_time_per_app = (uint32_t)__bt_dm_time_diff_msec(app_scan_base, cur_time) / scan_app_cnt;
121                 __bt_bm_add_prev_time(scan_time_per_app);
122         }
123
124         gettimeofday(&app_scan_base, 0);
125
126         data->atm_list = current_session_data->atm_list;
127         if (data->atm_list == NULL) {
128                 BT_DBG("No data transaction in this session");
129                 return BLUETOOTH_ERROR_NONE;
130         }
131
132         BT_DBG("App-wise data transaction details");
133         for (GSList *l = data->atm_list; l != NULL; l = g_slist_next(l)) {
134                 _bt_battery_app_data_t *t = (_bt_battery_app_data_t *)(l->data);
135                 BT_DBG("%ld %ld %d %d %u", (long int)(t->uid), (long int)(t->pid), t->rx_bytes, t->tx_bytes, t->time);
136         }
137
138         current_session_data->atm_list = NULL;
139         return BLUETOOTH_ERROR_NONE;
140 }
141
142 static GSList* is_app_present(GSList *start, uid_t uid, pid_t pid)
143 {
144         GSList *l = NULL;
145         _bt_battery_app_data_t *t;
146         BT_INFO("Checking Memory location %p", start);
147         for (l=start; l != NULL; l = g_slist_next(l)) {
148                 t = (_bt_battery_app_data_t *)(l->data);
149                 if (t->uid == uid && t->pid == pid) {
150                         BT_INFO("App details already exist");
151                         return l;
152                 }
153         }
154         return NULL;
155 }
156
157 void _bt_bm_add_transaction_details(uid_t uid, pid_t pid, int value, data_transaction_type_e type)
158 {
159         if (current_session_data == NULL) {
160                 BT_ERR("Session in progress but data structure is not initialized"); //error handling
161                 return;
162         }
163         GSList *t = is_app_present(current_session_data->atm_list, uid, pid);
164         _bt_battery_app_data_t *app_data = NULL;
165
166         if (t == NULL) {
167                 BT_INFO("Match not found, adding new node...");
168                 app_data = g_malloc0(sizeof(_bt_battery_app_data_t));
169                 app_data->uid = uid;
170                 app_data->pid = pid;
171                 if (type == RX_DATA)
172                         app_data->rx_bytes = value;
173                 else if (type == TX_DATA)
174                         app_data->tx_bytes = value;
175                 else
176                         app_data->time = value;
177                 current_session_data->atm_list = g_slist_append(current_session_data->atm_list, app_data);
178         }
179         else {
180                 BT_INFO("Match found, updating existing node...");
181                 app_data = (_bt_battery_app_data_t *)(t->data);
182                 if (type == RX_DATA)
183                         app_data->rx_bytes += value;
184                 else if (type == TX_DATA)
185                         app_data->tx_bytes += value;
186                 else
187                         app_data->time += value;
188         }
189 }
190
191 void _bt_start_session_time()
192 {
193         if (is_session_started == FALSE) {
194                 BT_DBG("Bt session starting...");
195                 is_session_started = TRUE;
196                 current_session_data = g_malloc0(sizeof(_bt_battery_data_t));
197                 current_session_data->session_start_time = time(NULL);
198                 current_session_data->session_end_time = 0;
199                 current_session_data->session_connected_time = 0;
200                 current_session_data->session_scan_time = 0;
201                 current_session_data->atm_list = NULL;
202         } else {
203                 if (current_session_data == NULL)
204                         BT_ERR("Session in progress but data structure is not initialized"); //error handling
205                 else
206                         BT_DBG("Bt session already in progress... Returning");
207         }
208 }
209
210 void _bt_stop_session_time()
211 {
212         if (is_session_started == FALSE) {
213                 BT_DBG("BT session not in progress... Returning"); //error handling
214                 return;
215         }
216         BT_DBG("Bt session ending...");
217         is_session_started = FALSE;
218         current_session_data->session_end_time = time(NULL);
219         __bt_display_session_data();
220 }
221
222 /* 1 app can operate the regacy and ble scan at the same time */
223 static GSList* __is_scan_app_present(GSList *start, bt_bm_scan_type_e type, uid_t uid, pid_t pid)
224 {
225         GSList *l = NULL;
226         bt_bm_scan_info_t *t;
227
228         for (l = start; l != NULL; l = g_slist_next(l)) {
229                 t = (bt_bm_scan_info_t *)(l->data);
230
231                 /* Find the regacy scan app for Inquiry stop */
232                 if (type == SCAN_REGACY && t->type != SCAN_LE) {
233                         BT_INFO("app already exist");
234                         return l;
235                 }
236
237                 if (t->uid == uid && t->pid == pid) {
238                         BT_INFO("app already exist");
239                         return l;
240                 }
241         }
242         return NULL;
243 }
244
245 static void __bt_bm_add_prev_time(uint32_t scan_time)
246 {
247         GSList *l = NULL;
248         bt_bm_scan_info_t *t;
249
250         for (l = scan_app_list; l != NULL; l = g_slist_next(l)) {
251                 t = (bt_bm_scan_info_t *)(l->data);
252                 _bt_bm_add_transaction_details(t->uid, t->pid, scan_time, TIME_DATA);
253         }
254 }
255
256 /* 1 regacy scan is only allowed in the platform
257  * BLE scan is allowed for many apps
258 */
259 /* When a app is added, we should add and reset the time. */
260 void _bt_bm_add_scan_app(bt_bm_scan_type_e type, uid_t uid, pid_t pid)
261 {
262         bt_bm_scan_info_t *scan_info = NULL;
263         GSList *app_list = NULL;
264         int app_cnt = 0;
265         uint32_t scan_time_per_app = 0;
266
267         BT_DBG("Scan type: %d", type);
268
269         if (scan_app_list) {
270                 app_cnt = g_slist_length(scan_app_list);
271                 app_list = __is_scan_app_present(scan_app_list, SCAN_BOTH, uid, pid);
272         }
273
274         if (app_list) {
275                 /* app try to scan both Regacy and LE */
276                 scan_info = (bt_bm_scan_info_t *)(app_list->data);
277
278                 if (scan_info == NULL) {
279                         BT_ERR("Can't get the scan info");
280                         return;
281                 }
282
283                 BT_DBG("Previous type: %d", scan_info->type);
284
285                 if (scan_info->type == type) {
286                         BT_ERR("Same scan type is doing");
287                         return;
288                 }
289
290                 scan_info->type = SCAN_BOTH;
291         } else {
292                 scan_info = g_malloc0(sizeof(bt_bm_scan_info_t));
293                 scan_info->uid = uid;
294                 scan_info->pid = pid;
295                 scan_info->type = type;
296
297                 if (app_cnt > 0) {
298                         struct timeval cur_time;
299
300                         gettimeofday(&cur_time, 0);
301
302                         scan_time_per_app = (uint32_t)(__bt_dm_time_diff_msec(app_scan_base, cur_time)) / app_cnt;
303                         __bt_bm_add_prev_time(scan_time_per_app);
304
305                         /* Update the base time */
306                         gettimeofday(&app_scan_base, 0);
307                 }
308
309                 scan_app_list = g_slist_append(scan_app_list, scan_info);
310         }
311 }
312
313 /* When a app is removed, we should add and reset the time. */
314 void _bt_bm_remove_scan_app(bt_bm_scan_type_e type, uid_t uid, pid_t pid)
315 {
316         bt_bm_scan_info_t *scan_info = NULL;
317         GSList *app_list = NULL;
318         int app_cnt = 0;
319         uint32_t scan_time_per_app = 0;
320
321         BT_DBG("Scan type: %d", type);
322
323         if (scan_app_list == NULL) {
324                 BT_ERR("No scan app in list");
325                 return;
326         }
327
328         app_cnt = g_slist_length(scan_app_list);
329
330         if (app_cnt == 0) {
331                 BT_ERR("No scan app in list");
332                 return;
333         }
334
335         app_list = __is_scan_app_present(scan_app_list, type, uid, pid);
336
337         if (app_list) {
338                 struct timeval cur_time;
339
340                 scan_info = (bt_bm_scan_info_t *)(app_list->data);
341
342                 if (scan_info->type == SCAN_BOTH) {
343                         scan_info->type = (scan_info->type == SCAN_REGACY) ? SCAN_LE : SCAN_REGACY;
344                         return;
345                 }
346
347                 gettimeofday(&cur_time, 0);
348
349                 scan_time_per_app = (uint32_t)(__bt_dm_time_diff_msec(app_scan_base, cur_time)) / app_cnt;
350                 __bt_bm_add_prev_time(scan_time_per_app);
351
352                 /* Update the base time */
353                 gettimeofday(&app_scan_base, 0);
354
355                 scan_app_list = g_slist_remove(scan_app_list, scan_info);
356
357                 g_free(scan_info);
358         }
359 }
360
361 void _bt_start_scan_time()
362 {
363         if (current_session_data != NULL) {
364                 if (scan_cnt == 0) {
365                         BT_DBG("Starting scan time");
366                         gettimeofday(&scan_start, 0);
367                         gettimeofday(&app_scan_base, 0);
368                 }
369                 scan_cnt++;
370         } else {
371                 BT_ERR("Data structure uninitialized"); //error handling
372         }
373 }
374
375 void _bt_stop_scan_time()
376 {
377         if (scan_cnt == 0 || current_session_data == NULL)
378                 BT_ERR("Error encountered, returning..."); //error handling
379         else {
380                 scan_cnt--;
381                 if(scan_cnt == 0) {
382                         struct timeval cur_time;
383
384                         gettimeofday(&cur_time, 0);
385                         current_session_data->session_scan_time += (uint16_t)(__bt_dm_time_diff_msec(scan_start, cur_time));
386                         gettimeofday(&scan_start, 0);
387                         gettimeofday(&app_scan_base, 0);
388                 }
389         }
390 }
391
392 void _bt_start_connect_time()
393 {
394         if (current_session_data != NULL) {
395                 if (connect_cnt == 0) {
396                         BT_DBG("Starting connect time");
397                         gettimeofday(&connect_start, 0);
398                 }
399                 connect_cnt++;
400         }
401         else {
402                 BT_ERR("Data structure uninitialized"); //error handling
403         }
404 }
405
406 void _bt_stop_connect_time()
407 {
408         if(connect_cnt == 0 || current_session_data == NULL) {
409                 BT_ERR("Error encountered, returning..."); //error handling
410         }
411         else {
412                 connect_cnt--;
413                 if(connect_cnt == 0) {
414                         struct timeval cur_time;
415
416                         gettimeofday(&cur_time, 0);
417                         current_session_data->session_connected_time += (uint16_t)(__bt_dm_time_diff_msec(connect_start, cur_time));
418                 }
419         }
420 }
421
422 static void _bt_notify_battery_data(void)
423 {
424         BT_INFO("+");
425         _bt_battery_data_t *data = NULL;
426         int result;
427
428         data = g_new0(_bt_battery_data_t, 1);
429         result = _bt_bm_read_data(data);
430         GVariant *out_var = NULL, *param = NULL;
431         GArray *info = NULL;
432
433         if (result != BLUETOOTH_ERROR_NONE) {
434                 BT_ERR("Battery data not collected");
435         }
436         else {
437                 bt_battery_dbus_data_t dbus_data;
438                 memset(&dbus_data, 0, sizeof(bt_battery_dbus_data_t));
439                 dbus_data.session_start_time = data->session_start_time;
440                 dbus_data.session_end_time = data->session_end_time;
441                 dbus_data.session_scan_time = data->session_scan_time;
442                 dbus_data.session_connected_time = data->session_connected_time;
443                 dbus_data.tx_time = data->tx_time;
444                 dbus_data.rx_time = data->rx_time;
445                 dbus_data.idle_time = data->idle_time;
446
447                 /*Populating app data*/
448                 int n = 0;
449                 for (GSList *l = data->atm_list; l != NULL; l = g_slist_next(l)) {
450                         bt_battery_app_data *t = (bt_battery_app_data *)(l->data);
451                         memcpy(&dbus_data.app_data[n], t, sizeof(bt_battery_app_data));
452                         n++;
453                 }
454                 dbus_data.num_app = n;
455
456                 info = g_array_new(FALSE, FALSE, sizeof(gchar));
457                 g_array_append_vals(info, &dbus_data, sizeof(bt_battery_dbus_data_t));
458
459                 out_var = g_variant_new_from_data((const GVariantType *)"ay",
460                                 info->data, info->len,
461                                 TRUE, NULL, NULL);
462         }
463         param = g_variant_new("(iv)", result, out_var);
464         _bt_send_event(BT_ADAPTER_EVENT,
465                 BLUETOOTH_EVENT_DISABLED_BATTERY_DATA,
466                 param);
467
468         g_slist_free(data->atm_list);
469         g_free(data);
470         g_array_free(info, TRUE);
471         BT_INFO("-");
472 }
473
474 void _bt_bm_event_handler(gpointer data)
475 {
476         bt_service_oal_event_data_t *oal_event = data;
477         int event_type = oal_event->type;
478
479         switch(event_type) {
480         case OAL_EVENT_ADAPTER_ENABLED:
481                 BT_DBG("Handling Adapter Enabled");
482                 _bt_start_session_time();
483                 break;
484         case OAL_EVENT_ADAPTER_DISABLED:
485                 BT_DBG("Handling Adapter Disabled");
486                 _bt_stop_session_time();
487                 _bt_notify_battery_data();
488                 break;
489         case OAL_EVENT_ADAPTER_INQUIRY_STARTED:
490         case OAL_EVENT_BLE_DISCOVERY_STARTED:
491                 BT_DBG("Handling Adapter Discovery Start");
492                 _bt_start_scan_time();
493                 break;
494         case OAL_EVENT_ADAPTER_INQUIRY_FINISHED:
495                 /* Remove the regacy scan app */
496                 _bt_bm_remove_scan_app(SCAN_REGACY, 0, 0);
497
498                 _bt_stop_scan_time();
499                 break;
500         case OAL_EVENT_BLE_DISCOVERY_STOPPED:
501                 BT_DBG("Handling Adapter Discovery Stop");
502                 _bt_stop_scan_time();
503                 break;
504         case OAL_EVENT_SOCKET_OUTGOING_CONNECTED:
505         case OAL_EVENT_GATTC_CONNECTION_COMPLETED:
506                 BT_DBG("Handling Connection Start");
507                 _bt_start_connect_time();
508                 break;
509         case OAL_EVENT_SOCKET_DISCONNECTED:
510         case OAL_EVENT_GATTC_DISCONNECTION_COMPLETED:
511                 BT_DBG("Handling Connection Stop");
512                 _bt_stop_connect_time();
513                 break;
514         default:
515                 BT_DBG("The event is not currently being handled");
516         }
517 }