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>
34 #include "core/devices.h"
35 #include "display/poll.h"
36 #include "display/core.h"
37 #include "core/edbus-handler.h"
38 #include "core/common.h"
39 #include "core/device-notifier.h"
41 #define PREDEF_SET_DATETIME "set_datetime"
42 #define PREDEF_SET_TIMEZONE "set_timezone"
44 #ifndef TFD_TIMER_CANCELON_SET
45 #define TFD_TIMER_CANCELON_SET (1<<1)
48 #define O_CLOEXEC 0x2000000
52 #define O_NONBLOCK 0x4000
56 #define TFD_CLOEXEC O_CLOEXEC
60 #define TFD_NONBLOCK O_NONBLOCK
63 #define TIME_CHANGE_SIGNAL "STimeChanged"
65 static const char default_rtc0[] = "/dev/rtc0";
66 static const char default_rtc1[] = "/dev/rtc1";
67 static const char default_localtime[] = "/opt/etc/localtime";
69 static const time_t default_time = 2147483645; // max(32bit) -3sec
70 static Ecore_Fd_Handler *tfdh = NULL; // tfd change noti
72 static Eina_Bool tfd_cb(void *data, Ecore_Fd_Handler * fd_handler);
73 static int timerfd_check_stop(int fd);
74 static int timerfd_check_start(void);
76 char *substring(const char *str, size_t begin, size_t len)
78 if (str == 0 || strlen(str) == 0 || strlen(str) < begin
79 || strlen(str) < (begin + len))
82 return strndup(str + begin, len);
85 int handle_timezone(char *str)
91 const char *sympath = default_localtime;
95 const char *tzpath = str;
97 _D("TZPATH = %s", tzpath);
99 if (stat(tzpath, &sts) == -1 && errno == ENOENT) {
100 _E("invalid tzpath(%s)", tzpath);
104 /* FIXME for debugging purpose */
106 ts = localtime(&now);
107 _D("cur local time is %s", asctime(ts));
109 /* unlink current link
110 * eg. rm /opt/etc/localtime */
111 if (stat(sympath, &sts) == -1 && errno == ENOENT) {
114 ret = unlink(sympath);
116 _E("unlink error : [%d]%s", ret,
120 _D("unlink success");
124 * eg. ln -s /usr/share/zoneinfo/Asia/Seoul /opt/etc/localtime */
125 ret = symlink(tzpath, sympath);
127 _E("symlink error : [%d]%s", ret, strerror(errno));
130 _D("symlink success");
134 /* FIXME for debugging purpose */
135 ts = localtime(&now);
136 _D("new local time is %s", asctime(ts));
141 * TODO : error handling code should be added here.
143 int handle_date(char *str)
152 tmp = (long int)atoi(str);
153 timet = (time_t) tmp;
155 _D("ctime = %s", ctime(&timet));
156 vconf_set_int(VCONFKEY_SYSTEM_TIMECHANGE, timet);
161 int set_datetime_action(int argc, char **argv)
164 unsigned int pm_state;
167 if (vconf_get_int(VCONFKEY_PM_STATE, &ret) != 0)
168 _E("Fail to get vconf value for pm state\n");
176 pm_lock_internal(INTERNAL_LOCK_TIME, pm_state, STAY_CUR_STATE, 0);
177 ret = handle_date(argv[0]);
178 pm_unlock_internal(INTERNAL_LOCK_TIME, pm_state, STAY_CUR_STATE);
182 int set_timezone_action(int argc, char **argv)
185 unsigned int pm_state;
188 if (vconf_get_int(VCONFKEY_PM_STATE, &ret) != 0)
189 _E("Fail to get vconf value for pm state\n");
197 pm_lock_internal(INTERNAL_LOCK_TIME, pm_state, STAY_CUR_STATE, 0);
198 ret = handle_timezone(argv[0]);
199 pm_unlock_internal(INTERNAL_LOCK_TIME, pm_state, STAY_CUR_STATE);
203 static void time_changed_broadcast(void)
205 broadcast_edbus_signal(DEVICED_PATH_TIME, DEVICED_INTERFACE_TIME,
206 TIME_CHANGE_SIGNAL, NULL, NULL);
209 static int timerfd_check_start(void)
212 struct itimerspec tmr;
214 if ((tfd = timerfd_create(CLOCK_REALTIME,TFD_NONBLOCK|TFD_CLOEXEC)) == -1) {
215 _E("error timerfd_create() %d", errno);
220 tfdh = ecore_main_fd_handler_add(tfd,ECORE_FD_READ,tfd_cb,NULL,NULL,NULL);
222 _E("error ecore_main_fd_handler_add");
225 memset(&tmr, 0, sizeof(tmr));
226 tmr.it_value.tv_sec = default_time;
228 if (timerfd_settime(tfd,TFD_TIMER_ABSTIME|TFD_TIMER_CANCELON_SET,&tmr,NULL) < 0) {
229 _E("error timerfd_settime() %d", errno);
235 static int timerfd_check_stop(int tfd)
238 ecore_main_fd_handler_del(tfdh);
248 static Eina_Bool tfd_cb(void *data, Ecore_Fd_Handler * fd_handler)
254 if (!ecore_main_fd_handler_active_get(fd_handler,ECORE_FD_READ)) {
255 _E("error ecore_main_fd_handler_get()");
259 if((tfd = ecore_main_fd_handler_fd_get(fd_handler)) == -1) {
260 _E("error ecore_main_fd_handler_fd_get()");
264 ret = read(tfd,&ticks,sizeof(ticks));
265 if (ret < 0 && errno == ECANCELED) {
266 vconf_set_int(VCONFKEY_SYSMAN_STIME, VCONFKEY_SYSMAN_STIME_CHANGED);
267 time_changed_broadcast();
268 timerfd_check_stop(tfd);
269 _D("NOTIFICATION here");
270 timerfd_check_start();
272 _E("unexpected read (err:%d)", errno);
278 static DBusMessage *dbus_time_handler(E_DBus_Object *obj, DBusMessage *msg)
281 DBusMessageIter iter;
289 dbus_error_init(&err);
291 if (!dbus_message_get_args(msg, &err,
292 DBUS_TYPE_STRING, &type_str,
293 DBUS_TYPE_INT32, &argc,
294 DBUS_TYPE_STRING, &argv, DBUS_TYPE_INVALID)) {
295 _E("there is no message");
301 _E("message is invalid!");
306 pid = get_edbus_sender_pid(msg);
307 if (kill(pid, 0) == -1) {
308 _E("%d process does not exist, dbus ignored!", pid);
313 if (strncmp(type_str, PREDEF_SET_DATETIME, strlen(PREDEF_SET_DATETIME)) == 0)
314 ret = set_datetime_action(argc, (char **)&argv);
315 else if (strncmp(type_str, PREDEF_SET_TIMEZONE, strlen(PREDEF_SET_TIMEZONE)) == 0)
316 ret = set_timezone_action(argc, (char **)&argv);
318 reply = dbus_message_new_method_return(msg);
319 dbus_message_iter_init_append(reply, &iter);
320 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
325 static const struct edbus_method edbus_methods[] = {
326 { PREDEF_SET_DATETIME, "sis", "i", dbus_time_handler },
327 { PREDEF_SET_TIMEZONE, "sis", "i", dbus_time_handler },
331 static int time_lcd_changed_cb(void *data)
333 int lcd_state = (int)data;
336 if (lcd_state < S_LCDOFF)
339 lcd_state = check_lcdoff_lock_state();
340 if (lcd_state || !tfdh)
342 tfd = ecore_main_fd_handler_fd_get(tfdh);
347 timerfd_check_stop(tfd);
353 timerfd_check_start();
358 static void time_init(void *data)
362 ret = register_edbus_method(DEVICED_PATH_SYSNOTI, edbus_methods, ARRAY_SIZE(edbus_methods));
364 _E("fail to init edbus method(%d)", ret);
366 if (timerfd_check_start() == -1) {
367 _E("fail system time change detector init");
369 register_notifier(DEVICE_NOTIFIER_LCD, time_lcd_changed_cb);
372 static const struct device_ops time_device_ops = {
373 .priority = DEVICE_PRIORITY_NORMAL,
378 DEVICE_OPS_REGISTER(&time_device_ops)