From 2c77b7e18df874428b0ec9d65a6b3386915f60a3 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 18 Aug 2014 23:03:38 -0500 Subject: use powerd for hw alarm wakeups --- CMakeLists.txt | 6 - debian/control | 2 - include/datetime/wakeup-timer-powerd.h | 62 +++++++ include/datetime/wakeup-timer-uha.h | 64 ------- src/CMakeLists.txt | 6 +- src/main.cpp | 22 +-- src/wakeup-timer-powerd.cpp | 304 +++++++++++++++++++++++++++++++++ src/wakeup-timer-uha.cpp | 175 ------------------- 8 files changed, 370 insertions(+), 271 deletions(-) create mode 100644 include/datetime/wakeup-timer-powerd.h delete mode 100644 include/datetime/wakeup-timer-uha.h create mode 100644 src/wakeup-timer-powerd.cpp delete mode 100644 src/wakeup-timer-uha.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b4987e..4d2fd95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,12 +44,6 @@ pkg_check_modules (SERVICE_DEPS REQUIRED properties-cpp>=0.0.1) include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS}) -CHECK_INCLUDE_FILE(ubuntu/hardware/alarm.h HAVE_UBUNTU_HW_ALARM_H) -if (HAVE_UBUNTU_HW_ALARM_H) - set (SERVICE_DEPS_LIBRARIES -lubuntu_platform_hardware_api ${SERVICE_DEPS_LIBRARIES}) - add_definitions(-DHAVE_UBUNTU_HW_ALARM_H) -endif () - ## ## custom targets ## diff --git a/debian/control b/debian/control index 9e7133e..4b5f893 100644 --- a/debian/control +++ b/debian/control @@ -22,8 +22,6 @@ Build-Depends: cmake, libedataserver1.2-dev (>= 3.5), liburl-dispatcher1-dev, libproperties-cpp-dev, - libubuntu-platform-hardware-api-headers [armhf i386 amd64], - libubuntu-platform-hardware-api-dev [armhf i386 amd64], libdbustest1-dev, locales, Standards-Version: 3.9.3 diff --git a/include/datetime/wakeup-timer-powerd.h b/include/datetime/wakeup-timer-powerd.h new file mode 100644 index 0000000..6adbbb8 --- /dev/null +++ b/include/datetime/wakeup-timer-powerd.h @@ -0,0 +1,62 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_WAKEUP_TIMER_POWERD_H +#define INDICATOR_DATETIME_WAKEUP_TIMER_POWERD_H + +#include +#include + +#include // std::unique_ptr, std::shared_ptr + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +/** + * \brief a WakeupTimer implemented with g_timeout_add() + */ +class PowerdWakeupTimer: public WakeupTimer +{ +public: + PowerdWakeupTimer(const std::shared_ptr&); + ~PowerdWakeupTimer(); + void set_wakeup_time(const DateTime&); + core::Signal<>& timeout(); + +private: + PowerdWakeupTimer(const PowerdWakeupTimer&) =delete; + PowerdWakeupTimer& operator=(const PowerdWakeupTimer&) =delete; + class Impl; + std::unique_ptr p; +}; + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_WAKEUP_TIMER_MAINLOOP_H diff --git a/include/datetime/wakeup-timer-uha.h b/include/datetime/wakeup-timer-uha.h deleted file mode 100644 index 093548b..0000000 --- a/include/datetime/wakeup-timer-uha.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2014 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * Authors: - * Charles Kerr - */ - -#ifndef INDICATOR_DATETIME_WAKEUP_TIMER_UHA_H -#define INDICATOR_DATETIME_WAKEUP_TIMER_UHA_H - -#include -#include - -#include // std::unique_ptr, std::shared_ptr - -namespace unity { -namespace indicator { -namespace datetime { - -/*** -**** -***/ - -/** - * \brief a WakeupTimer implemented the UbuntuHardwareAlarm API - */ -class UhaWakeupTimer: public WakeupTimer -{ -public: - UhaWakeupTimer(const std::shared_ptr&); - ~UhaWakeupTimer(); - void set_wakeup_time (const DateTime&); - core::Signal<>& timeout(); - - static bool is_supported(); - -private: - UhaWakeupTimer(const UhaWakeupTimer&) =delete; - UhaWakeupTimer& operator= (const UhaWakeupTimer&) =delete; - class Impl; - std::unique_ptr p; -}; - -/*** -**** -***/ - -} // namespace datetime -} // namespace indicator -} // namespace unity - -#endif // INDICATOR_DATETIME_WAKEUP_TIMER_UHA_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a466a48..e583334 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,10 +35,8 @@ set (SERVICE_CXX_SOURCES timezone-geoclue.cpp timezones-live.cpp utils.c - wakeup-timer-mainloop.cpp) -if (HAVE_UBUNTU_HW_ALARM_H) - set (SERVICE_CXX_SOURCES ${SERVICE_CXX_SOURCES} wakeup-timer-uha.cpp) -endif () + wakeup-timer-mainloop.cpp + wakeup-timer-powerd.cpp) # generated sources include (GdbusCodegen) diff --git a/src/main.cpp b/src/main.cpp index eb90020..48d3d20 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,13 +31,9 @@ #include #include #include -#include +#include #include -#ifdef HAVE_UBUNTU_HW_ALARM_H - #include -#endif - #include // bindtextdomain() #include @@ -66,20 +62,6 @@ namespace return engine; } - std::shared_ptr create_wakeup_timer(const std::shared_ptr& clock) - { - std::shared_ptr wakeup_timer; - -#ifdef HAVE_UBUNTU_HW_ALARM_H - if (UhaWakeupTimer::is_supported()) // prefer to use the platform API - wakeup_timer = std::make_shared(clock); - else -#endif - wakeup_timer = std::make_shared(clock); - - return wakeup_timer; - } - std::shared_ptr create_state(const std::shared_ptr& engine, const std::shared_ptr& tz) { @@ -120,7 +102,7 @@ namespace upcoming_planner->date().set(now); }); - auto wakeup_timer = create_wakeup_timer(clock); + auto wakeup_timer = std::make_shared(clock); return std::make_shared(clock, upcoming_planner, wakeup_timer); } } diff --git a/src/wakeup-timer-powerd.cpp b/src/wakeup-timer-powerd.cpp new file mode 100644 index 0000000..b3dcec8 --- /dev/null +++ b/src/wakeup-timer-powerd.cpp @@ -0,0 +1,304 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +#include // BUS_POWERD_NAME + +#include // std::shared_ptr + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +class PowerdWakeupTimer::Impl +{ +public: + + Impl(const std::shared_ptr& clock): + m_clock(clock), + m_cancellable(g_cancellable_new()) + { + g_bus_get(G_BUS_TYPE_SYSTEM, m_cancellable, on_bus_ready, this); + } + + ~Impl() + { + clear_current_cookie(); + + g_cancellable_cancel(m_cancellable); + g_clear_object(&m_cancellable); + + if (m_sub_id) + g_dbus_connection_signal_unsubscribe(m_bus.get(), m_sub_id); + + if (m_watch_tag) + g_bus_unwatch_name(m_watch_tag); + } + + void set_wakeup_time(const DateTime& d) + { + m_wakeup_time = d; + update_cookie(); + } + + core::Signal<>& timeout() { return m_timeout; } + +private: + + void emit_timeout() { return m_timeout(); } + + static void on_bus_ready(GObject * /*unused*/, + GAsyncResult * res, + gpointer gself) + { + GError * error; + GDBusConnection * bus; + + error = nullptr; + bus = g_bus_get_finish(res, &error); + if (bus == nullptr) + { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("%s Couldn't get system bus: %s", G_STRLOC, error->message); + } + else + { + static_cast(gself)->init_bus(bus); + } + + g_clear_object(&bus); + g_clear_error(&error); + } + + void init_bus(GDBusConnection* connection) + { + m_bus.reset(G_DBUS_CONNECTION(g_object_ref(G_OBJECT(connection))), + [](GDBusConnection *c){g_object_unref(G_OBJECT(c));}); + + m_sub_id = g_dbus_connection_signal_subscribe(m_bus.get(), + BUS_POWERD_NAME, + BUS_POWERD_INTERFACE, + "Wakeup", + BUS_POWERD_PATH, + nullptr, + G_DBUS_SIGNAL_FLAGS_NONE, + on_wakeup_signal, + this, // userdata + nullptr); // userdata free + + m_watch_tag = g_bus_watch_name_on_connection(m_bus.get(), + BUS_POWERD_NAME, + G_BUS_NAME_WATCHER_FLAGS_NONE, + on_name_appeared_static, + nullptr, // name-vanished, + this, // userdata + nullptr); // userdata free + } + + static void + on_wakeup_signal(GDBusConnection * /*connection*/, + const gchar * sender_name, + const gchar * /*object_path*/, + const gchar * /*interface_name*/, + const gchar * signal_name, + GVariant * /*parameters*/, + gpointer gself) + { + g_debug("%s got DBus signal '%s' from '%s'", G_STRLOC, signal_name, sender_name); + static_cast(gself)->emit_timeout(); + } + + static void + on_name_appeared_static(GDBusConnection * /*connection*/, + const gchar * name, + const gchar * name_owner, + gpointer gself) + { + g_debug("%s %s owns %s now; let's ask for a new cookie", G_STRLOC, name, name_owner); + static_cast(gself)->update_cookie(); + } + + /*** + **** requestWakeup + ***/ + + void update_cookie() + { + if (!m_bus) + return; + + // if we've already got a cookie, clear it + clear_current_cookie(); + g_warn_if_fail(m_cookie.empty()); + + // get a new cookie, if necessary + if (m_wakeup_time.is_set()) + { + g_debug("%s calling %s::requestWakeup(%s)", + G_STRLOC, BUS_POWERD_NAME, + m_wakeup_time.format("%F %T").c_str()); + + auto args = g_variant_new("(st)", + GETTEXT_PACKAGE, + uint64_t(m_wakeup_time.to_unix())); + + g_dbus_connection_call(m_bus.get(), + BUS_POWERD_NAME, + BUS_POWERD_PATH, + BUS_POWERD_INTERFACE, + "requestWakeup", // method_name + args, + G_VARIANT_TYPE("(s)"), // reply_type + G_DBUS_CALL_FLAGS_NONE, + -1, // use default timeout + m_cancellable, + on_request_wakeup_done, + this); + } + } + + static void on_request_wakeup_done(GObject * o, + GAsyncResult * res, + gpointer gself) + { + GError * error; + GVariant * ret; + + error = nullptr; + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(o), res, &error); + if (ret == nullptr) + { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("%s Could not set hardware wakeup: %s", G_STRLOC, error->message); + } + else + { + const char* s = NULL; + g_variant_get(ret, "(&s)", &s); + g_debug("%s %s::requestWakeup() sent cookie %s", + G_STRLOC, BUS_POWERD_NAME, s); + + auto& cookie = static_cast(gself)->m_cookie; + if (s != nullptr) + cookie = s; + else + cookie.clear(); + } + + // cleanup + g_clear_pointer(&ret, g_variant_unref); + g_clear_error(&error); + } + + /*** + **** clearWakeup + ***/ + + void clear_current_cookie() + { + if (!m_cookie.empty()) + { + g_debug("%s calling %s::clearWakeup(%s)", + G_STRLOC, BUS_POWERD_NAME, m_cookie.c_str()); + + g_dbus_connection_call(m_bus.get(), + BUS_POWERD_NAME, + BUS_POWERD_PATH, + BUS_POWERD_INTERFACE, + "clearWakeup", // method_name + g_variant_new("(s)", m_cookie.c_str()), + nullptr, // no response type + G_DBUS_CALL_FLAGS_NONE, + -1, // use default timeout + nullptr, // cancellable + on_clear_wakeup_done, + nullptr); + m_cookie.clear(); + } + } + + // this is only here to log errors + static void on_clear_wakeup_done(GObject * o, + GAsyncResult * res, + gpointer /*unused*/) + { + GError * error; + GVariant * ret; + + error = nullptr; + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(o), res, &error); + if (!ret && !g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("%s Couldn't clear hardware wakeup: %s", G_STRLOC, error->message); + + // cleanup + g_clear_pointer(&ret, g_variant_unref); + g_clear_error(&error); + } + + /*** + **** + ***/ + + core::Signal<> m_timeout; + const std::shared_ptr& m_clock; + DateTime m_wakeup_time; + + std::shared_ptr m_bus; + GCancellable * m_cancellable = nullptr; + std::string m_cookie; + guint m_watch_tag = 0; + guint m_sub_id = 0; +}; + +/*** +**** +***/ + +PowerdWakeupTimer::PowerdWakeupTimer(const std::shared_ptr& clock): + p(new Impl(clock)) +{ +} + +PowerdWakeupTimer::~PowerdWakeupTimer() +{ +} + +void PowerdWakeupTimer::set_wakeup_time(const DateTime& d) +{ + p->set_wakeup_time(d); +} + +core::Signal<>& PowerdWakeupTimer::timeout() +{ + return p->timeout(); +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/wakeup-timer-uha.cpp b/src/wakeup-timer-uha.cpp deleted file mode 100644 index 437eda2..0000000 --- a/src/wakeup-timer-uha.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2014 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * Authors: - * Charles Kerr - */ - -#include - -#include - -#include - -#include - -#include // struct timespec -#include -#include - -namespace unity { -namespace indicator { -namespace datetime { - -/*** -**** -***/ - -class UhaWakeupTimer::Impl -{ - -public: - - Impl(const std::shared_ptr& clock): - m_clock(clock), - m_hardware_alarm(u_hardware_alarm_create()) - { - // fire up a worker thread that initially just sleeps - set_wakeup_time_to_the_distant_future(); - m_thread = std::move(std::thread([&](){threadfunc();})); - } - - ~Impl() - { - // tell the worker thread to wake up and exit - m_yielding = true; - set_wakeup_time(m_clock->localtime().add_full(0,0,0,0,0,0.1)); - - // wait for it to happen - if (m_thread.joinable()) - m_thread.join(); - - g_idle_remove_by_data(this); - - u_hardware_alarm_unref(m_hardware_alarm); - } - - void set_wakeup_time(const DateTime& d) - { - g_debug("%s %s", G_STRLOC, G_STRFUNC); - std::lock_guard lg(m_mutex); - - const auto wakeup_time = d.to_unix(); - - // simple sanity check: don't try to wait for something that's already passed - const auto now = m_clock->localtime().to_unix(); - g_return_if_fail (wakeup_time >= now); - - struct timespec sleep_interval; - sleep_interval.tv_sec = wakeup_time; - sleep_interval.tv_nsec = 0; - g_debug("%s %s setting hardware wakeup time to %s (%zu seconds from now)", - G_STRLOC, G_STRFUNC, - d.format("%F %T").c_str(), - (size_t)(wakeup_time - now)); - u_hardware_alarm_set_relative_to_with_behavior(m_hardware_alarm, - U_HARDWARE_ALARM_TIME_REFERENCE_RTC, - U_HARDWARE_ALARM_SLEEP_BEHAVIOR_WAKEUP_DEVICE, - &sleep_interval); - } - - core::Signal<>& timeout() { return m_timeout; } - -private: - - void set_wakeup_time_to_the_distant_future() - { - const auto tomorrow = m_clock->localtime().add_full(0,0,1,0,0,0); - set_wakeup_time(tomorrow); - } - - static gboolean kick_idle (gpointer gself) - { - static_cast(gself)->m_timeout(); - - return G_SOURCE_REMOVE; - } - - void threadfunc() - { - while (!m_yielding) - { - // wait for the next hw alarm - UHardwareAlarmWaitResult wait_result; - g_debug ("calling wait_for_next_alarm"); - auto rc = u_hardware_alarm_wait_for_next_alarm(m_hardware_alarm, &wait_result); - g_return_if_fail (rc == U_STATUS_SUCCESS); - - // set a long wakeup interval for the next iteration of the loop. - // if there's another Appointment queued up by the Planner, - // our timeout() listener will call set_wakeup_time() to set the - // real wakeup interval. - set_wakeup_time_to_the_distant_future(); - - // delegate the kick back to the main thread - g_idle_add (kick_idle, this); - } - } - - core::Signal<> m_timeout; - std::recursive_mutex m_mutex; - bool m_yielding = false; - const std::shared_ptr& m_clock; - UHardwareAlarm m_hardware_alarm = nullptr; - std::thread m_thread; -}; - -/*** -**** -***/ - -UhaWakeupTimer::UhaWakeupTimer(const std::shared_ptr& clock): - p(new Impl(clock)) -{ -} - -UhaWakeupTimer::~UhaWakeupTimer() -{ -} - -bool UhaWakeupTimer::is_supported() -{ - auto hw_alarm = u_hardware_alarm_create(); - g_debug ("%s hardware alarm %p", G_STRFUNC, hw_alarm); - return hw_alarm != nullptr; -} - -void UhaWakeupTimer::set_wakeup_time(const DateTime& d) -{ - p->set_wakeup_time(d); -} - -core::Signal<>& UhaWakeupTimer::timeout() -{ - return p->timeout(); -} - -/*** -**** -***/ - -} // namespace datetime -} // namespace indicator -} // namespace unity -- cgit v1.2.3