4 * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd.
6 * Licensed under the Apache License, Version 2.0 (the License);
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
24 #include <sys/types.h>
25 #include <sys/statvfs.h>
31 #include "device-node.h"
33 #include "core/devices.h"
34 #include "core/common.h"
35 #include "core/edbus-handler.h"
36 #include "core/device-notifier.h"
37 #include "core/config-parser.h"
39 #define MEMNOTIFY_NORMAL 0x0000
40 #define MEMNOTIFY_LOW 0xfaac
41 #define MEMNOTIFY_CRITICAL 0xdead
42 #define MEMNOTIFY_REBOOT 0xb00f
44 #define MEMORY_STATUS_USR_PATH "/opt/usr"
45 #define MEMORY_MEGABYTE_VALUE 1048576
47 #define MEMNOTI_WARNING_VALUE (5) // 5% under
48 #define MEMNOTI_CRITICAL_VALUE (0.1) // 0.1% under
49 #define MEMNOTI_FULL_VALUE (0.0) // 0.0% under
51 #define SIGNAL_LOWMEM_STATE "ChangeState"
52 #define SIGNAL_LOWMEM_FULL "Full"
54 #define POPUP_KEY_MEMNOTI "_MEM_NOTI_"
55 #define POPUP_KEY_APPNAME "_APP_NAME_"
57 #define LOWMEM_POPUP_NAME "lowmem-syspopup"
59 #define MEMNOTI_TIMER_INTERVAL 5
60 #define MEM_TRIM_TIMER_INTERVAL 86400 /* 24 hour */
61 #define MEM_FSTRIM_PATH "/sbin/fstrim"
63 #define MEM_TRIM_START_TIME 2 // AM 02:00:00
65 #define HOUR_SEC (MIN_SEC * MIN_SEC)
69 #define STORAGE_CONF_FILE "/etc/deviced/storage.conf"
72 MEMNOTI_LEVEL_CRITICAL = 0,
73 MEMNOTI_LEVEL_WARNING,
83 struct storage_config_info {
85 double critical_level;
89 static Ecore_Fd_Handler *lowmem_efd = NULL;
91 static int cur_mem_state = MEMNOTIFY_NORMAL;
93 static Ecore_Timer *memnoti_timer = NULL;
94 static Ecore_Timer *mem_trim_timer = NULL;
96 static struct storage_config_info storage_info = {
97 .warning_level = MEMNOTI_WARNING_VALUE,
98 .critical_level = MEMNOTI_CRITICAL_VALUE,
99 .full_level = MEMNOTI_FULL_VALUE,
102 static void memnoti_send_broadcast(int status)
112 snprintf(str_status, sizeof(str_status), "%d", status);
114 broadcast_edbus_signal(DEVICED_PATH_LOWMEM, DEVICED_INTERFACE_LOWMEM,
115 SIGNAL_LOWMEM_STATE, "i", arr);
118 static void memnoti_level_broadcast(enum memnoti_level level)
120 static int status = 0;
121 if (level == MEMNOTI_LEVEL_CRITICAL && status == 0)
123 else if (level != MEMNOTI_LEVEL_CRITICAL && status == 1)
127 _D("send user mem noti : %d %d", level, status);
128 memnoti_send_broadcast(status);
131 static int memnoti_popup(enum memnoti_level level)
136 struct popup_data *params;
137 static const struct device_ops *apps = NULL;
139 if (level != MEMNOTI_LEVEL_WARNING && level != MEMNOTI_LEVEL_CRITICAL) {
140 _E("level check error : %d",level);
144 if (level == MEMNOTI_LEVEL_WARNING) {
146 } else if (level == MEMNOTI_LEVEL_CRITICAL) {
150 ret = vconf_get_int(VCONFKEY_STARTER_SEQUENCE, &val);
151 if (val == 0 || ret != 0)
154 FIND_DEVICE_INT(apps, "apps");
156 params = malloc(sizeof(struct popup_data));
157 if (params == NULL) {
161 params->name = LOWMEM_POPUP_NAME;
162 params->key = POPUP_KEY_MEMNOTI;
163 params->value = strdup(value);
164 apps->init((void *)params);
171 static enum memnoti_level check_memnoti_level(double total, double avail)
173 double tmp_size = (avail/total)*100;
175 if (tmp_size > storage_info.warning_level)
176 return MEMNOTI_LEVEL_NORMAL;
177 if (tmp_size > storage_info.critical_level)
178 return MEMNOTI_LEVEL_WARNING;
179 return MEMNOTI_LEVEL_CRITICAL;
182 static void memnoti_full_broadcast(double total, double avail)
184 static int status = 0;
186 double tmp_size = (avail/total)*100;
191 if (tmp_size <= storage_info.full_level && status == 0)
193 else if (tmp_size > storage_info.full_level && status == 1)
198 _D("send memory full noti : %d (total: %4.4lf avail: %4.4lf)", status, total, avail);
199 snprintf(str_status, sizeof(str_status), "%d", status);
201 broadcast_edbus_signal(DEVICED_PATH_LOWMEM, DEVICED_INTERFACE_LOWMEM,
202 SIGNAL_LOWMEM_FULL, "i", arr);
205 static void memory_status_set_full_mem_size(void)
211 storage_get_internal_memory_size(&s);
212 dTotal = (double)s.f_frsize * s.f_blocks;
213 dAvail = (double)s.f_bsize * s.f_bavail;
215 storage_info.full_level += (MEMORY_MEGABYTE_VALUE/dTotal)*100;
216 _I("full : %4.4lf avail : %4.4lf warning : %4.4lf critical : %4.4lf",
217 storage_info.full_level, (dAvail*100/dTotal),
218 storage_info.warning_level, storage_info.critical_level);
221 static Eina_Bool memory_status_get_available_size(void *data)
223 static enum memnoti_level old = MEMNOTI_LEVEL_NORMAL;
224 enum memnoti_level now;
230 storage_get_internal_memory_size(&s);
231 dTotal = (double)s.f_frsize * s.f_blocks;
232 dAvail = (double)s.f_bsize * s.f_bavail;
234 memnoti_full_broadcast(dTotal, dAvail);
236 now = check_memnoti_level(dTotal, dAvail);
238 memnoti_level_broadcast(now);
240 if (now < MEMNOTI_LEVEL_NORMAL && now < old) {
241 ret = memnoti_popup(now);
243 now = MEMNOTI_LEVEL_NORMAL;
247 ecore_timer_interval_set(memnoti_timer, MEMNOTI_TIMER_INTERVAL);
252 static int __memnoti_fd_init(void)
254 memory_status_set_full_mem_size();
255 memory_status_get_available_size(NULL);
256 memnoti_timer = ecore_timer_add(MEMNOTI_TIMER_INTERVAL,
257 memory_status_get_available_size, NULL);
258 if (memnoti_timer == NULL)
259 _E("fail mem available noti timer add");
263 static Eina_Bool memory_trim_cb(void *data)
265 ecore_timer_interval_set(memnoti_timer, MEM_TRIM_TIMER_INTERVAL);
266 if (launch_if_noexist(MEM_FSTRIM_PATH, MEMORY_STATUS_USR_PATH) == -1) {
267 _E("fail to launch fstrim");
269 _D("fs memory trim is operated");
274 static int __mem_trim_delta(struct tm *cur_tm)
279 if (cur_tm->tm_hour < MEM_TRIM_START_TIME)
283 delta += ((sign_val) * (MEM_TRIM_START_TIME - cur_tm->tm_hour) * HOUR_SEC);
284 delta -= ((sign_val) * (cur_tm->tm_min * MIN_SEC + cur_tm->tm_sec));
288 static int __run_mem_trim(void)
295 cur_tm = (struct tm *)malloc(sizeof(struct tm));
296 if (cur_tm == NULL) {
297 _E("Fail to memory allocation");
301 if (localtime_r(&now, cur_tm) == NULL) {
302 _E("Fail to get localtime");
307 mem_trim_time = MEM_TRIM_TIMER_INTERVAL + __mem_trim_delta(cur_tm);
308 _D("start mem trim timer", mem_trim_time);
309 mem_trim_timer = ecore_timer_add(mem_trim_time, memory_trim_cb, NULL);
310 if (mem_trim_timer == NULL) {
311 _E("Fail to add mem trim timer");
319 static DBusMessage *edbus_getstatus(E_DBus_Object *obj, DBusMessage *msg)
321 DBusMessageIter iter;
328 storage_get_internal_memory_size(&s);
329 dTotal = (double)s.f_frsize * s.f_blocks;
330 dAvail = (double)s.f_bsize * s.f_bavail;
332 reply = dbus_message_new_method_return(msg);
333 dbus_message_iter_init_append(reply, &iter);
334 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT64, &dTotal);
335 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT64, &dAvail);
339 static DBusMessage *edbus_memtrim(E_DBus_Object *obj, DBusMessage *msg)
341 DBusMessageIter iter;
345 ret = launch_if_noexist(MEM_FSTRIM_PATH, MEMORY_STATUS_USR_PATH);
347 _E("fail to launch fstrim");
349 _D("fs memory trim is operated");
352 reply = dbus_message_new_method_return(msg);
353 dbus_message_iter_init_append(reply, &iter);
354 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
358 static const struct edbus_method edbus_methods[] = {
359 { "getstorage", NULL, "i", edbus_getstatus },
360 { "MemTrim", NULL, "i", edbus_memtrim },
361 /* Add methods here */
364 static int booting_done(void *data)
372 if (__memnoti_fd_init() == -1)
373 _E("fail remain mem noti control fd init");
378 static int lowmem_poweroff(void *data)
381 ecore_timer_del(memnoti_timer);
382 memnoti_timer = NULL;
384 if (mem_trim_timer) {
385 ecore_timer_del(mem_trim_timer);
386 mem_trim_timer = NULL;
391 static int load_config(struct parse_result *result, void *user_data)
393 struct storage_config_info *info = (struct storage_config_info *)user_data;
400 if (!MATCH(result->section, "LOWSTORAGE"))
404 value = result->value;
406 if (MATCH(name, "WARNING_LEVEL"))
407 info->warning_level = (double)atof(value);
408 else if (MATCH(name, "CRITICAL_LEVEL"))
409 info->critical_level = (double)atof(value);
410 else if (MATCH(name, "FULL_LEVEL"))
411 info->full_level = (double)atof(value);
416 static void storage_config_load(struct storage_config_info *info)
420 ret = config_parse(STORAGE_CONF_FILE, load_config, info);
422 _E("Failed to load %s, %d Use default value!", STORAGE_CONF_FILE, ret);
425 static void lowmem_init(void *data)
429 storage_config_load(&storage_info);
430 register_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done);
431 register_notifier(DEVICE_NOTIFIER_POWEROFF, lowmem_poweroff);
432 ret = register_edbus_method(DEVICED_PATH_STORAGE, edbus_methods, ARRAY_SIZE(edbus_methods));
434 _E("fail to init edbus method(%d)", ret);
436 if (__run_mem_trim() < 0) {
437 _E("fail mem trim timer start");
441 static void lowmem_exit(void *data)
443 unregister_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done);
444 unregister_notifier(DEVICE_NOTIFIER_POWEROFF, lowmem_poweroff);
447 static const struct device_ops lowmem_device_ops = {
448 .priority = DEVICE_PRIORITY_NORMAL,
454 DEVICE_OPS_REGISTER(&lowmem_device_ops)