[Non-ACR][Fix for TFIVE-1424]
[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         return true;
128 }
129
130 bool BatteryMonitor::__updateLastInfo()
131 {
132         char *query = sqlite3_mprintf("INSERT OR REPLACE INTO " BATTERY_LAST_INFO_TABLE " (" \
133                         BATTERY_ROW_ID ", " BATTERY_LAST_FULL_TIME ", " BATTERY_LAST_HEART_ACCESS_TIME ") VALUES (%s, %d, %d)",
134                         DEFAULT_ROW_ID_STR, __lastFullTime, __lastHeartAccessTime);
135         bool ret = __database->execute(query, NULL);
136         sqlite3_free(query);
137         return ret;
138 }
139
140 void BatteryMonitor::__chargerChangeCb(device_callback_e type, void* value, void* userData)
141 {
142         IF_FAIL_VOID(type == DEVICE_CALLBACK_BATTERY_CHARGING);
143
144         bool isCharging = intptr_t(value);
145         IF_FAIL_VOID(!isCharging);
146
147         int percent;
148         int error = device_battery_get_percent(&percent);
149         IF_FAIL_VOID_TAG(error == DEVICE_ERROR_NONE, _E, "Failed to get battery capacity");
150         IF_FAIL_VOID(percent == 100);
151
152         // If charger is disconnected after 100% charged
153         BatteryMonitor* instance = getInstance();
154
155         __needSync = true;
156
157         instance->__lastFullTime = CURRENT_TIME;
158         _I("Charger is disconnected after fully charged. Last fully charged time: %d", instance->__lastFullTime);
159
160         if (!instance->__updateLastInfo())
161                 _E("Failed to update last reset time and last percent time");
162 }
163
164 void BatteryMonitor::__timeChangeCb(keynode_t* node, void* userData)
165 {
166         int timeDiff = vconf_keynode_get_int(node);
167         IF_FAIL_VOID(timeDiff != 0);
168
169         _I("Time changed(diff: %d). Related timestamps will be modified.", timeDiff);
170
171         BatteryMonitor* instance = getInstance();
172
173         __needSync = true;
174
175         instance->__lastFullTime = instance->__lastFullTime + timeDiff;
176         instance->__lastHeartAccessTime = instance->__lastHeartAccessTime + timeDiff;
177
178         if (instance->__updateLastInfo()) {
179                 __instance->__modifyLastCpuUsage(timeDiff);
180         } else {
181                 _E("Failed to update last reset time and last percent time");
182         }
183 }
184
185 gboolean BatteryMonitor::__timeoutCb(gpointer data)
186 {
187         BatteryMonitor::getInstance()->__processBatteryUsage();
188
189         return TRUE;
190 }
191
192 int BatteryMonitor::start()
193 {
194         __heartReader = new HeartDbReader(__uid);
195
196         __loadLastInfo();
197
198         int error = device_add_callback(DEVICE_CALLBACK_BATTERY_CHARGING, __chargerChangeCb, NULL);
199         IF_FAIL_RETURN_TAG(error == DEVICE_ERROR_NONE, E_FAILED, _E, "Failed to set battery charging change cb");
200
201         error = vconf_notify_key_changed(VCONFKEY_SYSTEM_TIME_CHANGED, __timeChangeCb, NULL);
202         IF_FAIL_RETURN_TAG(error == VCONF_OK, E_FAILED, _E, "Failed to set time changed cb");
203
204         _D("Start timer to request HEART data");
205         __timer->addTimeout(HEART_DB_QUERY_INTERVAL, __timeoutCb, NULL);
206
207         return E_NONE;
208 }
209
210 int BatteryMonitor::stop()
211 {
212         if (__heartReader) {
213                 delete __heartReader;
214                 __heartReader = NULL;
215         }
216
217         int error = device_remove_callback(DEVICE_CALLBACK_BATTERY_CHARGING, __chargerChangeCb);
218         IF_FAIL_RETURN_TAG(error == DEVICE_ERROR_NONE, E_FAILED, _E, "Failed to remove callback for charger status");
219
220         error = vconf_ignore_key_changed(VCONFKEY_SYSTEM_TIME_CHANGED, __timeChangeCb);
221         IF_FAIL_RETURN_TAG(error == VCONF_OK, E_FAILED, _E, "Failed to remove callback for time changed");
222
223         return E_NONE;
224 }
225
226 bool BatteryMonitor::__getLastCpuUsageTable(CpuUsageMap* lastHeartCpuLog)
227 {
228         std::vector<std::shared_ptr<Tuple>> cpuLog;
229         bool ret = __database->execute("SELECT " BATTERY_APP_ID ", " BATTERY_TIMESTAMP ", "
230                                 BATTERY_UTIME ", " BATTERY_STIME "," BATTERY_PID " FROM " BATTERY_LAST_CPU_USAGE_TABLE,
231                                 COL_STRING COL_INT64 COL_INT64 COL_INT64 COL_INT64, NULL, &cpuLog);
232         IF_FAIL_RETURN_TAG(ret, false, _E, "Failed to load last cpu times of apps");
233
234         unsigned int k = 0;
235         std::string appId;
236         int64_t timestamp;
237         int64_t stime;
238         int64_t utime;
239         int64_t pid;
240
241         while (k < cpuLog.size()) {
242                 cpuLog[k]->getAt(0, &appId);
243                 cpuLog[k]->getAt(1, &timestamp);
244                 cpuLog[k]->getAt(2, &utime);
245                 cpuLog[k]->getAt(3, &stime);
246                 cpuLog[k]->getAt(4, &pid);
247                 k++;
248
249                 CpuLogInfo appInfo;
250                 appInfo.timestamp = timestamp;
251                 appInfo.utime = utime;
252                 appInfo.stime = stime;
253                 appInfo.pid = pid;
254                 (*lastHeartCpuLog)[appId] = appInfo;
255         }
256         return true;
257 }
258
259 bool BatteryMonitor::__processBatteryUsage()
260 {
261         int currentTime = CURRENT_TIME;
262         if (__lastHeartAccessTime > currentTime) {
263                 __lastHeartAccessTime = currentTime - DEFAULT_TIME_DIFF;
264                 __lastFullTime = currentTime - DEFAULT_TIME_DIFF;
265                 __updateLastInfo();
266                 _W("Last fully charged time & last heart access time changed(%d)", __lastFullTime);
267         }
268
269         // Read from heart cpu table
270         bool ret = __heartReader->dbOpen();
271         IF_FAIL_RETURN_TAG(ret, true, _E, "Failed to open heart db");
272
273         std::vector<Json::Value> heartCpuLog;
274         ret = __heartReader->readCpuLog(__lastHeartAccessTime, &heartCpuLog);
275         __heartReader->dbClose();
276
277         IF_FAIL_RETURN_TAG(ret, true, _E, "Cannot read from heart cpu table");
278         IF_FAIL_RETURN_TAG(heartCpuLog.size() > 0, true, _W, "Heart cpu data is not prepared");
279         _D("Read %d rows from heart cpu table from %d", heartCpuLog.size(), __lastHeartAccessTime);
280
281         __lastHeartAccessTime = heartCpuLog.back()[BATTERY_TIMESTAMP].asInt64();
282         __updateLastInfo();
283
284         // Get last heart cpu log
285         CpuUsageMap lastHeartCpuLog;;
286         ret = __getLastCpuUsageTable(&lastHeartCpuLog);
287
288         // Calculate per app battery usage
289         std::vector<Json::Value> usage;
290         BatteryUsageAnalyzer::calculateBatteryUsage(heartCpuLog, lastHeartCpuLog, &usage);
291
292         // Insert battery usage
293         ret = __insertBatteryUsageLog(usage);
294         IF_FAIL_RETURN_TAG(ret, false, _E, "Failed to insert per app battery usage");
295
296         // Update last cpu usage
297         ret = __updateLastCpuUsageLog(lastHeartCpuLog);
298         IF_FAIL_RETURN_TAG(ret, false, _E, "Failed to update last cpu log");
299
300         __needSync = false;
301
302         return true;
303 }
304
305 bool BatteryMonitor::__insertBatteryUsageLog(std::vector<Json::Value>& usage)
306 {
307         IF_FAIL_RETURN_TAG(usage.size(), true, _W, "No data");
308         std::string query("INSERT INTO " BATTERY_USAGE_TABLE \
309                                 "(" BATTERY_APP_ID ", " BATTERY_START_TIME ", " BATTERY_END_TIME ", " \
310                                 BATTERY_UTIME ", " BATTERY_STIME "," BATTERY_TOTAL_TIME ") VALUES");
311
312         std::string appId;
313         int startTime, endTime;
314         int stime, utime;
315
316         for (unsigned int i = 0; i < usage.size(); i++) {
317                 appId = usage[i][BATTERY_APP_ID].asString();
318                 startTime = usage[i][BATTERY_START_TIME].asInt();
319                 endTime = usage[i][BATTERY_END_TIME].asInt();
320                 utime = usage[i][BATTERY_UTIME].asInt();
321                 stime = usage[i][BATTERY_STIME].asInt();
322
323                 query += " ('" + appId + "', "
324                                 + std::to_string(startTime) + ", "
325                                 + std::to_string(endTime) + ", "
326                                 + std::to_string(utime) + ", "
327                                 + std::to_string(stime) + ", "
328                                 + std::to_string(utime + stime) + ")";
329
330                 query += ", ";
331         }
332         query = query.substr(0, query.size() - 2);
333
334         // Remove expired log
335         query += "; DELETE FROM " + std::string(BATTERY_USAGE_TABLE)
336                         + " WHERE EndTime < " + std::to_string(CURRENT_TIME - LOG_RETENTION_PERIOD)
337                         + " OR EndTime > " + std::to_string(CURRENT_TIME) + ";";
338
339         _D("Insert %d rows of per app battery usage", usage.size());
340         return __database->execute(query.c_str(), NULL);
341 }
342
343 bool BatteryMonitor::__updateLastCpuUsageLog(CpuUsageMap& usage)
344 {
345         _D("Delete all rows from last cpu usage table");
346         __database->execute("DELETE FROM " BATTERY_LAST_CPU_USAGE_TABLE, NULL);
347
348         IF_FAIL_RETURN_TAG(usage.size(), true, _W, "No data");
349
350         std::string query("INSERT INTO " BATTERY_LAST_CPU_USAGE_TABLE "(" BATTERY_APP_ID ", " BATTERY_UTIME ", " BATTERY_STIME ", " BATTERY_TIMESTAMP ", " BATTERY_PID ") VALUES");
351
352         std::string appId;
353         int stime, utime, pid;
354         int timestamp;
355
356         for (auto it = usage.begin(); it != usage.end(); it++) {
357                 appId = it->first;
358                 timestamp = (it->second).timestamp;
359                 utime = (it->second).utime;
360                 stime = (it->second).stime;
361                 pid = (it->second).pid;
362
363                 query += " ('" + appId
364                                 + "', " + std::to_string(utime)
365                                 + ", " + std::to_string(stime)
366                                 + ", " + std::to_string(timestamp)
367                                 + ", " + std::to_string(pid) + ")";
368
369                 query += ", ";
370         }
371
372         _D("Insert %d rows in app last times table", usage.size());
373         query = query.substr(0, query.size() - 2);
374         return __database->execute(query.c_str(), NULL);
375 }
376
377 // Used for Recent Battery Usage
378 int64_t BatteryMonitor::getLastFullTime()
379 {
380         return __lastFullTime;
381 }
382
383 void BatteryMonitor::prepareData()
384 {
385         int timeDiff = CURRENT_TIME - __lastHeartAccessTime;
386         IF_FAIL_VOID_TAG(__needSync || timeDiff >= TIME_DIFF_THRESHOLD || timeDiff < 0,
387                 _D, "Battery usage was updated %d minutes ago", timeDiff / 60);
388
389         _D("Request to sync heart cpu data");
390         bool ret = __heartReader->requestSync();
391         IF_FAIL_VOID_TAG(ret, _E, "Failed to sync heart db");
392
393         __processBatteryUsage();
394 }
395
396 void BatteryMonitor::__modifyLastCpuUsage(int timeDiff)
397 {
398         char *query = sqlite3_mprintf("UPDATE Temp_LastCpuUsagePerApp SET Timestamp = Timestamp + (%d)", timeDiff);
399         __database->execute(query, NULL);
400         sqlite3_free(query);
401
402         _D("Modified timestamp of LastCpuUsage");
403 }