--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <map>
+#include <list>
+#include <system_info.h>
+#include <alarm.h>
+
+#include <ContextTypes.h>
+#include <JobSchedulerTypesPrivate.h>
+#include <job_scheduler_types_internal.h>
+#include <JobContext.h>
+#include <Attribute.h>
+#include "../JobState.h"
+#include "../ContextPublisher.h"
+#include "../IContextObserver.h"
+
+#define ONE_DAY 86400
+
+using namespace ctx;
+
+namespace {
+ class AlarmEvent : public ContextPublisher {
+ public:
+ AlarmEvent(uid_t uid);
+ ~AlarmEvent();
+
+ void addObserver(IContextObserver* observer, void* userData);
+ void removeObserver(IContextObserver* observer);
+
+ const char* getUri() const;
+ const char* getPrivilege() const;
+
+ bool isReadable() const;
+
+ bool isReady();
+ const Json::Value& getData();
+
+ protected:
+ void read();
+ void subscribe();
+ void unsubscribe();
+
+ private:
+ void __addTimer(IContextObserver* observer);
+ void __addTimer(IContextObserver* observer, int minOfDay);
+ void __removeTimer(IContextObserver* observer);
+
+ int __setAlarm(int minOfDay);
+ void __unsetAlarm(int alarmId);
+
+ static int __alarmCb(int alarmId, void* cbData);
+
+ struct TimerEntry {
+ int alarmId;
+ std::list<IContextObserver*> observers;
+ TimerEntry() : alarmId(-1) {}
+ };
+
+ std::map<int, TimerEntry> __timers;
+ };
+}
+
+REGISTER_PUBLISHER(CTX_SCHED_URI_PREFIX "event/time", AlarmEvent)
+
+AlarmEvent::AlarmEvent(uid_t uid)
+{
+ _D("Created");
+}
+
+AlarmEvent::~AlarmEvent()
+{
+ unsubscribe();
+
+ _D("Destroyed");
+}
+
+void AlarmEvent::addObserver(IContextObserver* observer, void* userData)
+{
+ __observers.emplace_back(observer, userData);
+
+ __addTimer(observer);
+}
+
+void AlarmEvent::removeObserver(IContextObserver* observer)
+{
+ __observers.remove_if(
+ [&](const ObserverInfo& info)->bool {
+ return (info.observer == observer);
+ });
+
+ __removeTimer(observer);
+}
+
+const char* AlarmEvent::getUri() const
+{
+ // This is not an officially supported context item
+ return CTX_SCHED_URI_PREFIX "event/time";
+}
+
+const char* AlarmEvent::getPrivilege() const
+{
+ return "http://tizen.org/privilege/alarm.set";
+}
+
+bool AlarmEvent::isReadable() const
+{
+ return false;
+}
+
+bool AlarmEvent::isReady()
+{
+ return true;
+}
+
+const Json::Value& AlarmEvent::getData()
+{
+ static const char* weekDays[] = {
+ VALUE(SUN), VALUE(MON), VALUE(TUE), VALUE(WED), VALUE(THU), VALUE(FRI), VALUE(SAT)
+ };
+
+ time_t rawtime;
+ struct tm timeInfo;
+
+ time(&rawtime);
+ tzset();
+ localtime_r(&rawtime, &timeInfo);
+
+ int dayOfMonth = timeInfo.tm_mday;
+ int minuteOfDay = timeInfo.tm_hour * 60 + timeInfo.tm_min;
+ const char* weekDay = weekDays[timeInfo.tm_wday];
+
+ __data[NAME(DAY_OF_MONTH)] = dayOfMonth;
+ __data[NAME(DAY_OF_WEEK)] = weekDay;
+ __data[NAME(TIME_OF_DAY)] = minuteOfDay;
+
+ return __data;
+}
+
+void AlarmEvent::read()
+{
+}
+
+void AlarmEvent::subscribe()
+{
+}
+
+void AlarmEvent::unsubscribe()
+{
+}
+
+void AlarmEvent::__addTimer(IContextObserver* observer)
+{
+ _D("Add Observer: %p", observer);
+
+ JobState* jobState = dynamic_cast<JobState*>(observer);
+ IF_FAIL_VOID_TAG(jobState, _W, "Casting failed");
+
+ OnDemandJobInfo* jobInfo = static_cast<OnDemandJobInfo*>(jobState->getJobInfo());
+
+ for (auto& trg : jobInfo->getTriggers()) {
+ if (trg->getUri() != getUri()) continue;
+
+ IntegerAttribute* attr = dynamic_cast<IntegerAttribute*>(trg->getAttribute(NAME(TIME_OF_DAY)));
+ if (!attr) continue;
+
+ for (auto& t : attr->getTargets()) {
+ __addTimer(observer, t);
+ }
+ }
+}
+
+void AlarmEvent::__addTimer(IContextObserver* observer, int minOfDay)
+{
+ _D("Add Observer: %p, Time: %d", observer, minOfDay);
+
+ TimerEntry& entry = __timers[minOfDay];
+
+ if (entry.alarmId < 0)
+ entry.alarmId = __setAlarm(minOfDay);
+
+ entry.observers.push_back(observer);
+}
+
+void AlarmEvent::__removeTimer(IContextObserver* observer)
+{
+ _D("Remove Observer: %p", observer);
+
+ for (auto& it : __timers) {
+ TimerEntry& entry = it.second;
+ entry.observers.remove(observer);
+ if (entry.observers.empty()) {
+ __unsetAlarm(entry.alarmId);
+ entry.alarmId = -1;
+ }
+ }
+}
+
+int AlarmEvent::__setAlarm(int minOfDay)
+{
+ time_t rawTime;
+ struct tm timeInfo;
+
+ time(&rawTime);
+ tzset();
+ localtime_r(&rawTime, &timeInfo);
+
+ time_t now = timeInfo.tm_hour * 3600 + timeInfo.tm_min * 60 + timeInfo.tm_sec;
+ time_t diff = minOfDay * 60 - now;
+ if (diff < 0)
+ diff += ONE_DAY;
+
+ int alarmId = -1;
+ alarmmgr_add_alarm_withcb(ALARM_TYPE_VOLATILE, diff, ONE_DAY, __alarmCb, this, &alarmId);
+ _D("Alarm-%d at %d (S-%d)", alarmId, minOfDay, static_cast<int>(diff));
+
+ return alarmId;
+}
+
+void AlarmEvent::__unsetAlarm(int alarmId)
+{
+ _D("Unset Alarm-%d", alarmId);
+
+ alarmmgr_remove_alarm(alarmId);
+}
+
+int AlarmEvent::__alarmCb(int alarmId, void* cbData)
+{
+ AlarmEvent* pubs = static_cast<AlarmEvent*>(cbData);
+ pubs->notifyObservers();
+ return 0;
+}