Modify recnet battery usage to return data after booting, if device never fully charged.
[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
38
39 BatteryMonitor::BatteryMonitor()
40 {
41         __initialize();
42 }
43
44 BatteryMonitor::~BatteryMonitor()
45 {
46 }
47
48 void BatteryMonitor::__initialize()
49 {
50         std::vector<Json> records;
51         __dbMgr.executeSync(
52                 "CREATE TABLE IF NOT EXISTS " BATTERY_USAGE_TABLE \
53                 " (" BATTERY_USAGE_TABLE_COLUMNS ")", &records);
54
55         __dbMgr.executeSync(
56                 "CREATE TABLE IF NOT EXISTS " BATTERY_LAST_INFO_TABLE \
57                 " (" BATTERY_LAST_INFO_TABLE_COLUMNS ")", &records);
58
59         __dbMgr.executeSync(
60                 "CREATE TABLE IF NOT EXISTS " BATTERY_LAST_CPU_USAGE_TABLE \
61                 " (" BATTERY_LAST_CPU_USAGE_TABLE_COLUMNS ")", &records);
62
63         __dbMgr.createTableSync(BATTERY_TEMP_TIME_INFO, BATTERY_TEMP_TIME_INFO_COLUMNS, NULL);
64 }
65
66 bool BatteryMonitor::__loadLastInfo()
67 {
68         __bootingTime = CURRENT_TIME;
69
70         device_battery_is_charging(&__isCharging);
71
72         std::vector<Json> records;
73         bool ret = __dbMgr.executeSync(
74                         "SELECT * FROM " BATTERY_LAST_INFO_TABLE \
75                         " WHERE " BATTERY_ROW_ID " = " DEFAULT_ROW_ID_STR, &records);
76         IF_FAIL_RETURN_TAG(ret, false, _E, "Failed to load last info");
77
78         if (records.size() == 1) {
79                 records[0].get(NULL, BATTERY_LAST_RESET_TIME, &__lastResetTime);
80                 records[0].get(NULL, BATTERY_LAST_PERCENT, &__lastPercent);
81                 records[0].get(NULL, BATTERY_LAST_PERCENT_TIME, &__lastPercentTime);
82                 records[0].get(NULL, BATTERY_LAST_HEART_TIMESTAMP, &__lastHeartTimestamp);
83         }
84         records.clear();
85
86         __batteryTimeInfoVec.clear();
87         ret = __dbMgr.executeSync("SELECT * FROM " BATTERY_TEMP_TIME_INFO , &__batteryTimeInfoVec);
88
89         ret = __dbMgr.executeSync("DELETE FROM " BATTERY_TEMP_TIME_INFO, &records);
90
91         return true;
92 }
93
94 bool BatteryMonitor::__updateLastInfo()
95 {
96         if (__batteryTimeInfoVec.size() > 0) {
97                 for (unsigned int i = 0; i < __batteryTimeInfoVec.size(); i++) {
98                         int64_t rowId;
99                         __dbMgr.insertSync(BATTERY_TEMP_TIME_INFO, __batteryTimeInfoVec.at(i), &rowId);
100                 }
101         }
102
103         IF_FAIL_RETURN_TAG(__lastResetTime != 0 || __lastPercentTime != 0, false, _W, "Last info doesn't exist");
104
105         char *query = sqlite3_mprintf(
106                         "INSERT OR REPLACE INTO " BATTERY_LAST_INFO_TABLE " (" \
107                         BATTERY_ROW_ID ", " BATTERY_LAST_RESET_TIME ", " \
108                         BATTERY_LAST_PERCENT ", " BATTERY_LAST_PERCENT_TIME ", " BATTERY_LAST_HEART_TIMESTAMP ") VALUES (%s, %d, %d, %d, %d)",
109                         DEFAULT_ROW_ID_STR, __lastResetTime, __lastPercent, __lastPercentTime, __lastHeartTimestamp);
110
111         std::vector<Json> records;
112         bool ret = __dbMgr.executeSync(query, &records);
113         sqlite3_free(query);
114         IF_FAIL_RETURN_TAG(ret, false, _E, "Failed to update battery stat last info");
115
116         return true;
117 }
118
119 void BatteryMonitor::__batteryChangeCb(device_callback_e type, void* value, void* userData)
120 {
121         IF_FAIL_VOID(type == DEVICE_CALLBACK_BATTERY_CAPACITY);
122
123         int percent = intptr_t(value);
124         int currentTime = CURRENT_TIME;
125         BatteryMonitor* instance = static_cast<BatteryMonitor*>(userData);
126
127         if (!__isCharging && percent == (__lastPercent - 1) && __lastPercentTime != 0) {
128                 _D("Battery capacity decreased: %d% -> %d%", __lastPercent, percent);
129
130                 Json battTimeInfo;
131                 battTimeInfo.set(NULL, BATTERY_AMOUNT, percent);
132                 battTimeInfo.set(NULL, BATTERY_START_TIME, __lastPercentTime);
133                 battTimeInfo.set(NULL, BATTERY_END_TIME, currentTime);
134                 __batteryTimeInfoVec.push_back(battTimeInfo);
135
136                 instance->processBatteryUsage();
137         }
138
139         __lastPercentTime = currentTime;
140         __lastPercent = percent;
141 }
142
143 void BatteryMonitor::__chargerChangeCb(device_callback_e type, void* value, void* userData)
144 {
145         IF_FAIL_VOID(type == DEVICE_CALLBACK_BATTERY_CHARGING);
146
147         __isCharging = intptr_t(value);
148         IF_FAIL_VOID(!__isCharging);
149
150         int percent;
151         if (__lastPercent < 0) {
152                 int error = device_battery_get_percent(&percent);
153                 IF_FAIL_VOID_TAG(error == DEVICE_ERROR_NONE, _E, "Failed to get battery capacity");
154         } else {
155                 percent = __lastPercent;
156         }
157
158         // If charger is disconnected after 100% charged
159         if (percent == 100) {
160                 __lastResetTime = CURRENT_TIME;
161                 __lastPercentTime = __lastResetTime;
162                 _D("Charger is disconnected after fully charged. ResetTime: %d", __lastResetTime);
163         }
164 }
165
166 int BatteryMonitor::start()
167 {
168         __loadLastInfo();
169
170         int error = device_add_callback(DEVICE_CALLBACK_BATTERY_CAPACITY, __batteryChangeCb, this);
171         IF_FAIL_RETURN_TAG(error == DEVICE_ERROR_NONE, ERR_OPERATION_FAILED, _E, "Failed to set battery capacity change cb");
172
173         error = device_add_callback(DEVICE_CALLBACK_BATTERY_CHARGING, __chargerChangeCb, NULL);
174         IF_FAIL_RETURN_TAG(error == DEVICE_ERROR_NONE, ERR_OPERATION_FAILED, _E, "Failed to set battery charging change cb");
175
176         return ERR_NONE;
177 }
178
179 int BatteryMonitor::stop()
180 {
181         __updateLastInfo();
182
183         int error = device_remove_callback(DEVICE_CALLBACK_BATTERY_CAPACITY, __batteryChangeCb);
184         IF_FAIL_RETURN_TAG(error == DEVICE_ERROR_NONE, ERR_OPERATION_FAILED, _E, "Failed to remove callback for battery capacity");
185
186         error = device_remove_callback(DEVICE_CALLBACK_BATTERY_CHARGING, __chargerChangeCb);
187         IF_FAIL_RETURN_TAG(error == DEVICE_ERROR_NONE, ERR_OPERATION_FAILED, _E, "Failed to remove callback for charger status");
188
189         return ERR_NONE;
190 }
191
192 bool BatteryMonitor::__getLastCpuUsageTable(CpuUsageMap* lastCpuUsage)
193 {
194         std::vector<Json> lastCpuUsageLog;
195         bool ret = __dbMgr.executeSync(
196                         "SELECT * FROM " BATTERY_LAST_CPU_USAGE_TABLE,
197                         &lastCpuUsageLog);
198         IF_FAIL_RETURN_TAG(ret, false, _E, "Failed to load last Cpu Times of Apps");
199
200         unsigned int k = 0;
201         std::string appId;
202         int timestamp;
203         int stime;
204         int utime;
205
206         while (k < lastCpuUsageLog.size()) {
207                 lastCpuUsageLog[k].get(NULL, BATTERY_APP_ID, &appId);
208                 lastCpuUsageLog[k].get(NULL, BATTERY_TIMESTAMP, &timestamp);
209                 lastCpuUsageLog[k].get(NULL, BATTERY_UTIME, &utime);
210                 lastCpuUsageLog[k].get(NULL, BATTERY_STIME, &stime);
211                 k++;
212
213                 LastAppCpuUsageInfo lastAppCpuUsage;
214                 lastAppCpuUsage.timestamp = timestamp;
215                 lastAppCpuUsage.utime = utime;
216                 lastAppCpuUsage.stime = stime;
217                 (*lastCpuUsage)[appId] = lastAppCpuUsage;
218         }
219         return true;
220 }
221
222 bool BatteryMonitor::processBatteryUsage()
223 {
224         IF_FAIL_RETURN_TAG(__batteryTimeInfoVec.size() > 0, true, _D, "All per-app battery usages are already calculated");
225
226         int totalStartTime;
227         int totalEndTime;
228         __batteryTimeInfoVec.front().get(NULL, BATTERY_START_TIME, &totalStartTime);
229         __batteryTimeInfoVec.back().get(NULL, BATTERY_END_TIME, &totalEndTime);
230
231         // Read cpu table from heart db for time span of stacked in __batteryTimeInfoVec
232         HeartDbReader heartReader;
233         bool ret = heartReader.open();
234         IF_FAIL_RETURN_TAG(ret, false, _E, "Failed to open heart db");
235
236         std::vector<Json> cpuUsageLog;
237         ret = heartReader.readCpuUsageLog(totalStartTime, totalEndTime, &cpuUsageLog);
238         heartReader.close();
239         IF_FAIL_RETURN_TAG(ret, false, _E, "Cannot read from heart cpu table");
240         IF_FAIL_RETURN_TAG(cpuUsageLog.size() > 0, true, _W, "Heart cpu data is not prepared yet (%d ~ %d)", totalStartTime, totalEndTime);
241         _D("Read %d rows from heart cpu table from %d to %d", cpuUsageLog.size(), totalStartTime, totalEndTime);
242
243         // Get the last timestamp of HEART cpu data and maximum app times tables in cache
244         cpuUsageLog.back().get(NULL, BATTERY_TIMESTAMP, &__lastHeartTimestamp);
245         CpuUsageMap lastCpuUsage;
246         ret = __getLastCpuUsageTable(&lastCpuUsage);
247
248         unsigned int i;
249         for (i = 0; i < __batteryTimeInfoVec.size(); i++) {
250                 int startTime;
251                 int endTime;
252
253                 Json row = __batteryTimeInfoVec[i].str();
254                 row.get(NULL, BATTERY_START_TIME, &startTime);
255                 row.get(NULL, BATTERY_END_TIME, &endTime);
256
257                 if (endTime > __lastHeartTimestamp) {
258                         _W("[%d] Heart cpu data is not prepared yet (%d ~ %d)", i, startTime, endTime);
259                         break;
260                 }
261
262                 // Calculate per app battery usage
263                 std::vector<Json> usage;
264                 ret = __analyzer.calculateBatteryUsage(startTime, endTime, cpuUsageLog, lastCpuUsage, &usage);
265                 if (!ret) {
266                         _E("[%d] Failed to calculate battery usage (%d ~ %d)", i, startTime, endTime);
267                         continue;
268                 }
269                 _D("[%d] Battery usage per app calculated (%d ~ %d)", i, startTime, endTime);
270
271                 // Insert battery usage
272                 ret = __insertBatteryUsageLog(usage);
273                 if (!ret) {
274                         _E("Failed to insert per app battery usage");
275                 }
276         }
277
278         // Insert log of last times of apps
279         if (i != 0) {
280                 ret = __insertLastCpuUsageLog(lastCpuUsage);
281                 if (!ret) {
282                         _E("Failed to insert last Cpu Usage of apps");
283                 }
284         }
285
286         // Remove completed time info
287         _D("Total %d time intervals, %d intervals are calculated", __batteryTimeInfoVec.size(), i);
288         __batteryTimeInfoVec.erase(__batteryTimeInfoVec.begin(), __batteryTimeInfoVec.begin() + i);
289
290         return true;
291 }
292
293 bool BatteryMonitor::__insertBatteryUsageLog(std::vector<Json>& usage)
294 {
295         IF_FAIL_RETURN_TAG(usage.size(), true, _W, "No data");
296         std::string query("INSERT INTO " BATTERY_USAGE_TABLE \
297                                 "(" BATTERY_APP_ID ", " BATTERY_START_TIME ", " \
298                                 BATTERY_END_TIME ", " BATTERY_UTIME ", " \
299                                 BATTERY_STIME ", " BATTERY_AMOUNT ") VALUES");
300
301         std::string appId;
302         int startTime;
303         int stime, utime;
304         int endTime;
305         double amount;
306
307         for (unsigned int i = 0; i < usage.size(); i++) {
308                 usage[i].get(NULL, BATTERY_APP_ID, &appId);
309                 usage[i].get(NULL, BATTERY_START_TIME, &startTime);
310                 usage[i].get(NULL, BATTERY_END_TIME, &endTime);
311                 usage[i].get(NULL, BATTERY_UTIME, &utime);
312                 usage[i].get(NULL, BATTERY_STIME, &stime);
313                 usage[i].get(NULL, BATTERY_AMOUNT, &amount);
314
315                 query += " ('" + appId + "', "
316                                 + std::to_string(startTime) + ", "
317                                 + std::to_string(endTime) + ", "
318                                 + std::to_string(utime) + ", "
319                                 + std::to_string(stime) + ", "
320                                 + std::to_string((int) (amount * 10000)) + ")";
321
322                 query += ", ";
323         }
324         query = query.substr(0, query.size() - 2);
325
326         _D("Insert %d rows of per app battery usage", usage.size());
327         return __dbMgr.executeSync(query.c_str(), NULL);
328 }
329
330 bool BatteryMonitor::__insertLastCpuUsageLog(CpuUsageMap& usage)
331 {
332         IF_FAIL_RETURN_TAG(usage.size(), true, _W, "No data");
333         _D("Delete all rows from app last times table");
334         __dbMgr.executeSync("DELETE FROM " BATTERY_LAST_CPU_USAGE_TABLE, NULL);
335
336         std::string query("INSERT INTO " BATTERY_LAST_CPU_USAGE_TABLE "(" BATTERY_APP_ID ", " BATTERY_UTIME ", " BATTERY_STIME "," BATTERY_TIMESTAMP ") VALUES");
337
338         std::string appId;
339         int stime, utime;
340         int timestamp;
341
342         for (auto it = usage.begin(); it != usage.end(); it++) {
343                 appId = it->first;
344                 timestamp = (it->second).timestamp;
345                 utime = (it->second).utime;
346                 stime = (it->second).stime;
347
348                 query += " ('" + appId
349                                 + "', " + std::to_string(utime)
350                                 + ", " + std::to_string(stime)
351                                 + ", " + std::to_string(timestamp) + ")";
352
353                 query += ", ";
354         }
355
356         _D("Insert %d rows in app last times table", usage.size());
357         query = query.substr(0, query.size() - 2);
358         return __dbMgr.executeSync(query.c_str(), NULL);
359 }
360
361 // Used for Recent Battery Usage
362 int BatteryMonitor::getLastResetTime()
363 {
364         if (__lastResetTime == 0) {
365                 _I("Device has not fully charged until now. Start time of recent battery usage will be booting time");
366                 return __bootingTime;
367         }
368
369         return __lastResetTime;
370 }