[Non-ACR] Fix battery monitor bugs
[platform/core/context/app-history.git] / src / server / 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 <sstream>
19 #include <sqlite3.h>
20 #include <glib.h>
21 #include <device/battery.h>
22 #include <Tuple.h>
23 #include <Timer.h>
24 #include <AppHistoryTypesPrivate.h>
25 #include "../DatabaseManager.h"
26 #include "BatteryMonitor.h"
27
28 #define DEFAULT_ROW_ID_STR "1"
29 #define HEART_DB_QUERY_INTERVAL 10 * 60 * 1000
30 #define DEFAULT_TIME_DIFF       600
31 #define TIME_DIFF_THRESHOLD     300
32
33 using namespace ctx;
34
35 bool __needSync = true;
36 BatteryMonitor* BatteryMonitor::__instance = NULL;
37
38 BatteryMonitor::BatteryMonitor() :
39         __lastFullTime(0),
40         __lastHeartAccessTime(0),
41         __database(NULL),
42         __heartReader(NULL),
43         __uid(0),
44         __timer(NULL)
45 {
46 }
47
48 BatteryMonitor::~BatteryMonitor()
49 {
50 }
51
52 BatteryMonitor* BatteryMonitor::getInstance()
53 {
54         IF_FAIL_RETURN(!__instance, __instance);
55
56         __instance = new BatteryMonitor();
57
58         return __instance;
59 }
60
61 void BatteryMonitor::destroy()
62 {
63         IF_FAIL_VOID(__instance);
64
65         delete __instance;
66         __instance = NULL;
67 }
68
69 void BatteryMonitor::initialize(uid_t uid, Timer* timer)
70 {
71         /* Set UID */
72         __uid = uid;
73
74         /* Set timer */
75         __timer = timer;
76
77         /* Set database */
78         __database = DatabaseManager::getInstance();
79
80         /* Create tables */
81         __database->execute(
82                 "CREATE TABLE IF NOT EXISTS " BATTERY_USAGE_TABLE \
83                 " (" BATTERY_USAGE_TABLE_COLUMNS "); " \
84                 "CREATE TABLE IF NOT EXISTS " BATTERY_LAST_INFO_TABLE \
85                 " (" BATTERY_LAST_INFO_TABLE_COLUMNS "); " \
86                 "CREATE TABLE IF NOT EXISTS " BATTERY_LAST_CPU_USAGE_TABLE \
87                 " (" BATTERY_LAST_CPU_USAGE_TABLE_COLUMNS ");", NULL);
88 }
89
90 bool BatteryMonitor::__loadLastInfo()
91 {
92         std::vector<std::string> columns;
93         std::vector<std::shared_ptr<Tuple>> records;
94         bool ret = __database->execute(
95                         "SELECT " BATTERY_LAST_FULL_TIME ", " BATTERY_LAST_HEART_ACCESS_TIME " FROM " BATTERY_LAST_INFO_TABLE \
96                         " WHERE " BATTERY_ROW_ID " = " DEFAULT_ROW_ID_STR, COL_INT64 COL_INT64, NULL, &records);
97         IF_FAIL_RETURN_TAG(ret, false, _E, "Failed to load last info");
98
99         if (records.size() == 1) {
100                 records[0]->getAt(0, &__lastFullTime);
101                 records[0]->getAt(1, &__lastHeartAccessTime);
102         }
103
104         // Adjust last full time & last heart access time
105         int percent = 0;
106         bool charging = false;
107         device_battery_get_percent(&percent);
108         device_battery_is_charging(&charging);
109
110         int currentTime = CURRENT_TIME;
111         if (!charging && percent == 100) {      // Fully charged
112                 __lastFullTime = currentTime;
113                 __lastHeartAccessTime = currentTime;
114                 _I("Fully charged. Last fully charged time: %d", __lastFullTime);
115                 __updateLastInfo();
116         } else if (__lastFullTime == 0) {       // Never fully charged yet
117                 __lastFullTime = currentTime;
118                 _I("Device has not fully charged until now. Start time of recent battery usage will be %d", __lastFullTime);
119                 __updateLastInfo();
120         } else if (__lastFullTime > currentTime || currentTime - __lastFullTime > LOG_RETENTION_PERIOD) {
121                 __lastFullTime = currentTime;
122                 __lastHeartAccessTime = currentTime;
123                 _I("Last fully charged time changed: %d", __lastFullTime);
124                 __updateLastInfo();
125         }
126
127         if (__lastHeartAccessTime == 0)
128                 __lastHeartAccessTime = currentTime - DEFAULT_TIME_DIFF;
129
130         return true;
131 }
132
133 bool BatteryMonitor::__updateLastInfo()
134 {
135         char *query = sqlite3_mprintf("INSERT OR REPLACE INTO " BATTERY_LAST_INFO_TABLE " (" \
136                         BATTERY_ROW_ID ", " BATTERY_LAST_FULL_TIME ", " BATTERY_LAST_HEART_ACCESS_TIME ") VALUES (%s, %lld, %lld)",
137                         DEFAULT_ROW_ID_STR, __lastFullTime, __lastHeartAccessTime);
138         bool ret = __database->execute(query, NULL);
139         sqlite3_free(query);
140         return ret;
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         bool isCharging = intptr_t(value);
148         IF_FAIL_VOID(!isCharging);
149
150         int percent;
151         int error = device_battery_get_percent(&percent);
152         IF_FAIL_VOID_TAG(error == DEVICE_ERROR_NONE, _E, "Failed to get battery capacity");
153         IF_FAIL_VOID(percent == 100);
154
155         // If charger is disconnected after 100% charged
156         BatteryMonitor* instance = getInstance();
157
158         __needSync = true;
159
160         instance->__lastFullTime = CURRENT_TIME;
161         _I("Charger is disconnected after fully charged. Last fully charged time: %d", instance->__lastFullTime);
162
163         if (!instance->__updateLastInfo())
164                 _E("Failed to update last reset time and last percent time");
165 }
166
167 void BatteryMonitor::__timeChangeCb(keynode_t* node, void* userData)
168 {
169         int timeDiff = vconf_keynode_get_int(node);
170         IF_FAIL_VOID(timeDiff != 0);
171
172         _I("Time changed(diff: %d). Related timestamps will be modified.", timeDiff);
173
174         BatteryMonitor* instance = getInstance();
175
176         __needSync = true;
177
178         instance->__lastFullTime = instance->__lastFullTime + timeDiff;
179         instance->__lastHeartAccessTime = instance->__lastHeartAccessTime + timeDiff;
180
181         if (instance->__updateLastInfo()) {
182                 __instance->__modifyLastCpuUsage(timeDiff);
183         } else {
184                 _E("Failed to update last reset time and last percent time");
185         }
186 }
187
188 gboolean BatteryMonitor::__timeoutCb(gpointer data)
189 {
190         BatteryMonitor::getInstance()->__processBatteryUsage();
191
192         return TRUE;
193 }
194
195 int BatteryMonitor::start()
196 {
197         __heartReader = new HeartDbReader(__uid);
198
199         __loadLastInfo();
200
201         int error = device_add_callback(DEVICE_CALLBACK_BATTERY_CHARGING, __chargerChangeCb, NULL);
202         IF_FAIL_RETURN_TAG(error == DEVICE_ERROR_NONE, E_FAILED, _E, "Failed to set battery charging change cb");
203
204         error = vconf_notify_key_changed(VCONFKEY_SYSTEM_TIME_CHANGED, __timeChangeCb, NULL);
205         IF_FAIL_RETURN_TAG(error == VCONF_OK, E_FAILED, _E, "Failed to set time changed cb");
206
207         _D("Start timer to request HEART data");
208         __timer->addTimeout(HEART_DB_QUERY_INTERVAL, __timeoutCb, NULL);
209
210         return E_NONE;
211 }
212
213 int BatteryMonitor::stop()
214 {
215         if (__heartReader) {
216                 delete __heartReader;
217                 __heartReader = NULL;
218         }
219
220         int error = device_remove_callback(DEVICE_CALLBACK_BATTERY_CHARGING, __chargerChangeCb);
221         IF_FAIL_RETURN_TAG(error == DEVICE_ERROR_NONE, E_FAILED, _E, "Failed to remove callback for charger status");
222
223         error = vconf_ignore_key_changed(VCONFKEY_SYSTEM_TIME_CHANGED, __timeChangeCb);
224         IF_FAIL_RETURN_TAG(error == VCONF_OK, E_FAILED, _E, "Failed to remove callback for time changed");
225
226         return E_NONE;
227 }
228
229 bool BatteryMonitor::__getLastCpuUsageTable(CpuUsageMap* lastHeartCpuLog)
230 {
231         std::vector<std::shared_ptr<Tuple>> cpuLog;
232         bool ret = __database->execute("SELECT " BATTERY_APP_ID ", " BATTERY_TIMESTAMP ", "
233                                 BATTERY_UTIME ", " BATTERY_STIME "," BATTERY_PID " FROM " BATTERY_LAST_CPU_USAGE_TABLE,
234                                 COL_STRING COL_INT64 COL_INT64 COL_INT64 COL_INT64, NULL, &cpuLog);
235         IF_FAIL_RETURN_TAG(ret, false, _E, "Failed to load last cpu times of apps");
236
237         unsigned int k = 0;
238         std::string appId;
239         int64_t timestamp;
240         int64_t stime;
241         int64_t utime;
242         int64_t pid;
243
244         while (k < cpuLog.size()) {
245                 cpuLog[k]->getAt(0, &appId);
246                 cpuLog[k]->getAt(1, &timestamp);
247                 cpuLog[k]->getAt(2, &utime);
248                 cpuLog[k]->getAt(3, &stime);
249                 cpuLog[k]->getAt(4, &pid);
250                 k++;
251
252                 CpuLogInfo appInfo;
253                 appInfo.timestamp = timestamp;
254                 appInfo.utime = utime;
255                 appInfo.stime = stime;
256                 appInfo.pid = pid;
257                 (*lastHeartCpuLog)[appId] = appInfo;
258         }
259         return true;
260 }
261
262 bool BatteryMonitor::__processBatteryUsage()
263 {
264         int currentTime = CURRENT_TIME;
265         if (__lastHeartAccessTime > currentTime) {
266                 __lastHeartAccessTime = currentTime - DEFAULT_TIME_DIFF;
267                 __lastFullTime = currentTime - DEFAULT_TIME_DIFF;
268                 __updateLastInfo();
269                 _W("Last fully charged time & last heart access time changed(%d)", __lastFullTime);
270         }
271
272         // Read from heart cpu table
273         bool ret = __heartReader->dbOpen();
274         IF_FAIL_RETURN_TAG(ret, true, _E, "Failed to open heart db");
275
276         std::vector<Json::Value> heartCpuLog;
277         ret = __heartReader->readCpuLog(__lastHeartAccessTime, &heartCpuLog);
278         __heartReader->dbClose();
279
280         IF_FAIL_RETURN_TAG(ret, true, _E, "Cannot read from heart cpu table");
281         IF_FAIL_RETURN_TAG(heartCpuLog.size() > 0, true, _W, "Heart cpu data is not prepared");
282         _D("Read %d rows from heart cpu table from %lld", \
283                                 heartCpuLog.size(), (long long int)__lastHeartAccessTime);
284
285         __lastHeartAccessTime = heartCpuLog.back()[BATTERY_TIMESTAMP].asInt64();
286         __updateLastInfo();
287
288         // Get last heart cpu log
289         CpuUsageMap lastHeartCpuLog;;
290         ret = __getLastCpuUsageTable(&lastHeartCpuLog);
291
292         // Calculate per app battery usage
293         std::vector<Json::Value> usage;
294         BatteryUsageAnalyzer::calculateBatteryUsage(heartCpuLog, lastHeartCpuLog, &usage);
295
296         // Insert battery usage
297         ret = __insertBatteryUsageLog(usage);
298         IF_FAIL_RETURN_TAG(ret, false, _E, "Failed to insert per app battery usage");
299
300         // Update last cpu usage
301         ret = __updateLastCpuUsageLog(lastHeartCpuLog);
302         IF_FAIL_RETURN_TAG(ret, false, _E, "Failed to update last cpu log");
303
304         __needSync = false;
305
306         return true;
307 }
308
309 bool BatteryMonitor::__insertBatteryUsageLog(std::vector<Json::Value>& usage)
310 {
311         IF_FAIL_RETURN_TAG(usage.size(), true, _W, "No data");
312         std::string query("INSERT INTO " BATTERY_USAGE_TABLE \
313                                 "(" BATTERY_APP_ID ", " BATTERY_START_TIME ", " BATTERY_END_TIME ", " \
314                                 BATTERY_UTIME ", " BATTERY_STIME "," BATTERY_TOTAL_TIME ") VALUES");
315
316         std::string appId;
317         int startTime, endTime;
318         int stime, utime;
319
320         for (unsigned int i = 0; i < usage.size(); i++) {
321                 appId = usage[i][BATTERY_APP_ID].asString();
322                 startTime = usage[i][BATTERY_START_TIME].asInt();
323                 endTime = usage[i][BATTERY_END_TIME].asInt();
324                 utime = usage[i][BATTERY_UTIME].asInt();
325                 stime = usage[i][BATTERY_STIME].asInt();
326
327                 query += " ('" + appId + "', "
328                                 + std::to_string(startTime) + ", "
329                                 + std::to_string(endTime) + ", "
330                                 + std::to_string(utime) + ", "
331                                 + std::to_string(stime) + ", "
332                                 + std::to_string(utime + stime) + ")";
333
334                 query += ", ";
335         }
336         query = query.substr(0, query.size() - 2);
337
338         // Remove expired log
339         query += "; DELETE FROM " + std::string(BATTERY_USAGE_TABLE)
340                         + " WHERE EndTime < " + std::to_string(CURRENT_TIME - LOG_RETENTION_PERIOD)
341                         + " OR EndTime > " + std::to_string(CURRENT_TIME) + ";";
342
343         _D("Insert %d rows of per app battery usage", usage.size());
344         return __database->execute(query.c_str(), NULL);
345 }
346
347 bool BatteryMonitor::__updateLastCpuUsageLog(CpuUsageMap& usage)
348 {
349         _D("Delete all rows from last cpu usage table");
350         __database->execute("DELETE FROM " BATTERY_LAST_CPU_USAGE_TABLE, NULL);
351
352         IF_FAIL_RETURN_TAG(usage.size(), true, _W, "No data");
353
354         std::string query("INSERT INTO " BATTERY_LAST_CPU_USAGE_TABLE "(" BATTERY_APP_ID ", " BATTERY_UTIME ", " BATTERY_STIME ", " BATTERY_TIMESTAMP ", " BATTERY_PID ") VALUES");
355
356         std::string appId;
357         int stime, utime, pid;
358         int timestamp;
359
360         for (auto it = usage.begin(); it != usage.end(); it++) {
361                 appId = it->first;
362                 timestamp = (it->second).timestamp;
363                 utime = (it->second).utime;
364                 stime = (it->second).stime;
365                 pid = (it->second).pid;
366
367                 query += " ('" + appId
368                                 + "', " + std::to_string(utime)
369                                 + ", " + std::to_string(stime)
370                                 + ", " + std::to_string(timestamp)
371                                 + ", " + std::to_string(pid) + ")";
372
373                 query += ", ";
374         }
375
376         _D("Insert %d rows in app last times table", usage.size());
377         query = query.substr(0, query.size() - 2);
378         return __database->execute(query.c_str(), NULL);
379 }
380
381 // Used for Recent Battery Usage
382 int BatteryMonitor::getLastFullTime()
383 {
384         return __lastFullTime;
385 }
386
387 void BatteryMonitor::prepareData()
388 {
389         int timeDiff = CURRENT_TIME - __lastHeartAccessTime;
390         IF_FAIL_VOID_TAG(__needSync || timeDiff >= TIME_DIFF_THRESHOLD || timeDiff < 0,
391                 _D, "Battery usage was updated %d minutes ago", timeDiff / 60);
392
393         _D("Request to sync heart cpu data");
394         bool ret = __heartReader->requestSync();
395         IF_FAIL_VOID_TAG(ret, _E, "Failed to sync heart db");
396
397         __processBatteryUsage();
398 }
399
400 void BatteryMonitor::__modifyLastCpuUsage(int timeDiff)
401 {
402         char *query = sqlite3_mprintf("UPDATE Temp_LastCpuUsagePerApp SET Timestamp = Timestamp + (%d)", timeDiff);
403         __database->execute(query, NULL);
404         sqlite3_free(query);
405
406         _D("Modified timestamp of LastCpuUsage");
407 }