5133774d407915320c12acda601add13d6ec2886
[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 #include <vconf.h>
29
30 #include <oal-event.h>
31
32 #include "bt-service-battery-monitor.h"
33 #include "bt-service-common.h"
34 #include "bt-service-event.h"
35 #include "bt-service-core-adapter.h"
36
37 /* Avoid the build error related to vconf.h's dependency */
38 #ifndef VCONFKEY_BATTERY_MONITOR_STATUS
39 #define VCONFKEY_BATTERY_MONITOR_STATUS "db/bluetooth/bmstatus"
40 #endif
41
42 static struct timeval scan_start;
43 static struct timeval connect_start;
44 static struct timeval app_scan_base;
45 static int scan_cnt = 0;
46 static int connect_cnt = 0;
47 static gboolean is_session_started = FALSE;
48
49 static GSList *scan_app_list = NULL;
50
51 typedef struct {
52         int type;
53         void *data;
54 } bt_service_oal_event_data_t;
55
56 _bt_battery_data_t *current_session_data = NULL;
57
58 static void __bt_bm_add_prev_time(uint32_t scan_time);
59 static int __bt_start_session_time(void);
60
61 uint32_t static __bt_dm_time_diff_msec(struct timeval prev, struct timeval cur)
62 {
63     return (uint32_t)((cur.tv_sec - prev.tv_sec) * 1000.0f + (cur.tv_usec - prev.tv_usec) / 1000.0f);
64 }
65
66 static void __bt_display_session_data()
67 {
68         BT_DBG("Displaying session data...");
69
70         if (current_session_data == NULL) {
71                 BT_ERR("Session in progress but data structure is not initialized");
72                 return;
73         }
74
75         BT_DBG("session_start_time = %ld", current_session_data->session_start_time);
76         BT_DBG("session_end_time = %ld", current_session_data->session_end_time);
77         BT_DBG("session_scan_time = %d", current_session_data->session_scan_time);
78         BT_DBG("session_connected_time = %d", current_session_data->session_connected_time);
79 }
80
81 /*After reading data, the function resets it*/
82 int _bt_bm_read_data(_bt_battery_data_t *data)
83 {
84         struct timeval cur_time;
85         uint32_t tx_time = 0;
86         uint32_t rx_time = 0;
87         uint32_t idle_time = 0;
88         uint32_t energy_used = 0;
89         uint16_t scan_time_per_app = 0;
90         int scan_app_cnt = 0;
91
92         BT_DBG("");
93
94         if (data == NULL) {
95                 BT_ERR("Received NULL pointer in argument, returning...");
96                 return BLUETOOTH_ERROR_NO_DATA;
97         }
98
99         if (_bt_get_energy_info(&tx_time, &rx_time,
100                                 &idle_time, &energy_used) != BLUETOOTH_ERROR_NONE) {
101                 BT_ERR("Fail to get energy info");
102                 return BLUETOOTH_ERROR_NOT_SUPPORT;
103         }
104
105         if (is_session_started == FALSE) {
106                 if (__bt_start_session_time() != BLUETOOTH_ERROR_NONE) {
107                         BT_ERR("Fail to start session time");
108                         return BLUETOOTH_ERROR_NOT_SUPPORT;
109                 }
110         }
111
112         if (current_session_data == NULL) {
113                 BT_ERR("Session in progress but data structure is not initialized");
114                 return BLUETOOTH_ERROR_INTERNAL;
115         }
116
117         gettimeofday(&cur_time, 0);
118
119         data->tx_time = tx_time;
120         data->rx_time = rx_time;
121         data->idle_time = idle_time;
122
123         data->session_start_time = current_session_data->session_start_time;
124         data->session_end_time = time(NULL);
125         current_session_data->session_start_time = time(NULL);
126         current_session_data->session_end_time = 0;
127
128         data->session_scan_time = current_session_data->session_scan_time;
129         if (scan_cnt) {
130                 data->session_scan_time += (uint16_t)__bt_dm_time_diff_msec(scan_start, cur_time);
131                 gettimeofday(&scan_start, 0);
132         }
133         current_session_data->session_scan_time = 0;
134
135         data->session_connected_time = current_session_data->session_connected_time;
136         if (connect_cnt) {
137                 data->session_connected_time += (uint16_t)__bt_dm_time_diff_msec(connect_start, cur_time);
138                 gettimeofday(&connect_start, 0);
139         }
140         current_session_data->session_connected_time = 0;
141
142         scan_app_cnt = g_slist_length(scan_app_list);
143
144         if (scan_app_cnt > 0) {
145                 scan_time_per_app = (uint32_t)__bt_dm_time_diff_msec(app_scan_base, cur_time) / scan_app_cnt;
146                 __bt_bm_add_prev_time(scan_time_per_app);
147         }
148
149         gettimeofday(&app_scan_base, 0);
150
151         data->atm_list = current_session_data->atm_list;
152         if (data->atm_list == NULL) {
153                 BT_DBG("No data transaction in this session");
154                 return BLUETOOTH_ERROR_NONE;
155         }
156
157         BT_DBG("App-wise data transaction details");
158         for (GSList *l = data->atm_list; l != NULL; l = g_slist_next(l)) {
159                 _bt_battery_app_data_t *t = (_bt_battery_app_data_t *)(l->data);
160                 BT_DBG("%ld %ld %d %d %u", (long int)(t->uid), (long int)(t->pid), t->rx_bytes, t->tx_bytes, t->time);
161         }
162
163         current_session_data->atm_list = NULL;
164         return BLUETOOTH_ERROR_NONE;
165 }
166
167 static GSList* is_app_present(GSList *start, uid_t uid, pid_t pid)
168 {
169         GSList *l = NULL;
170         _bt_battery_app_data_t *t;
171         BT_INFO("Checking Memory location %p", start);
172         for (l=start; l != NULL; l = g_slist_next(l)) {
173                 t = (_bt_battery_app_data_t *)(l->data);
174                 if (t->uid == uid && t->pid == pid) {
175                         BT_INFO("App details already exist");
176                         return l;
177                 }
178         }
179         return NULL;
180 }
181
182 void _bt_bm_add_transaction_details(uid_t uid, pid_t pid, int value, data_transaction_type_e type)
183 {
184         if (is_session_started == FALSE)
185                 __bt_start_session_time();
186
187         if (current_session_data == NULL) {
188                 BT_ERR("Session in progress but data structure is not initialized");
189                 return;
190         }
191         GSList *t = is_app_present(current_session_data->atm_list, uid, pid);
192         _bt_battery_app_data_t *app_data = NULL;
193
194         if (t == NULL) {
195                 BT_INFO("Match not found, adding new node...");
196                 app_data = g_malloc0(sizeof(_bt_battery_app_data_t));
197                 app_data->uid = uid;
198                 app_data->pid = pid;
199                 if (type == RX_DATA)
200                         app_data->rx_bytes = value;
201                 else if (type == TX_DATA)
202                         app_data->tx_bytes = value;
203                 else
204                         app_data->time = value;
205                 current_session_data->atm_list = g_slist_append(current_session_data->atm_list, app_data);
206         }
207         else {
208                 BT_INFO("Match found, updating existing node...");
209                 app_data = (_bt_battery_app_data_t *)(t->data);
210                 if (type == RX_DATA)
211                         app_data->rx_bytes += value;
212                 else if (type == TX_DATA)
213                         app_data->tx_bytes += value;
214                 else
215                         app_data->time += value;
216         }
217 }
218
219 static int __bt_start_session_time(void)
220 {
221         int state = 0;
222
223         if (is_session_started == TRUE) {
224                 BT_ERR("Session is already started");
225                 return BLUETOOTH_ERROR_ALREADY_INITIALIZED;
226         }
227
228         if (vconf_get_bool(VCONFKEY_BATTERY_MONITOR_STATUS, &state) != 0) {
229                 BT_ERR("vconf_get_bool failed");
230                 return BLUETOOTH_ERROR_INTERNAL;
231         }
232
233         if (state == 0) {
234                 BT_ERR("Battery is not monitoring in now");
235                 return BLUETOOTH_ERROR_NOT_SUPPORT;
236         }
237
238         BT_DBG("Bt session starting...");
239         is_session_started = TRUE;
240
241         if (current_session_data == NULL)
242                 current_session_data = g_malloc0(sizeof(_bt_battery_data_t));
243
244         current_session_data->session_start_time = time(NULL);
245         current_session_data->session_end_time = 0;
246         current_session_data->session_connected_time = 0;
247         current_session_data->session_scan_time = 0;
248         current_session_data->atm_list = NULL;
249
250         return BLUETOOTH_ERROR_NONE;
251 }
252
253 void _bt_stop_session_time(void)
254 {
255         if (is_session_started == FALSE) {
256                 BT_DBG("BT session not in progress... Returning");
257                 return;
258         }
259
260         BT_DBG("Bt session ending...");
261         is_session_started = FALSE;
262
263         if (current_session_data == NULL) {
264                 BT_ERR("Session in progress but data structure is not initialized");
265                 return;
266         }
267
268         current_session_data->session_end_time = time(NULL);
269         __bt_display_session_data();
270 }
271
272 /* 1 app can operate the regacy and ble scan at the same time */
273 static GSList* __is_scan_app_present(GSList *start, bt_bm_scan_type_e type, uid_t uid, pid_t pid)
274 {
275         GSList *l = NULL;
276         bt_bm_scan_info_t *t;
277
278         for (l = start; l != NULL; l = g_slist_next(l)) {
279                 t = (bt_bm_scan_info_t *)(l->data);
280
281                 /* Find the regacy scan app for Inquiry stop */
282                 if (type == SCAN_REGACY && t->type != SCAN_LE) {
283                         BT_INFO("app already exist");
284                         return l;
285                 }
286
287                 if (t->uid == uid && t->pid == pid) {
288                         BT_INFO("app already exist");
289                         return l;
290                 }
291         }
292         return NULL;
293 }
294
295 static void __bt_bm_add_prev_time(uint32_t scan_time)
296 {
297         GSList *l = NULL;
298         bt_bm_scan_info_t *t;
299
300         for (l = scan_app_list; l != NULL; l = g_slist_next(l)) {
301                 t = (bt_bm_scan_info_t *)(l->data);
302                 _bt_bm_add_transaction_details(t->uid, t->pid, scan_time, TIME_DATA);
303         }
304 }
305
306 /* 1 regacy scan is only allowed in the platform
307  * BLE scan is allowed for many apps
308 */
309 /* When a app is added, we should add and reset the time. */
310 void _bt_bm_add_scan_app(bt_bm_scan_type_e type, uid_t uid, pid_t pid)
311 {
312         bt_bm_scan_info_t *scan_info = NULL;
313         GSList *app_list = NULL;
314         int app_cnt = 0;
315         uint32_t scan_time_per_app = 0;
316
317         BT_DBG("Scan type: %d", type);
318
319         if (scan_app_list) {
320                 app_cnt = g_slist_length(scan_app_list);
321                 app_list = __is_scan_app_present(scan_app_list, SCAN_BOTH, uid, pid);
322         }
323
324         if (app_list) {
325                 /* app try to scan both Regacy and LE */
326                 scan_info = (bt_bm_scan_info_t *)(app_list->data);
327
328                 if (scan_info == NULL) {
329                         BT_ERR("Can't get the scan info");
330                         return;
331                 }
332
333                 BT_DBG("Previous type: %d", scan_info->type);
334
335                 if (scan_info->type == type) {
336                         BT_ERR("Same scan type is doing");
337                         return;
338                 }
339
340                 scan_info->type = SCAN_BOTH;
341         } else {
342                 scan_info = g_malloc0(sizeof(bt_bm_scan_info_t));
343                 scan_info->uid = uid;
344                 scan_info->pid = pid;
345                 scan_info->type = type;
346
347                 if (app_cnt > 0) {
348                         struct timeval cur_time;
349
350                         gettimeofday(&cur_time, 0);
351
352                         scan_time_per_app = (uint32_t)(__bt_dm_time_diff_msec(app_scan_base, cur_time)) / app_cnt;
353                         __bt_bm_add_prev_time(scan_time_per_app);
354
355                         /* Update the base time */
356                         gettimeofday(&app_scan_base, 0);
357                 }
358
359                 scan_app_list = g_slist_append(scan_app_list, scan_info);
360         }
361 }
362
363 /* When a app is removed, we should add and reset the time. */
364 void _bt_bm_remove_scan_app(bt_bm_scan_type_e type, uid_t uid, pid_t pid)
365 {
366         bt_bm_scan_info_t *scan_info = NULL;
367         GSList *app_list = NULL;
368         int app_cnt = 0;
369         uint32_t scan_time_per_app = 0;
370
371         BT_DBG("Scan type: %d", type);
372
373         if (scan_app_list == NULL) {
374                 BT_ERR("No scan app in list");
375                 return;
376         }
377
378         app_cnt = g_slist_length(scan_app_list);
379
380         if (app_cnt == 0) {
381                 BT_ERR("No scan app in list");
382                 return;
383         }
384
385         app_list = __is_scan_app_present(scan_app_list, type, uid, pid);
386
387         if (app_list) {
388                 struct timeval cur_time;
389
390                 scan_info = (bt_bm_scan_info_t *)(app_list->data);
391
392                 if (scan_info->type == SCAN_BOTH) {
393                         scan_info->type = (scan_info->type == SCAN_REGACY) ? SCAN_LE : SCAN_REGACY;
394                         return;
395                 }
396
397                 gettimeofday(&cur_time, 0);
398
399                 scan_time_per_app = (uint32_t)(__bt_dm_time_diff_msec(app_scan_base, cur_time)) / app_cnt;
400                 __bt_bm_add_prev_time(scan_time_per_app);
401
402                 /* Update the base time */
403                 gettimeofday(&app_scan_base, 0);
404
405                 scan_app_list = g_slist_remove(scan_app_list, scan_info);
406
407                 g_free(scan_info);
408         }
409 }
410
411 void _bt_start_scan_time()
412 {
413         if (is_session_started == FALSE) {
414                 if (__bt_start_session_time() != BLUETOOTH_ERROR_NONE) {
415                         BT_ERR("Fail to start session time");
416                         return;
417                 }
418         }
419
420         if (current_session_data != NULL) {
421                 if (scan_cnt == 0) {
422                         BT_DBG("Starting scan time");
423                         gettimeofday(&scan_start, 0);
424                         gettimeofday(&app_scan_base, 0);
425                 }
426                 scan_cnt++;
427         } else {
428                 BT_ERR("Data structure uninitialized");
429         }
430 }
431
432 void _bt_stop_scan_time()
433 {
434         if (is_session_started == FALSE) {
435                 if (__bt_start_session_time() != BLUETOOTH_ERROR_NONE) {
436                         BT_ERR("Fail to start session time");
437                         return;
438                 }
439         }
440
441         if (scan_cnt == 0 || current_session_data == NULL)
442                 BT_ERR("Error encountered, returning...");
443         else {
444                 scan_cnt--;
445                 if(scan_cnt == 0) {
446                         struct timeval cur_time;
447
448                         gettimeofday(&cur_time, 0);
449                         current_session_data->session_scan_time += (uint16_t)(__bt_dm_time_diff_msec(scan_start, cur_time));
450                         gettimeofday(&scan_start, 0);
451                         gettimeofday(&app_scan_base, 0);
452                 }
453         }
454 }
455
456 void _bt_start_connect_time()
457 {
458         if (is_session_started == FALSE) {
459                 if (__bt_start_session_time() != BLUETOOTH_ERROR_NONE) {
460                         BT_ERR("Fail to start session time");
461                         return;
462                 }
463         }
464
465         if (current_session_data != NULL) {
466                 if (connect_cnt == 0) {
467                         BT_DBG("Starting connect time");
468                         gettimeofday(&connect_start, 0);
469                 }
470                 connect_cnt++;
471         }
472         else {
473                 BT_ERR("Data structure uninitialized");
474         }
475 }
476
477 void _bt_stop_connect_time()
478 {
479         if (is_session_started == FALSE) {
480                 if (__bt_start_session_time() != BLUETOOTH_ERROR_NONE) {
481                         BT_ERR("Fail to start session time");
482                         return;
483                 }
484         }
485
486         if(connect_cnt == 0 || current_session_data == NULL) {
487                 BT_ERR("Error encountered, returning...");
488         } else {
489                 connect_cnt--;
490                 if(connect_cnt == 0) {
491                         struct timeval cur_time;
492
493                         gettimeofday(&cur_time, 0);
494                         current_session_data->session_connected_time += (uint16_t)(__bt_dm_time_diff_msec(connect_start, cur_time));
495                 }
496         }
497 }
498
499 static void __bt_notify_battery_data(void)
500 {
501         BT_INFO("+");
502         _bt_battery_data_t *data = NULL;
503         int result;
504
505         data = g_new0(_bt_battery_data_t, 1);
506         result = _bt_bm_read_data(data);
507         GVariant *out_var = NULL, *param = NULL;
508         GArray *info = NULL;
509
510         if (result != BLUETOOTH_ERROR_NONE) {
511                 BT_ERR("Battery data not collected");
512         }
513         else {
514                 bt_battery_dbus_data_t dbus_data;
515                 memset(&dbus_data, 0, sizeof(bt_battery_dbus_data_t));
516                 dbus_data.session_start_time = data->session_start_time;
517                 dbus_data.session_end_time = data->session_end_time;
518                 dbus_data.session_scan_time = data->session_scan_time;
519                 dbus_data.session_connected_time = data->session_connected_time;
520                 dbus_data.tx_time = data->tx_time;
521                 dbus_data.rx_time = data->rx_time;
522                 dbus_data.idle_time = data->idle_time;
523
524                 /*Populating app data*/
525                 int n = 0;
526                 for (GSList *l = data->atm_list; l != NULL; l = g_slist_next(l)) {
527                         bt_battery_app_data *t = (bt_battery_app_data *)(l->data);
528                         memcpy(&dbus_data.app_data[n], t, sizeof(bt_battery_app_data));
529                         n++;
530                 }
531                 dbus_data.num_app = n;
532
533                 info = g_array_new(FALSE, FALSE, sizeof(gchar));
534                 g_array_append_vals(info, &dbus_data, sizeof(bt_battery_dbus_data_t));
535
536                 out_var = g_variant_new_from_data((const GVariantType *)"ay",
537                                 info->data, info->len,
538                                 TRUE, NULL, NULL);
539         }
540         param = g_variant_new("(iv)", result, out_var);
541         _bt_send_event(BT_ADAPTER_EVENT,
542                 BLUETOOTH_EVENT_DISABLED_BATTERY_DATA,
543                 param);
544
545         g_slist_free(data->atm_list);
546         g_free(data);
547         g_array_free(info, TRUE);
548         BT_INFO("-");
549 }
550
551 void _bt_bm_event_handler(gpointer data)
552 {
553         bt_service_oal_event_data_t *oal_event = data;
554         int event_type = oal_event->type;
555
556         switch(event_type) {
557         case OAL_EVENT_ADAPTER_ENABLED:
558                 BT_DBG("Handling Adapter Enabled");
559                 if (__bt_start_session_time() != BLUETOOTH_ERROR_NONE)
560                         BT_ERR("Fail to start session time");
561                 break;
562         case OAL_EVENT_ADAPTER_DISABLED:
563                 BT_DBG("Handling Adapter Disabled");
564                 if (is_session_started == TRUE) {
565                         _bt_stop_session_time();
566                         __bt_notify_battery_data();
567                 }
568                 break;
569         case OAL_EVENT_ADAPTER_INQUIRY_STARTED:
570         case OAL_EVENT_BLE_DISCOVERY_STARTED:
571                 BT_DBG("Handling Adapter Discovery Start");
572                 _bt_start_scan_time();
573                 break;
574         case OAL_EVENT_ADAPTER_INQUIRY_FINISHED:
575                 /* Remove the regacy scan app */
576                 _bt_bm_remove_scan_app(SCAN_REGACY, 0, 0);
577
578                 _bt_stop_scan_time();
579                 break;
580         case OAL_EVENT_BLE_DISCOVERY_STOPPED:
581                 BT_DBG("Handling Adapter Discovery Stop");
582                 _bt_stop_scan_time();
583                 break;
584         case OAL_EVENT_SOCKET_OUTGOING_CONNECTED:
585         case OAL_EVENT_GATTC_CONNECTION_COMPLETED:
586                 BT_DBG("Handling Connection Start");
587                 _bt_start_connect_time();
588                 break;
589         case OAL_EVENT_SOCKET_DISCONNECTED:
590         case OAL_EVENT_GATTC_DISCONNECTION_COMPLETED:
591                 BT_DBG("Handling Connection Stop");
592                 _bt_stop_connect_time();
593                 break;
594         default:
595                 BT_DBG("The event is not currently being handled");
596         }
597 }