Added code to query HeartDB every 10 minutes
[platform/core/context/context-provider.git] / src / battery-stats / BatteryMonitor.cpp
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <map>
18 #include <sqlite3.h>
19 #include <time.h>
20 #include <glib.h>
21 #include <Types.h>
22 #include <Json.h>
23 #include "HeartDbReader.h"
24 #include "BatteryMonitor.h"
25
26 #define DEFAULT_ROW_ID_STR "1"
27
28 using namespace ctx;
29
30 static int __bootingTime = 0;
31 static int __lastResetTime = 0;
32 static int __lastPercent = -1;
33 static int __lastPercentTime = 0;
34 static int __lastHeartTimestamp = 0;
35 static bool __isCharging = 0;
36 static std::vector<Json> __batteryTimeInfoVec;
37 static bool __timerRunning = false;
38
39 #define DB_QUERY_DELAY 600000
40
41 BatteryMonitor::BatteryMonitor()
42 {
43         __initialize();
44 }
45
46 BatteryMonitor::~BatteryMonitor()
47 {
48 }
49
50 void BatteryMonitor::__initialize()
51 {
52         std::vector<Json> records;
53         __dbMgr.executeSync(
54                 "CREATE TABLE IF NOT EXISTS " BATTERY_USAGE_TABLE \
55                 " (" BATTERY_USAGE_TABLE_COLUMNS ")", &records);
56
57         __dbMgr.executeSync(
58                 "CREATE TABLE IF NOT EXISTS " BATTERY_LAST_INFO_TABLE \
59                 " (" BATTERY_LAST_INFO_TABLE_COLUMNS ")", &records);
60
61         __dbMgr.executeSync(
62                 "CREATE TABLE IF NOT EXISTS " BATTERY_LAST_CPU_USAGE_TABLE \
63                 " (" BATTERY_LAST_CPU_USAGE_TABLE_COLUMNS ")", &records);
64
65         __dbMgr.createTableSync(BATTERY_TEMP_TIME_INFO, BATTERY_TEMP_TIME_INFO_COLUMNS, NULL);
66 }
67
68 bool BatteryMonitor::__loadLastInfo()
69 {
70         __bootingTime = CURRENT_TIME;
71
72         device_battery_is_charging(&__isCharging);
73
74         std::vector<Json> records;
75         bool ret = __dbMgr.executeSync(
76                         "SELECT * FROM " BATTERY_LAST_INFO_TABLE \
77                         " WHERE " BATTERY_ROW_ID " = " DEFAULT_ROW_ID_STR, &records);
78         IF_FAIL_RETURN_TAG(ret, false, _E, "Failed to load last info");
79
80         if (records.size() == 1) {
81                 records[0].get(NULL, BATTERY_LAST_RESET_TIME, &__lastResetTime);
82                 records[0].get(NULL, BATTERY_LAST_PERCENT, &__lastPercent);
83                 records[0].get(NULL, BATTERY_LAST_PERCENT_TIME, &__lastPercentTime);
84                 records[0].get(NULL, BATTERY_LAST_HEART_TIMESTAMP, &__lastHeartTimestamp);
85         }
86         records.clear();
87
88         __batteryTimeInfoVec.clear();
89         ret = __dbMgr.executeSync("SELECT * FROM " BATTERY_TEMP_TIME_INFO , &__batteryTimeInfoVec);
90
91         ret = __dbMgr.executeSync("DELETE FROM " BATTERY_TEMP_TIME_INFO, &records);
92
93         return true;
94 }
95
96 bool BatteryMonitor::__updateLastInfo()
97 {
98         if (__batteryTimeInfoVec.size() > 0) {
99                 for (unsigned int i = 0; i < __batteryTimeInfoVec.size(); i++) {
100                         int64_t rowId;
101                         __dbMgr.insertSync(BATTERY_TEMP_TIME_INFO, __batteryTimeInfoVec.at(i), &rowId);
102                 }
103         }
104
105         IF_FAIL_RETURN_TAG(__lastResetTime != 0 || __lastPercentTime != 0, false, _W, "Last info doesn't exist");
106
107         char *query = sqlite3_mprintf(
108                         "INSERT OR REPLACE INTO " BATTERY_LAST_INFO_TABLE " (" \
109                         BATTERY_ROW_ID ", " BATTERY_LAST_RESET_TIME ", " \
110                         BATTERY_LAST_PERCENT ", " BATTERY_LAST_PERCENT_TIME ", " BATTERY_LAST_HEART_TIMESTAMP ") VALUES (%s, %d, %d, %d, %d)",
111                         DEFAULT_ROW_ID_STR, __lastResetTime, __lastPercent, __lastPercentTime, __lastHeartTimestamp);
112
113         std::vector<Json> records;
114         bool ret = __dbMgr.executeSync(query, &records);
115         sqlite3_free(query);
116         IF_FAIL_RETURN_TAG(ret, false, _E, "Failed to update battery stat last info");
117
118         return true;
119 }
120
121 void BatteryMonitor::__batteryChangeCb(device_callback_e type, void* value, void* userData)
122 {
123         IF_FAIL_VOID(type == DEVICE_CALLBACK_BATTERY_CAPACITY);
124
125         int percent = intptr_t(value);
126         int currentTime = CURRENT_TIME;
127         BatteryMonitor* instance = static_cast<BatteryMonitor*>(userData);
128
129         if (!__isCharging && percent == (__lastPercent - 1) && __lastPercentTime != 0) {
130                 _D("Battery capacity decreased: %d% -> %d%", __lastPercent, percent);
131
132                 Json battTimeInfo;
133                 battTimeInfo.set(NULL, BATTERY_AMOUNT, percent);
134                 battTimeInfo.set(NULL, BATTERY_START_TIME, __lastPercentTime);
135                 battTimeInfo.set(NULL, BATTERY_END_TIME, currentTime);
136                 __batteryTimeInfoVec.push_back(battTimeInfo);
137
138                 bool dataRemaining = instance->processBatteryUsage();
139                 if (dataRemaining && !__timerRunning) {
140                                 __timerRunning = true;
141                                 _D("Start timer to request HEART data");
142                                 g_timeout_add(DB_QUERY_DELAY, __timeoutCb, instance);
143                 }
144         }
145
146         __lastPercentTime = currentTime;
147         __lastPercent = percent;
148 }
149
150 void BatteryMonitor::__chargerChangeCb(device_callback_e type, void* value, void* userData)
151 {
152         IF_FAIL_VOID(type == DEVICE_CALLBACK_BATTERY_CHARGING);
153
154         __isCharging = intptr_t(value);
155         IF_FAIL_VOID(!__isCharging);
156
157         int percent;
158         if (__lastPercent < 0) {
159                 int error = device_battery_get_percent(&percent);
160                 IF_FAIL_VOID_TAG(error == DEVICE_ERROR_NONE, _E, "Failed to get battery capacity");
161         } else {
162                 percent = __lastPercent;
163         }
164
165         // If charger is disconnected after 100% charged
166         if (percent == 100) {
167                 __lastResetTime = CURRENT_TIME;
168                 __lastPercentTime = __lastResetTime;
169                 _D("Charger is disconnected after fully charged. ResetTime: %d", __lastResetTime);
170         }
171 }
172
173 gboolean BatteryMonitor::__timeoutCb(gpointer data) {
174         BatteryMonitor* instance = static_cast<BatteryMonitor*>(data);
175
176         bool dataRemaining = instance->processBatteryUsage();
177         if (!dataRemaining) {
178                 _D("Stop timer, no more data to process");
179                 __timerRunning = false;
180         }
181
182         return dataRemaining ? TRUE : FALSE;
183 }
184
185 int BatteryMonitor::start()
186 {
187         __loadLastInfo();
188
189         int error = device_add_callback(DEVICE_CALLBACK_BATTERY_CAPACITY, __batteryChangeCb, this);
190         IF_FAIL_RETURN_TAG(error == DEVICE_ERROR_NONE, ERR_OPERATION_FAILED, _E, "Failed to set battery capacity change cb");
191
192         error = device_add_callback(DEVICE_CALLBACK_BATTERY_CHARGING, __chargerChangeCb, NULL);
193         IF_FAIL_RETURN_TAG(error == DEVICE_ERROR_NONE, ERR_OPERATION_FAILED, _E, "Failed to set battery charging change cb");
194
195         __timerRunning = true;
196         _D("Start timer to request HEAERT data");
197         g_timeout_add(DB_QUERY_DELAY, __timeoutCb, this);
198         return ERR_NONE;
199 }
200
201 int BatteryMonitor::stop()
202 {
203         __updateLastInfo();
204
205         int error = device_remove_callback(DEVICE_CALLBACK_BATTERY_CAPACITY, __batteryChangeCb);
206         IF_FAIL_RETURN_TAG(error == DEVICE_ERROR_NONE, ERR_OPERATION_FAILED, _E, "Failed to remove callback for battery capacity");
207
208         error = device_remove_callback(DEVICE_CALLBACK_BATTERY_CHARGING, __chargerChangeCb);
209         IF_FAIL_RETURN_TAG(error == DEVICE_ERROR_NONE, ERR_OPERATION_FAILED, _E, "Failed to remove callback for charger status");
210
211         return ERR_NONE;
212 }
213
214 bool BatteryMonitor::__getLastCpuUsageTable(CpuUsageMap* lastCpuUsage)
215 {
216         std::vector<Json> lastCpuUsageLog;
217         bool ret = __dbMgr.executeSync(
218                         "SELECT * FROM " BATTERY_LAST_CPU_USAGE_TABLE,
219                         &lastCpuUsageLog);
220         IF_FAIL_RETURN_TAG(ret, false, _E, "Failed to load last Cpu Times of Apps");
221
222         unsigned int k = 0;
223         std::string appId;
224         int timestamp;
225         int stime;
226         int utime;
227
228         while (k < lastCpuUsageLog.size()) {
229                 lastCpuUsageLog[k].get(NULL, BATTERY_APP_ID, &appId);
230                 lastCpuUsageLog[k].get(NULL, BATTERY_TIMESTAMP, &timestamp);
231                 lastCpuUsageLog[k].get(NULL, BATTERY_UTIME, &utime);
232                 lastCpuUsageLog[k].get(NULL, BATTERY_STIME, &stime);
233                 k++;
234
235                 LastAppCpuUsageInfo lastAppCpuUsage;
236                 lastAppCpuUsage.timestamp = timestamp;
237                 lastAppCpuUsage.utime = utime;
238                 lastAppCpuUsage.stime = stime;
239                 (*lastCpuUsage)[appId] = lastAppCpuUsage;
240         }
241         return true;
242 }
243
244 //Return false if all is processed, true otherwise
245 bool BatteryMonitor::processBatteryUsage()
246 {
247         IF_FAIL_RETURN_TAG(__batteryTimeInfoVec.size() > 0, false, _D, "All per-app battery usages are already calculated");
248
249         int totalStartTime;
250         int totalEndTime;
251         __batteryTimeInfoVec.front().get(NULL, BATTERY_START_TIME, &totalStartTime);
252         __batteryTimeInfoVec.back().get(NULL, BATTERY_END_TIME, &totalEndTime);
253
254         // Read cpu table from heart db for time span of stacked in __batteryTimeInfoVec
255         HeartDbReader heartReader;
256         bool ret = heartReader.open();
257         IF_FAIL_RETURN_TAG(ret, true, _E, "Failed to open heart db");
258
259         std::vector<Json> cpuUsageLog;
260         ret = heartReader.readCpuUsageLog(totalStartTime, totalEndTime, &cpuUsageLog);
261         heartReader.close();
262         IF_FAIL_RETURN_TAG(ret, true, _E, "Cannot read from heart cpu table");
263         IF_FAIL_RETURN_TAG(cpuUsageLog.size() > 0, true, _W, "Heart cpu data is not prepared yet (%d ~ %d)", totalStartTime, totalEndTime);
264         _D("Read %d rows from heart cpu table from %d to %d", cpuUsageLog.size(), totalStartTime, totalEndTime);
265
266         // Get the last timestamp of HEART cpu data and maximum app times tables in cache
267         cpuUsageLog.back().get(NULL, BATTERY_TIMESTAMP, &__lastHeartTimestamp);
268         CpuUsageMap lastCpuUsage;
269         ret = __getLastCpuUsageTable(&lastCpuUsage);
270
271         unsigned int i;
272         for (i = 0; i < __batteryTimeInfoVec.size(); i++) {
273                 int startTime;
274                 int endTime;
275
276                 Json row = __batteryTimeInfoVec[i].str();
277                 row.get(NULL, BATTERY_START_TIME, &startTime);
278                 row.get(NULL, BATTERY_END_TIME, &endTime);
279
280                 if (endTime > __lastHeartTimestamp) {
281                         _W("[%d] Heart cpu data is not prepared yet (%d ~ %d)", i, startTime, endTime);
282                         break;
283                 }
284
285                 // Calculate per app battery usage
286                 std::vector<Json> usage;
287                 ret = __analyzer.calculateBatteryUsage(startTime, endTime, cpuUsageLog, lastCpuUsage, &usage);
288                 if (!ret) {
289                         _E("[%d] Failed to calculate battery usage (%d ~ %d)", i, startTime, endTime);
290                         continue;
291                 }
292                 _D("[%d] Battery usage per app calculated (%d ~ %d)", i, startTime, endTime);
293
294                 // Insert battery usage
295                 ret = __insertBatteryUsageLog(usage);
296                 if (!ret) {
297                         _E("Failed to insert per app battery usage");
298                 }
299         }
300
301         // Insert log of last times of apps
302         if (i != 0) {
303                 ret = __insertLastCpuUsageLog(lastCpuUsage);
304                 if (!ret) {
305                         _E("Failed to insert last Cpu Usage of apps");
306                 }
307         }
308
309         // Remove completed time info
310         _D("Total %d time intervals, %d intervals are calculated", __batteryTimeInfoVec.size(), i);
311         __batteryTimeInfoVec.erase(__batteryTimeInfoVec.begin(), __batteryTimeInfoVec.begin() + i);
312
313         if (__batteryTimeInfoVec.size() == 0) {
314                 return false;
315         }
316         else {
317                 return true;
318         }
319 }
320
321 bool BatteryMonitor::__insertBatteryUsageLog(std::vector<Json>& usage)
322 {
323         IF_FAIL_RETURN_TAG(usage.size(), true, _W, "No data");
324         std::string query("INSERT INTO " BATTERY_USAGE_TABLE \
325                                 "(" BATTERY_APP_ID ", " BATTERY_START_TIME ", " \
326                                 BATTERY_END_TIME ", " BATTERY_UTIME ", " \
327                                 BATTERY_STIME ", " BATTERY_AMOUNT ") VALUES");
328
329         std::string appId;
330         int startTime;
331         int stime, utime;
332         int endTime;
333         double amount;
334
335         for (unsigned int i = 0; i < usage.size(); i++) {
336                 usage[i].get(NULL, BATTERY_APP_ID, &appId);
337                 usage[i].get(NULL, BATTERY_START_TIME, &startTime);
338                 usage[i].get(NULL, BATTERY_END_TIME, &endTime);
339                 usage[i].get(NULL, BATTERY_UTIME, &utime);
340                 usage[i].get(NULL, BATTERY_STIME, &stime);
341                 usage[i].get(NULL, BATTERY_AMOUNT, &amount);
342
343                 query += " ('" + appId + "', "
344                                 + std::to_string(startTime) + ", "
345                                 + std::to_string(endTime) + ", "
346                                 + std::to_string(utime) + ", "
347                                 + std::to_string(stime) + ", "
348                                 + std::to_string((int) (amount * 10000)) + ")";
349
350                 query += ", ";
351         }
352         query = query.substr(0, query.size() - 2);
353
354         _D("Insert %d rows of per app battery usage", usage.size());
355         return __dbMgr.executeSync(query.c_str(), NULL);
356 }
357
358 bool BatteryMonitor::__insertLastCpuUsageLog(CpuUsageMap& usage)
359 {
360         IF_FAIL_RETURN_TAG(usage.size(), true, _W, "No data");
361         _D("Delete all rows from app last times table");
362         __dbMgr.executeSync("DELETE FROM " BATTERY_LAST_CPU_USAGE_TABLE, NULL);
363
364         std::string query("INSERT INTO " BATTERY_LAST_CPU_USAGE_TABLE "(" BATTERY_APP_ID ", " BATTERY_UTIME ", " BATTERY_STIME "," BATTERY_TIMESTAMP ") VALUES");
365
366         std::string appId;
367         int stime, utime;
368         int timestamp;
369
370         for (auto it = usage.begin(); it != usage.end(); it++) {
371                 appId = it->first;
372                 timestamp = (it->second).timestamp;
373                 utime = (it->second).utime;
374                 stime = (it->second).stime;
375
376                 query += " ('" + appId
377                                 + "', " + std::to_string(utime)
378                                 + ", " + std::to_string(stime)
379                                 + ", " + std::to_string(timestamp) + ")";
380
381                 query += ", ";
382         }
383
384         _D("Insert %d rows in app last times table", usage.size());
385         query = query.substr(0, query.size() - 2);
386         return __dbMgr.executeSync(query.c_str(), NULL);
387 }
388
389 // Used for Recent Battery Usage
390 int BatteryMonitor::getLastResetTime()
391 {
392         if (__lastResetTime == 0) {
393                 _I("Device has not fully charged until now. Start time of recent battery usage will be booting time");
394                 return __bootingTime;
395         }
396
397         return __lastResetTime;
398 }