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.
23 #include <sys/types.h>
28 #include <sys/ioctl.h>
29 #include <linux/rtc.h>
31 #include <sys/timerfd.h>
33 #include "core/data.h"
34 #include "core/queue.h"
36 #include "core/devices.h"
37 #include "display/poll.h"
38 #include "display/core.h"
39 #include "core/edbus-handler.h"
40 #include "core/common.h"
41 #include "core/device-notifier.h"
43 #define PREDEF_SET_DATETIME "set_datetime"
44 #define PREDEF_SET_TIMEZONE "set_timezone"
46 #ifndef TFD_TIMER_CANCELON_SET
47 #define TFD_TIMER_CANCELON_SET (1<<1)
50 #define O_CLOEXEC 0x2000000
54 #define O_NONBLOCK 0x4000
58 #define TFD_CLOEXEC O_CLOEXEC
62 #define TFD_NONBLOCK O_NONBLOCK
65 #define TIME_CHANGE_SIGNAL "STimeChanged"
67 static const char default_rtc0[] = "/dev/rtc0";
68 static const char default_rtc1[] = "/dev/rtc1";
69 static const char default_localtime[] = "/opt/etc/localtime";
71 static const time_t default_time = 2147483645; // max(32bit) -3sec
72 static Ecore_Fd_Handler *tfdh = NULL; // tfd change noti
74 static Eina_Bool tfd_cb(void *data, Ecore_Fd_Handler * fd_handler);
75 static int timerfd_check_stop(int fd);
76 static int timerfd_check_start(void);
78 char *substring(const char *str, size_t begin, size_t len)
80 if (str == 0 || strlen(str) == 0 || strlen(str) < begin
81 || strlen(str) < (begin + len))
84 return strndup(str + begin, len);
87 int handle_timezone(char *str)
93 const char *sympath = default_localtime;
97 const char *tzpath = str;
99 _D("TZPATH = %s", tzpath);
101 if (stat(tzpath, &sts) == -1 && errno == ENOENT) {
102 _E("invalid tzpath(%s)", tzpath);
106 /* FIXME for debugging purpose */
108 ts = localtime(&now);
109 _D("cur local time is %s", asctime(ts));
111 /* unlink current link
112 * eg. rm /opt/etc/localtime */
113 if (stat(sympath, &sts) == -1 && errno == ENOENT) {
116 ret = unlink(sympath);
118 _E("unlink error : [%d]%s", ret,
122 _D("unlink success");
126 * eg. ln -s /usr/share/zoneinfo/Asia/Seoul /opt/etc/localtime */
127 ret = symlink(tzpath, sympath);
129 _E("symlink error : [%d]%s", ret, strerror(errno));
132 _D("symlink success");
136 /* FIXME for debugging purpose */
137 ts = localtime(&now);
138 _D("new local time is %s", asctime(ts));
143 * TODO : error handling code should be added here.
145 int handle_date(char *str)
154 tmp = (long int)atoi(str);
155 timet = (time_t) tmp;
157 _D("ctime = %s", ctime(&timet));
158 vconf_set_int(VCONFKEY_SYSTEM_TIMECHANGE, timet);
163 int set_datetime_action(int argc, char **argv)
166 unsigned int pm_state;
169 if (vconf_get_int(VCONFKEY_PM_STATE, &ret) != 0)
170 _E("Fail to get vconf value for pm state\n");
178 pm_lock_internal(INTERNAL_LOCK_TIME, pm_state, STAY_CUR_STATE, 0);
179 ret = handle_date(argv[0]);
180 pm_unlock_internal(INTERNAL_LOCK_TIME, pm_state, STAY_CUR_STATE);
184 int set_timezone_action(int argc, char **argv)
187 unsigned int pm_state;
190 if (vconf_get_int(VCONFKEY_PM_STATE, &ret) != 0)
191 _E("Fail to get vconf value for pm state\n");
199 pm_lock_internal(INTERNAL_LOCK_TIME, pm_state, STAY_CUR_STATE, 0);
200 ret = handle_timezone(argv[0]);
201 pm_unlock_internal(INTERNAL_LOCK_TIME, pm_state, STAY_CUR_STATE);
205 static void time_changed_broadcast(void)
207 broadcast_edbus_signal(DEVICED_PATH_TIME, DEVICED_INTERFACE_TIME,
208 TIME_CHANGE_SIGNAL, NULL, NULL);
211 static int timerfd_check_start(void)
214 struct itimerspec tmr;
216 if ((tfd = timerfd_create(CLOCK_REALTIME,TFD_NONBLOCK|TFD_CLOEXEC)) == -1) {
217 _E("error timerfd_create() %d", errno);
222 tfdh = ecore_main_fd_handler_add(tfd,ECORE_FD_READ,tfd_cb,NULL,NULL,NULL);
224 _E("error ecore_main_fd_handler_add");
227 memset(&tmr, 0, sizeof(tmr));
228 tmr.it_value.tv_sec = default_time;
230 if (timerfd_settime(tfd,TFD_TIMER_ABSTIME|TFD_TIMER_CANCELON_SET,&tmr,NULL) < 0) {
231 _E("error timerfd_settime() %d", errno);
237 static int timerfd_check_stop(int tfd)
240 ecore_main_fd_handler_del(tfdh);
250 static Eina_Bool tfd_cb(void *data, Ecore_Fd_Handler * fd_handler)
256 if (!ecore_main_fd_handler_active_get(fd_handler,ECORE_FD_READ)) {
257 _E("error ecore_main_fd_handler_get()");
261 if((tfd = ecore_main_fd_handler_fd_get(fd_handler)) == -1) {
262 _E("error ecore_main_fd_handler_fd_get()");
266 ret = read(tfd,&ticks,sizeof(ticks));
267 if (ret < 0 && errno == ECANCELED) {
268 vconf_set_int(VCONFKEY_SYSMAN_STIME, VCONFKEY_SYSMAN_STIME_CHANGED);
269 time_changed_broadcast();
270 timerfd_check_stop(tfd);
271 _D("NOTIFICATION here");
272 timerfd_check_start();
274 _E("unexpected read (err:%d)", errno);
280 static DBusMessage *dbus_time_handler(E_DBus_Object *obj, DBusMessage *msg)
283 DBusMessageIter iter;
291 dbus_error_init(&err);
293 if (!dbus_message_get_args(msg, &err,
294 DBUS_TYPE_STRING, &type_str,
295 DBUS_TYPE_INT32, &argc,
296 DBUS_TYPE_STRING, &argv, DBUS_TYPE_INVALID)) {
297 _E("there is no message");
303 _E("message is invalid!");
308 pid = get_edbus_sender_pid(msg);
309 if (kill(pid, 0) == -1) {
310 _E("%d process does not exist, dbus ignored!", pid);
315 if (strncmp(type_str, PREDEF_SET_DATETIME, strlen(PREDEF_SET_DATETIME)) == 0)
316 ret = set_datetime_action(argc, (char **)&argv);
317 else if (strncmp(type_str, PREDEF_SET_TIMEZONE, strlen(PREDEF_SET_TIMEZONE)) == 0)
318 ret = set_timezone_action(argc, (char **)&argv);
320 reply = dbus_message_new_method_return(msg);
321 dbus_message_iter_init_append(reply, &iter);
322 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
327 static const struct edbus_method edbus_methods[] = {
328 { PREDEF_SET_DATETIME, "sis", "i", dbus_time_handler },
329 { PREDEF_SET_TIMEZONE, "sis", "i", dbus_time_handler },
333 static int time_lcd_changed_cb(void *data)
335 int lcd_state = (int)data;
338 if (lcd_state < S_LCDOFF)
341 lcd_state = check_lcdoff_lock_state();
342 if (lcd_state || !tfdh)
344 tfd = ecore_main_fd_handler_fd_get(tfdh);
349 timerfd_check_stop(tfd);
355 timerfd_check_start();
360 static void time_init(void *data)
364 ret = register_edbus_method(DEVICED_PATH_SYSNOTI, edbus_methods, ARRAY_SIZE(edbus_methods));
366 _E("fail to init edbus method(%d)", ret);
368 register_action(PREDEF_SET_DATETIME, set_datetime_action, NULL, NULL);
369 register_action(PREDEF_SET_TIMEZONE, set_timezone_action, NULL, NULL);
370 if (timerfd_check_start() == -1) {
371 _E("fail system time change detector init");
373 register_notifier(DEVICE_NOTIFIER_LCD, time_lcd_changed_cb);
376 static const struct device_ops time_device_ops = {
377 .priority = DEVICE_PRIORITY_NORMAL,
382 DEVICE_OPS_REGISTER(&time_device_ops)