I have made the following changes intended for : CE:MW:Shared / mce Please review and accept or decline. BOSS has already run some checks on this request. See the "Messages from BOSS" section below.
https://build.pub.meego.com//request/show/8390 Thank You, spiiroin [This message was auto-generated] --- Request # 8390: Messages from BOSS: State: review at 2013-03-08T14:16:45 by bossbot Reviews: accepted by bossbot : Prechecks succeeded. new for CE-maintainers : Please replace this text with a review and approve/reject the review (not the SR). BOSS will take care of the rest Changes: submit: home:spiiroin:branches:CE:MW:Shared / mce -> CE:MW:Shared / mce changes files: -------------- --- mce.changes +++ mce.changes @@ -0,0 +1,5 @@ +* Wed Mar 06 2013 Simo Piiroinen <[email protected]> - 1.12.8 +- Allow disabling of automatic screen blanking from lock screen +- System startup ready detection is based on init-done flag file +- Implement cpu-keepalive plugin for mce + old: ---- mce-1.12.7.tar.bz2 new: ---- mce-1.12.8.tar.bz2 spec files: ----------- --- mce.spec +++ mce.spec @@ -1,6 +1,6 @@ Name: mce Summary: Mode Control Entity for Nokia mobile computers -Version: 1.12.7 +Version: 1.12.8 Release: 1 Group: System/System Control License: LGPLv2 @@ -20,7 +20,7 @@ BuildRequires: pkgconfig(dsme) >= 0.58 BuildRequires: pkgconfig(gconf-2.0) BuildRequires: pkgconfig(glib-2.0) >= 2.18.0 -BuildRequires: pkgconfig(mce) >= 1.12.2 +BuildRequires: pkgconfig(mce) >= 1.12.3 BuildRequires: kernel-headers >= 2.6.32 BuildRequires: libi2c-devel BuildRequires: systemd other changes: -------------- ++++++ mce-1.12.7.tar.bz2 -> mce-1.12.8.tar.bz2 --- .depend +++ .depend @@ -66,6 +66,16 @@ mce-io.h\ mce.h\ +filewatcher.o:\ + filewatcher.c\ + filewatcher.h\ + mce-log.h\ + +filewatcher.pic.o:\ + filewatcher.c\ + filewatcher.h\ + mce-log.h\ + libwakelock.o:\ libwakelock.c\ libwakelock.h\ @@ -352,6 +362,18 @@ mce.h\ modules/camera.h\ +modules/cpu-keepalive.o:\ + modules/cpu-keepalive.c\ + mce-dbus.h\ + mce-log.h\ + libwakelock.h\ + +modules/cpu-keepalive.pic.o:\ + modules/cpu-keepalive.c\ + mce-dbus.h\ + mce-log.h\ + libwakelock.h\ + modules/display.o:\ modules/display.c\ datapipe.h\ @@ -363,6 +385,7 @@ mce-lib.h\ mce-log.h\ mce.h\ + filewatcher.h\ libwakelock.h\ modules/display.h\ tklock.h\ @@ -378,6 +401,7 @@ mce-lib.h\ mce-log.h\ mce.h\ + filewatcher.h\ libwakelock.h\ modules/display.h\ tklock.h\ @@ -608,6 +632,14 @@ mce.h\ powerkey.h\ +test_inotify.o:\ + test_inotify.c\ + filewatcher.h\ + +test_inotify.pic.o:\ + test_inotify.c\ + filewatcher.h\ + tklock.o:\ tklock.c\ datapipe.h\ @@ -647,6 +679,7 @@ tools/mcetool.o:\ tools/mcetool.c\ modules/display.h\ + modules/filter-brightness-als.h\ modules/powersavemode.h\ tklock.h\ tools/mcetool.h\ @@ -654,6 +687,7 @@ tools/mcetool.pic.o:\ tools/mcetool.c\ modules/display.h\ + modules/filter-brightness-als.h\ modules/powersavemode.h\ tklock.h\ tools/mcetool.h\ --- Makefile +++ Makefile @@ -43,7 +43,7 @@ # CONFIGURATION # ---------------------------------------------------------------------------- -VERSION := 1.12.7 +VERSION := 1.12.8 INSTALL_BIN := install --mode=755 INSTALL_DIR := install -d @@ -147,6 +147,7 @@ MODULES += $(MODULE_DIR)/callstate.so MODULES += $(MODULE_DIR)/audiorouting.so MODULES += $(MODULE_DIR)/powersavemode.so +MODULES += $(MODULE_DIR)/cpu-keepalive.so # Tools to build TOOLS += $(TOOLDIR)/mcetool @@ -275,6 +276,7 @@ MCE_CORE += mce-lib.c MCE_CORE += median_filter.c MCE_CORE += evdev.c +MCE_CORE += filewatcher.c # HACK: do not link against libgconf-2 ifeq ($(strip $(ENABLE_BUILTIN_GCONF)),y) --- builtin-gconf.c +++ builtin-gconf.c @@ -1272,6 +1272,12 @@ .def = "1", }, { + // MCE_GCONF_TK_AUTO_BLANK_DISABLE_PATH @ tklock.h + .key = "/system/osso/dsm/locks/tklock_blank_disable", + .type = "i", + .def = "0", + }, + { .key = NULL, } }; --- debian/changelog +++ debian/changelog @@ -1,3 +1,11 @@ +mce (1.12.8) unstable; urgency=low + + * Allow disabling of automatic screen blanking from lock screen + * System startup ready detection is based on init-done flag file + * Implement cpu-keepalive plugin for mce + + -- Simo Piiroinen <[email protected]> Wed, 06 Mar 2013 14:47:35 +0200 + mce (1.12.7) unstable; urgency=low * Allow user processes to make get_config and set_config method calls --- filewatcher.c +++ filewatcher.c @@ -0,0 +1,533 @@ +/* ------------------------------------------------------------------------- * + * Copyright (C) 2013 Jolla Ltd. + * Contact: Simo Piiroinen <[email protected]> + * License: LGPLv2 + * ------------------------------------------------------------------------- */ + +#include "filewatcher.h" +#include "mce-log.h" + +#include <sys/inotify.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#define DEBUG_INOTIFY_EVENTS 0 + +static inline void *lea(const void *base, int offs) +{ + return ((char *)base)+offs; +} + +/* ------------------------------------------------------------------------- * + * Inotify event debugging helpers + * ------------------------------------------------------------------------- */ + +#if DEBUG_INOTIFY_EVENTS +/** Convert inotify event bitmask to human readable string + * + * @param mask event bit mask + * @param buff where to construct the string + * @param size how much space buff has (must be > 0) + * + * @return string with names of set bits separated with '+' + */ +static +const char * +inotify_mask_repr(uint32_t mask, char *buff, size_t size) +{ + static const struct + { + uint32_t mask; + const char *name; + } lut[] = + { +# define X(tag) { .mask = IN_##tag, .name = #tag }, + X(ACCESS) + X(MODIFY) + X(ATTRIB) + X(CLOSE_WRITE) + X(CLOSE_NOWRITE) + X(OPEN) + X(MOVED_FROM) + X(MOVED_TO) + X(CREATE) + X(DELETE) + X(DELETE_SELF) + X(MOVE_SELF) + X(UNMOUNT) + X(Q_OVERFLOW) + X(IGNORED) + X(ONLYDIR) + X(DONT_FOLLOW) + X(EXCL_UNLINK) + X(MASK_ADD) + X(ISDIR) + X(ONESHOT) +# undef X + { .mask = 0, .name = 0 } + }; + + char *pos = buff; + char *end = buff + size - 1; + + auto void adds(const char *s) + { + while( *s && pos < end) *pos++ = *s++; + } + + for( size_t i = 0; lut[i].mask; ++i ) + { + if( mask & lut[i].mask ) + { + mask ^= lut[i].mask; + if( pos > buff ) adds("+"); + adds(lut[i].name); + } + } + if( mask ) + { + char hex[32]; + snprintf(hex, sizeof hex, "0x%"PRIx32, mask); + if( pos > buff ) adds("+"); + adds(hex); + } + + return *pos = 0, buff; +} + +/** Emit inotify event details + * + * @param eve inotify_event pointer + */ +static +void +inotify_event_debug(const struct inotify_event *eve) +{ + char temp[256]; + printf("wd=%d\n", eve->wd); + printf("mask=%s\n", inotify_mask_repr(eve->mask, temp, sizeof temp)); + if( eve->len ) + { + printf("name=\"%s\"\n", eve->name); + } + printf("\n"); +} +#endif /* DEBUG_INOTIFY_EVENTS */ + +/* ------------------------------------------------------------------------- * + * File content change tracking + * ------------------------------------------------------------------------- */ + +/** Object for tracking file content in a directory */ +struct filewatcher_t +{ + /** inotify file descriptor */ + int inotify_fd; + + /** inotify watch descriptor */ + int inotify_wd; + + /** glib input watch for inotify_fd */ + guint watch_id; + + /** the directory to watch over */ + char *watch_path; + + /** the file in the watch_path to track */ + char *watch_file; + + /** function to call when watch_path/watch_file changes */ + filewatcher_changed_fn changed_cb; + + /** user data to pass to changed_cb */ + gpointer user_data; + + /** how to delete user_data when filewatcher_t is deleted */ + GDestroyNotify delete_cb; +}; + +/* Initialize filewatcher_t object to a sane state + * + * @param self pointer to uninitialized filewatcher_t object + */ +static +void +filewatcher_ctor(filewatcher_t *self) +{ + self->inotify_fd = -1; + self->inotify_wd = -1; + self->watch_path = 0; + self->watch_file = 0; + + self->watch_id = 0; + + self->changed_cb = 0; + + self->delete_cb = 0; + self->user_data = 0; +} + +/* Release all dynamic data from filewatcher_t object + * + * @param self pointer to initialized filewatcher_t object + */ +static +void +filewatcher_dtor(filewatcher_t *self) +{ + /* detach user data */ + if( self->delete_cb ) + { + self->delete_cb(self->user_data); + } + self->user_data = 0; + + /* detach glib io watch */ + if( self->watch_id ) + { + g_source_remove(self->watch_id), self->watch_id = 0; + } + + /* detach inotify fd */ + if( self->inotify_fd != -1 ) + { + if( self->inotify_wd != -1 ) + { + if( inotify_rm_watch(self->inotify_fd, self->inotify_wd) == -1 ) + { + mce_log(LL_WARN, "inotify_rm_watch: %m"); + } + self->inotify_wd = -1; + } + if( close(self->inotify_fd) == -1 ) + { + mce_log(LL_WARN, "close inotify fd: %m"); + } + self->inotify_fd = -1; + } + + /* release strings */ + g_free(self->watch_path), self->watch_path = 0; + g_free(self->watch_file), self->watch_file = 0; +} + +/* Delete a filewatcher_t object + * + * @param self pointer to initialized filewatcher_t object, or NULL + */ +void +filewatcher_delete(filewatcher_t *self) +{ + if( self != 0 ) + { + filewatcher_dtor(self); + g_free(self); + } +} + +/** Process inotify events + * + * @param self pointer to filewatcher_t object + * + * @return TRUE on success, or FALSE if further processing is not possible + */ +static +gboolean +filewatcher_process_events(filewatcher_t *self) +{ + gboolean res = FALSE; + gboolean flg = FALSE; + + char buf[2048]; + int todo, size; + struct inotify_event *eve; + + if( !self || self->inotify_fd == -1 ) + { + goto cleanup; + } + + todo = read(self->inotify_fd, buf, sizeof buf); + + if( todo < 0 ) + { + switch( errno ) + { + case EAGAIN: + case EINTR: + res = TRUE; + break; + + default: + mce_log(LL_WARN, "read inotify events: %m"); + break; + } + goto cleanup; + } + + if( todo == 0 ) + { + mce_log(LL_WARN, "read inotify events: EOF"); + goto cleanup; + } + +#if DEBUG_INOTIFY_EVENTS + printf("----\n"); +#endif + + for( eve = lea(buf, 0); todo; todo -= size, eve = lea(eve, size)) + { + if( todo < (int)sizeof *eve ) + { + mce_log(LL_WARN, "partial inotify event received"); + goto cleanup; + } + + size = sizeof *eve + eve->len; + + if( todo < size ) + { + mce_log(LL_WARN, "oversized inotify event received"); + goto cleanup; + } + +#if DEBUG_INOTIFY_EVENTS + inotify_event_debug(eve); +#endif + + if( eve->len && !strcmp(self->watch_file, eve->name) ) + { + flg = TRUE; + } + + if( eve->mask & IN_IGNORED ) + { + mce_log(LL_ERR, "inotify watch went defunct"); + flg = TRUE; + goto cleanup; + } + } + + res = TRUE; + +cleanup: + + if( flg && self && self->changed_cb ) + { + self->changed_cb(self->watch_path, self->watch_file, self->user_data); + } + + return res; +} + +/** Glib io glue for processing inotify event input + * + * @param source (not used) + * @param condition (not used) + * @param data pointer to filewatcher_t object (as void pointer) + * + * @return TRUE to keep the io watch alive, or + * FALSE if the io watch must be released + */ +static +gboolean +filewatcher_input_cb(GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + (void)source; (void)condition; + + filewatcher_t *self = data; + gboolean keep_going = filewatcher_process_events(self); + + if( !keep_going ) + { + mce_log(LL_WARN, "stopping inotify event io watch"); + self->watch_id = 0; + } + + return keep_going; +} + +/** Helper for setting up inotify file descriptor + * + * @note This function is meant to be called form + * filewatcher_create() function only! + * + * @param self pointer to filewatcher_t object + * + * @return TRUE on success, or FALSE on failure + */ +static +gboolean +filewatcher_setup_inotify(filewatcher_t *self) +{ + gboolean success = FALSE; + + uint32_t mask = (0 + | IN_CREATE + | IN_DELETE + | IN_CLOSE_WRITE + | IN_MOVED_TO + | IN_MOVED_FROM + | IN_DONT_FOLLOW + | IN_ONLYDIR); + + self->inotify_fd = inotify_init1(IN_CLOEXEC); + if( self->inotify_fd == -1 ) + { + mce_log(LL_WARN, "inotify_init: %m"); + goto cleanup; + } + + self->inotify_wd = inotify_add_watch(self->inotify_fd, + self->watch_path, mask); + if( self->inotify_wd == -1 ) + { + mce_log(LL_WARN, "%s: inotify_add_watch: %m", self->watch_path); + goto cleanup; + } + + success = TRUE; + +cleanup: + return success; +} + +/** Helper for setting up glib io watch for inotify file descriptor + * + * @note This function is meant to be called form + * filewatcher_create() function only! + * + * @param self pointer to filewatcher_t object + * + * @return TRUE on success, or FALSE on failure + */ +static +gboolean +filewatcher_setup_iowatch(filewatcher_t *self) +{ + gboolean success = FALSE; + + GIOChannel *chan = 0; + GError *err = 0; + + if( !(chan = g_io_channel_unix_new(self->inotify_fd)) ) + { + mce_log(LL_WARN, "%s: %m", "g_io_channel_unix_new"); + goto cleanup; + } + + /* the channel does not own the fd */ + g_io_channel_set_close_on_unref(chan, FALSE); + + /* Set to NULL encoding so that we can turn off the buffering */ + if( g_io_channel_set_encoding(chan, NULL, &err) != G_IO_STATUS_NORMAL ) + { + mce_log(LL_WARN, "%s: %s", "g_io_channel_set_encoding", + (err && err->message) ? err->message : "unknown"); + } + g_io_channel_set_buffered(chan, FALSE); + + self->watch_id = g_io_add_watch(chan, G_IO_IN, filewatcher_input_cb, self); + + if( !self->watch_id ) + { + mce_log(LL_WARN, "%s: %m", "g_io_add_watch"); + goto cleanup; + } + + success = TRUE; + +cleanup: + + g_clear_error(&err); + + if( chan ) g_io_channel_unref(chan); + + return success; +} + +/** Create an filewatcher_t object + * + * An inotify watcher is started for the given director/file. + * A glib io watch is used to process the inotify events. + * The change_cb is called when contents of the tracked file + * are assumed to have changed. + * + * @note The change_cb function will not be called during the + * initialization. You can make initial state evaluation + * to happen by calling filewatcher_force_trigger() after + * succesfull filewatcher_create(). + * + * @param dirpath directory to watch over + * @param filename file to watch in dirpath + * @param change_cb function to call when dirpath/filename changes + * @param user_data extra parameter to pass to change_cb + * @param delete_cb called on user_data when filewatcher_t itself is deleted + * + * @return pointer to filewatcher_t object, or NULL in case of errors + */ +filewatcher_t * +filewatcher_create(const char *dirpath, + const char *filename, + filewatcher_changed_fn change_cb, + gpointer user_data, + GDestroyNotify delete_cb) +{ + gboolean success = FALSE; + + filewatcher_t *self = g_malloc0(sizeof *self); + filewatcher_ctor(self); + + self->watch_path = g_strdup(dirpath); + self->watch_file = g_strdup(filename); + + self->changed_cb = change_cb; + + self->user_data = user_data; + self->delete_cb = delete_cb; + + if( !filewatcher_setup_inotify(self) ) + { + goto cleanup; + } + if( !filewatcher_setup_iowatch(self) ) + { + goto cleanup; + } + + success = TRUE; + +cleanup: + + if( !success ) + { + filewatcher_delete(self), self = 0; + } + + return self; +} + +/** Force calling the change notification callback + * + * This can be useful for example to feed initial + * state of tracked file via the same mechanism as + * the later changes get reported + * + * @param self pointer to filewatcher_t object + */ +void +filewatcher_force_trigger(filewatcher_t *self) +{ + if( self->changed_cb ) + { + self->changed_cb(self->watch_path, self->watch_file, self->user_data); + } +} --- filewatcher.h +++ filewatcher.h @@ -0,0 +1,38 @@ +/* ------------------------------------------------------------------------- * + * Copyright (C) 2013 Jolla Ltd. + * Contact: Simo Piiroinen <[email protected]> + * License: LGPLv2 + * ------------------------------------------------------------------------- */ + +#ifndef FILEWATCHER_H_ +# define FILEWATCHER_H_ + +# include <glib.h> + +# ifdef __cplusplus +extern "C" { +# elif 0 +} /* fool JED indentation ... */ +# endif + +typedef void (*filewatcher_changed_fn)(const char *path, + const char *file, + gpointer user_data); + +typedef struct filewatcher_t filewatcher_t; + +filewatcher_t *filewatcher_create(const char *dirpath, + const char *filename, + filewatcher_changed_fn change_cb, + gpointer user_data, + GDestroyNotify delete_cb); + +void filewatcher_delete(filewatcher_t *self); + +void filewatcher_force_trigger(filewatcher_t *self); + +# ifdef __cplusplus +}; +# endif + +#endif /* FILEWATCHER_H_ */ --- mce-dbus.c +++ mce-dbus.c @@ -52,6 +52,26 @@ /** Pointer to the DBusConnection */ static DBusConnection *dbus_connection = NULL; + + +/** Return reference to dbus connection cached at mce-dbus module + * + * For use in situations where the abstraction provided by mce-dbus + * makes things too complicated. + * + * Caller must release non-null return values with dbus_connection_unref(). + * + * @return DBusConnection, or NULL if mce has no dbus connection + */ +DBusConnection *dbus_connection_get(void) +{ + if( !dbus_connection ) { + mce_log(LL_WARN, "no dbus connection"); + return NULL; + } + return dbus_connection_ref(dbus_connection); +} + /** * Create a new D-Bus signal, with proper error checking * will exit the mainloop if an error occurs --- mce-dbus.h +++ mce-dbus.h @@ -26,6 +26,8 @@ #include <mce/dbus-names.h> +DBusConnection *dbus_connection_get(void); + DBusMessage *dbus_new_signal(const gchar *const path, const gchar *const interface, const gchar *const name); --- mce.conf +++ mce.conf @@ -104,5 +104,15 @@ <allow send_destination="com.nokia.mce" send_interface="com.nokia.mce.request" send_member="req_led_disable"/> + + <allow send_destination="com.nokia.mce" + send_interface="com.nokia.mce.request" + send_member="req_cpu_keepalive_period"/> + <allow send_destination="com.nokia.mce" + send_interface="com.nokia.mce.request" + send_member="req_cpu_keepalive_start"/> + <allow send_destination="com.nokia.mce" + send_interface="com.nokia.mce.request" + send_member="req_cpu_keepalive_stop"/> </policy> </busconfig> --- mce.ini +++ mce.ini @@ -16,7 +16,7 @@ # to avoid unnecessary brightness fluctuations on mce startup # # Note: the name should not include the "lib"-prefix -Modules=radiostates;filter-brightness-als;display;keypad;led;battery;inactivity;alarm;callstate;audiorouting;proximity;powersavemode +Modules=radiostates;filter-brightness-als;display;keypad;led;battery;inactivity;alarm;callstate;audiorouting;proximity;powersavemode;cpu-keepalive [HomeKey] --- modules/cpu-keepalive.c +++ modules/cpu-keepalive.c @@ -0,0 +1,1002 @@ +/* ------------------------------------------------------------------------- * + * Copyright (C) 2013 Jolla Ltd. + * Contact: Simo Piiroinen <[email protected]> + * License: LGPLv2 + * ------------------------------------------------------------------------- */ + +/** + * @file cpu-keepalive.c + * cpu-keepalive module -- this implements late suspend blocking for MCE + * <p> + * Copyright © 2013 Jolla Ltd. + * <p> + * @author Simo Piiroinen <[email protected]> + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mce. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <gmodule.h> +#include <time.h> +#include <string.h> + +#include "mce-log.h" +#include "mce-dbus.h" + +#ifdef ENABLE_WAKELOCKS +# include "../libwakelock.h" +#endif + +typedef struct client_t client_t; + +G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module); +G_MODULE_EXPORT void g_module_unload (GModule *module); + +/** The name of this module */ +static const char module_name[] = "cpu-keepalive"; + +#ifdef ENABLE_WAKELOCKS +/** RTC wakeup wakelock - acquired by dsme and released by mce */ +static const char rtc_wakelock[] = "mce_rtc_wakeup"; + +/* CPU keepalive wakelock - held by mce while there are active clients */ +static const char cpu_wakelock[] = "mce_cpu_keepalive"; +#endif /* ENABLE_WAKELOCKS */ + +static DBusConnection *systembus = 0; + +/** Clients we are tracking over D-Bus */ +static GHashTable *clients = 0; + +/** Timestamp of wakeup from dsme */ +static time_t wakeup_started = 0; + +/** Timeout for "clients should have issued keep alive requests" */ +static time_t wakeup_timeout = 0; + +/** Timer for releasing cpu-keepalive wakelock */ +static guint timer_id = 0; + +/** Maximum delay between MCE_CPU_KEEPALIVE_START_REQ method calls */ +#ifdef ENABLE_WAKELOCKS +# define MCE_CPU_KEEPALIVE_PERIOD_SECONDS 60 // 1 minute +#else +# define MCE_CPU_KEEPALIVE_PERIOD_SECONDS (60*60*24) // 1 day +#endif + +/** Maximum delay between rtc wakeup and the 1st keep alive request */ +#define MCE_RTC_WAKEUP_1ST_TIMEOUT_SECONDS 2 + +/** Extend rtc wakeup timeout if at least one keep alive request is received */ +#define MCE_RTC_WAKEUP_2ND_TIMEOUT_SECONDS 4 + +/* ========================================================================= * + * + * GENERIC UTILITIES + * + * ========================================================================= */ + +/** Get monotonic timestamp not affected by system time / timezone changes + * + * @return seconds since some reference point in time + */ +static +time_t +cpu_keepalive_get_time(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec; +} + +/* ========================================================================= * + * + * D-BUS UTILITIES + * + * ========================================================================= */ + +/** Helper for sending boolean replies to dbus method calls + * + * Reply will not be sent if no_reply attribute is set + * in the method call message. + * + * @param msg method call message to reply + * @param value TRUE/FALSE to send + * + * @return TRUE on success, or FALSE if reply could not be sent + */ +static +gboolean +cpu_keepalive_reply_bool(DBusMessage *const msg, gboolean value) +{ + gboolean success = TRUE; + + if( !dbus_message_get_no_reply(msg) ) + { + dbus_bool_t data = value; + DBusMessage *reply = dbus_new_method_reply(msg); + dbus_message_append_args(reply, + DBUS_TYPE_BOOLEAN, &data, + DBUS_TYPE_INVALID); + + /* dbus_send_message() unrefs the message */ + success = dbus_send_message(reply), reply = 0; + + if( !success ) + { + mce_log(LL_WARN, "failed to send reply to %s", + dbus_message_get_member(msg)); + } + } + + return success; +} + +/** Helper for sending boolean replies to dbus method calls + * + * Reply will not be sent if no_reply attribute is set + * in the method call message. + * + * @param msg method call message to reply + * @param value integer number to send + * + * @return TRUE on success, or FALSE if reply could not be sent + */ +static +gboolean +cpu_keepalive_reply_int(DBusMessage *const msg, gint value) +{ + gboolean success = TRUE; + + if( !dbus_message_get_no_reply(msg) ) + { + dbus_int32_t data = value; + DBusMessage *reply = dbus_new_method_reply(msg); + dbus_message_append_args(reply, + DBUS_TYPE_INT32, &data, + DBUS_TYPE_INVALID); + + /* dbus_send_message() unrefs the message */ + success = dbus_send_message(reply), reply = 0; + + if( !success ) + { + mce_log(LL_WARN, "failed to send reply to %s", + dbus_message_get_member(msg)); + } + } + + return success; +} + +/** Create a GetNameOwner method call message + * + * @param name the dbus name to query + * + * @return DBusMessage pointer + */ +static +DBusMessage * +cpu_keepalive_create_GetNameOwner_req(const char *name) +{ + DBusMessage *req = 0; + + req = dbus_message_new_method_call(DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetNameOwner"); + dbus_message_append_args(req, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID); + + return req; +} + +/** Parse a reply message to GetNameOwner method call + * + * @param rsp method call reply message + * + * @return dbus name of the name owner, or NULL in case of errors + */ +static +gchar * +cpu_keepalive_parse_GetNameOwner_rsp(DBusMessage *rsp) +{ + char *res = 0; + DBusError err = DBUS_ERROR_INIT; + char *dta = NULL; + + if( dbus_set_error_from_message(&err, rsp) || + !dbus_message_get_args(rsp, &err, + DBUS_TYPE_STRING, &dta, + DBUS_TYPE_INVALID) ) + { + if( strcmp(err.name, DBUS_ERROR_NAME_HAS_NO_OWNER) ) { + mce_log(LL_WARN, "%s: %s", err.name, err.message); + } + goto EXIT; + } + + res = g_strdup(dta); + +EXIT: + dbus_error_free(&err); + return res; +} + +/* ========================================================================= * + * + * INFORMATION ABOUT ACTIVE CLIENTS + * + * ========================================================================= */ + +/** Format string for constructing name owner lost match rules */ +static const char client_match_fmt[] = +"type='signal'" +",sender='"DBUS_SERVICE_DBUS"'" +",interface='"DBUS_INTERFACE_DBUS"'" +",member='NameOwnerChanged'" +",path='"DBUS_PATH_DBUS"'" +",arg0='%s'" +",arg2=''" +; + +/** Book keeping information for clients we are tracking */ +struct client_t +{ + /** The (private/sender) name of the dbus client */ + gchar *dbus_name; + + /** NameOwnerChanged signal match used for tracking death of client */ + char *match_rule; + + /** Upper bound for reneval of cpu keepalive for this client */ + time_t timeout; +}; + +/** Clear client cpu-keepalive timeout + * + * @param self pointer to client_t structure + */ +static +void +client_clear_timeout(client_t *self) +{ + self->timeout = 0; +} + + +/** Update client cpu-keepalive timeout + * + * @param self pointer to client_t structure + * @param when end of client cpu-keepalive period + */ +static +void +client_update_timeout(client_t *self, time_t when) +{ + if( self->timeout < when ) + { + self->timeout = when; + } +} + + +/** Create bookkeeping information for a dbus client + * + * Note: Will also add signal matching rule so that we get notified + * when the client loses dbus connection + * + * @param dbus_name name of the dbus client to track + * + * @return pointer to client_t structure + */ +static +client_t * +client_create(const char *dbus_name) +{ + client_t *self = g_malloc0(sizeof *self); + + self->dbus_name = g_strdup(dbus_name); + self->match_rule = g_strdup_printf(client_match_fmt, self->dbus_name); + self->timeout = 0; + + mce_log(LL_NOTICE, "added cpu-keepalive client %s", self->dbus_name); + + /* NULL error -> match will be added asynchronously */ + dbus_bus_add_match(systembus, self->match_rule, 0); + + return self; +} + +/** Destroy bookkeeping information about a dbus client + * + * Note: Will also remove the signal matching rule used for detecting + * when the client loses dbus connection + * + * @param self pointer to client_t structure + */ + +static +void +client_delete(client_t *self) +{ + if( self != 0 ) + { + mce_log(LL_NOTICE, "removed cpu-keepalive client %s", self->dbus_name); + + /* NULL error -> match will be removed asynchronously */ + dbus_bus_remove_match(systembus, self->match_rule, 0); + + g_free(self->dbus_name); + g_free(self->match_rule); + g_free(self); + } +} + +/** Typeless helper function for use as destroy callback + * + * @param self pointer to client_t structure + */ +static +void +client_delete_cb(void *self) +{ + client_delete(self); +} + +/* ========================================================================= * + * + * CPU-KEEPALIVE TIMER MANAGEMENT + * + * ========================================================================= */ + +/** Handle triggering of cpu-keepalive timer + * + * Releases cpu keepalive wakelock and thus allows the system to + * enter late suspend according to other policies. + * + * @param data (not used) + * + * @return FALSE (to stop then timer from repeating) + */ +static +gboolean +cpu_keepalive_timer_cb(gpointer data) +{ + (void)data; + + if( timer_id != 0 ) + { + mce_log(LL_NOTICE, "cpu-keepalive ended"); + timer_id = 0; + +#ifdef ENABLE_WAKELOCKS + wakelock_unlock(cpu_wakelock); +#endif + } + + return FALSE; +} + +/** Cancel end of cpu-keepalive timer + */ +static +void +cpu_keepalive_cancel_timer(void) +{ + if( timer_id != 0 ) + { + g_source_remove(timer_id), timer_id = 0; + } +} + +/** Reset cpu-keepalive timer + * + * @param when monotonic time of the end of cpu-keepalive period + */ +static +void +cpu_keepalive_set_timer(time_t when) +{ + cpu_keepalive_cancel_timer(); + + time_t now = cpu_keepalive_get_time(); + + if( when < now ) when = now; + + mce_log(LL_NOTICE, "cpu-keepalive ends at T%+d", (int)(now - when)); + + if( now < when ) + { + timer_id = g_timeout_add_seconds(when - now, + cpu_keepalive_timer_cb, 0); + } + else + { + timer_id = g_idle_add(cpu_keepalive_timer_cb, 0); + } +} + +/** Re-evaluate the end of cpu-keepalive period + * + * Calculates maximum of wakeup period and per client renew periods + * and uses it to reprogram the end of cpu-keepalive period + */ +static +void +cpu_keepalive_rethink(void) +{ + time_t maxtime = wakeup_timeout; + + GHashTableIter iter; + gpointer key, val; + +#ifdef ENABLE_WAKELOCKS + wakelock_lock(cpu_wakelock, -1); +#endif + + g_hash_table_iter_init(&iter, clients); + while( g_hash_table_iter_next (&iter, &key, &val) ) + { + client_t *client = val; + if( maxtime < client->timeout ) + { + maxtime = client->timeout; + } + } + cpu_keepalive_set_timer(maxtime); +} + +/* ========================================================================= * + * + * CLIENT MANAGEMENT + * + * ========================================================================= */ + +/** Remove bookkeeping data for a client and re-evaluate cpu keepalive status + * + * @param dbus_name dbus name of the client + */ +static +void +cpu_keepalive_remove_client(const gchar *dbus_name) +{ + if( g_hash_table_remove(clients, dbus_name) ) + { + cpu_keepalive_rethink(); + } +} + +/** Obtain bookkeeping data for a client + * + * @param dbus_name dbus name of the client + * + * @return client data, or NULL if dbus_name is not tracked + */ +static +client_t * +cpu_keepalive_get_client(const gchar *dbus_name) +{ + client_t *client = g_hash_table_lookup(clients, dbus_name); + return client; +} + +/** Call back for handling asynchronous client verification via GetNameOwner + * + * @param pending control structure for asynchronous d-bus methdod call + * @param user_data dbus_name of the client as void poiter + */ +static +void +cpu_keepalive_verify_name_cb(DBusPendingCall *pending, void *user_data) +{ + const gchar *name = user_data; + gchar *owner = 0; + DBusMessage *rsp = 0; + client_t *client = 0; + + if( !(rsp = dbus_pending_call_steal_reply(pending)) ) + { + goto EXIT; + } + + if( !(client = cpu_keepalive_get_client(name)) ) + { + mce_log(LL_WARN, "untracked client %s", name); + } + + if( !(owner = cpu_keepalive_parse_GetNameOwner_rsp(rsp)) || !*owner ) + { + mce_log(LL_WARN, "dead client %s", name); + cpu_keepalive_remove_client(name), client = 0; + } + else + { + mce_log(LL_WARN, "live client %s, owner %s", name, owner); + } + +EXIT: + g_free(owner); + + if( rsp ) dbus_message_unref(rsp); +} + +/** Verify that a client exists via an asynchronous GetNameOwner method call + * + * @param name the dbus name who's owner we wish to know + * + * @return TRUE if the method call was initiated, or FALSE in case of errors + */ +static +gboolean +cpu_keepalive_verify_name(const char *name) +{ + gboolean res = FALSE; + DBusMessage *req = 0; + DBusPendingCall *pc = 0; + gchar *key = 0; + + if( !(req = cpu_keepalive_create_GetNameOwner_req(name)) ) + { + goto EXIT; + } + + if( !dbus_connection_send_with_reply(systembus, req, &pc, -1) ) + { + goto EXIT; + } + + key = g_strdup(name); + + if( !dbus_pending_call_set_notify(pc, cpu_keepalive_verify_name_cb, + key, g_free) ) + { + goto EXIT; + } + + // pending call should not be cancelled + pc = 0; + + // key string is owned by pending call + key = 0; + + // success + res = TRUE; + +EXIT: + + g_free(key); + + if( pc ) dbus_pending_call_cancel(pc); + if( req ) dbus_message_unref(req); + + return res; +} + +/** Find existing / create new client data by dbus name + * + * @param dbus_name dbus name of the client + * + * @return client data + */ +static +client_t * +cpu_keepalive_add_client(const gchar *dbus_name) +{ + client_t *client = g_hash_table_lookup(clients, dbus_name); + + if( !client ) + { + /* The client_create() adds NameOwnerChanged signal match + * so that we know when/if the client exits, crashes or + * otherwise loses dbus connection. */ + + client = client_create(dbus_name); + g_hash_table_insert(clients, g_strdup(dbus_name), client); + + /* Then make an explicit GetNameOwner request to verify that + * the client is still running when we have the signal + * listening in the place. */ + cpu_keepalive_verify_name(dbus_name); + } + + return client; +} + +/** Register client and start minor keep-alive period + * + * If needed will add the dbus_name to list of tracked clients. + * + * @param dbus_name name of the tracked client + */ +static +void +cpu_keepalive_register(const gchar *dbus_name) +{ + client_t *client = cpu_keepalive_add_client(dbus_name); + + time_t when = cpu_keepalive_get_time() + 2; + + client_update_timeout(client, when); + + cpu_keepalive_rethink(); +} + +/** Adjust the cpu-keepalive timeout for dbus client + * + * If needed will add the dbus_name to list of tracked clients. + * + * @param dbus_name name of the tracked client + */ +static +void +cpu_keepalive_start(const gchar *dbus_name) +{ + client_t *client = cpu_keepalive_add_client(dbus_name); + + time_t when = cpu_keepalive_get_time() + MCE_CPU_KEEPALIVE_PERIOD_SECONDS; + + client_update_timeout(client, when); + + /* We got at least one keep alive request, extend the minimum + * alive time a bit to give other clients time to get scheduled */ + wakeup_timeout = wakeup_started + MCE_RTC_WAKEUP_2ND_TIMEOUT_SECONDS; + + cpu_keepalive_rethink(); +} + +/** Remove the cpu-keepalive timeout for dbus client + * + * @param dbus_name name of the tracked client + */ +static +void +cpu_keepalive_stop(const gchar *dbus_name) +{ + client_t *client = cpu_keepalive_get_client(dbus_name); + + if( client ) + { + client_clear_timeout(client); + cpu_keepalive_rethink(); + } + else + { + mce_log(LL_WARN, "untracked client %s", dbus_name); + } +} + +/** Transfer resume-due-to-rtc-input wakelock from dsme to mce + * + * @param dbus_name name of the client issuing the request + */ +static +void +cpu_keepalive_wakeup(const gchar *dbus_name) +{ + // FIXME: we should check that the dbus_name == DSME + (void)dbus_name; + + /* Time of wakeup received */ + wakeup_started = cpu_keepalive_get_time(); + + /* Timeout for the 1st keepalive message to come through */ + wakeup_timeout = wakeup_started + MCE_RTC_WAKEUP_1ST_TIMEOUT_SECONDS; + + cpu_keepalive_rethink(); + + mce_log(LL_NOTICE, "rtc wakeup finished"); +#ifdef ENABLE_WAKELOCKS + wakelock_unlock(rtc_wakelock); +#endif +} + +/* ========================================================================= * + * + * D-BUS METHOD CALL HANDLERS + * + * ========================================================================= */ + +/** D-Bus callback for the MCE_CPU_KEEPALIVE_PERIOD_REQ method call + * + * @param msg The D-Bus message + * + * @return TRUE on success, FALSE on failure + */ +static +gboolean +cpu_keepalive_period_cb(DBusMessage *const msg) +{ + mce_log(LL_INFO, "got method call"); + + gboolean success = FALSE; + + cpu_keepalive_register(dbus_message_get_sender(msg)); + + success = cpu_keepalive_reply_int(msg, MCE_CPU_KEEPALIVE_PERIOD_SECONDS); + + return success; +} + +/** D-Bus callback for the MCE_CPU_KEEPALIVE_START_REQ method call + * + * @param msg The D-Bus message + * + * @return TRUE on success, FALSE on failure + */ +static +gboolean +cpu_keepalive_start_cb(DBusMessage *const msg) +{ + mce_log(LL_INFO, "got method call"); + + gboolean success = FALSE; + + cpu_keepalive_start(dbus_message_get_sender(msg)); + + success = cpu_keepalive_reply_bool(msg, TRUE); + + return success; +} + +/** D-Bus callback for the MCE_CPU_KEEPALIVE_STOP_REQ method call + * + * @param msg The D-Bus message + * + * @return TRUE on success, FALSE on failure + */ +static +gboolean +cpu_keepalive_stop_cb(DBusMessage *const msg) +{ + mce_log(LL_INFO, "got method call"); + + gboolean success = FALSE; + + cpu_keepalive_stop(dbus_message_get_sender(msg)); + + success = cpu_keepalive_reply_bool(msg, TRUE); + + return success; +} + +/** D-Bus callback for the MCE_CPU_KEEPALIVE_WAKEUP_REQ method call + * + * @param msg The D-Bus message + * + * @return TRUE on success, FALSE on failure + */ +static +gboolean +cpu_keepalive_wakeup_cb(DBusMessage *const msg) +{ + mce_log(LL_INFO, "got method call"); + + gboolean success = FALSE; + + cpu_keepalive_wakeup(dbus_message_get_sender(msg)); + + success = cpu_keepalive_reply_bool(msg, TRUE); + + return success; +} + +/* ========================================================================= * + * + * D-BUS SIGNAL HANDLERS + * + * ========================================================================= */ + +/** D-Bus message filter for handling NameOwnerChanged signals + * + * @param con dbus connection + * @param msg message to be acted upon + * @param user_data (not used) + * + * @return DBUS_HANDLER_RESULT_NOT_YET_HANDLED (other filters see the msg too) + */ +static +DBusHandlerResult +cpu_keepalive_dbus_filter_cb(DBusConnection *con, DBusMessage *msg, + void *user_data) +{ + (void)user_data; + + DBusHandlerResult res = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + const char *sender = 0; + const char *object = 0; + + const char *name = 0; + const char *prev = 0; + const char *curr = 0; + + DBusError err = DBUS_ERROR_INIT; + + if( con != systembus ) + { + goto EXIT; + } + + if( !dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS, "NameOwnerChanged") ) + { + goto EXIT; + } + + sender = dbus_message_get_sender(msg); + if( strcmp(sender, DBUS_SERVICE_DBUS) ) + { + goto EXIT; + } + + object = dbus_message_get_path(msg); + if( strcmp(object, DBUS_PATH_DBUS) ) + { + goto EXIT; + } + + if( !dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &prev, + DBUS_TYPE_STRING, &curr, + DBUS_TYPE_INVALID) ) + { + mce_log(LL_WARN, "%s: %s", err.name, err.message); + goto EXIT; + } + + if( !*curr ) + { + mce_log(LL_INFO, "name lost owner: %s", name); + cpu_keepalive_remove_client(name); + } + +EXIT: + dbus_error_free(&err); + return res; +} + +/* ========================================================================= * + * + * MODULE INIT/QUIT + * + * ========================================================================= */ + +typedef gboolean (*handler_t)(DBusMessage *const msg); + +/** Book keeping for dbus method call handler status */ +static struct +{ + const char *member; + const handler_t handler; + gconstpointer cookie; +} methods[] = +{ + { MCE_CPU_KEEPALIVE_PERIOD_REQ, cpu_keepalive_period_cb, 0 }, + { MCE_CPU_KEEPALIVE_START_REQ, cpu_keepalive_start_cb, 0 }, + { MCE_CPU_KEEPALIVE_STOP_REQ, cpu_keepalive_stop_cb, 0 }, + { MCE_CPU_KEEPALIVE_WAKEUP_REQ, cpu_keepalive_wakeup_cb, 0 }, + { 0, 0, 0 } +}; + +/** Install signal and method call message handlers + * + * @return TRUE on success, or FALSE on failure + */ +static gboolean cpu_keepalive_attach_to_dbus(void) +{ + gboolean success = TRUE; + + /* Register signal handling filter */ + dbus_connection_add_filter(systembus, cpu_keepalive_dbus_filter_cb, 0, 0); + + /* Register dbus method call handlers */ + for( size_t i = 0; methods[i].member; ++i ) + { + mce_log(LL_INFO, "registering handler for: %s", methods[i].member); + + methods[i].cookie = mce_dbus_handler_add(MCE_REQUEST_IF, + methods[i].member, + NULL, + DBUS_MESSAGE_TYPE_METHOD_CALL, + methods[i].handler); + if( !methods[i].cookie ) + { + mce_log(LL_WARN, "failed to add dbus handler for: %s", + methods[i].member); + success = FALSE; + } + } + + return success; +} + +/** Remove signal and method call message handlers + */ +static void cpu_keepalive_detach_from_dbus(void) +{ + /* Remove signal handling filter */ + dbus_connection_remove_filter(systembus, cpu_keepalive_dbus_filter_cb, 0); + + /* Remove dbus method call handlers that we have registered */ + for( size_t i = 0; methods[i].member; ++i ) + { + if( methods[i].cookie ) + { + mce_log(LL_INFO, "removing handler for: %s", methods[i].member); + mce_dbus_handler_remove(methods[i].cookie); + methods[i].cookie = 0; + } + } +} + +/** Init function for the cpu-keepalive module + * + * @param module (not used) + * + * @return NULL on success, a string with an error message on failure + */ +const gchar *g_module_check_init(GModule *module) +{ + (void)module; + + const gchar *status = NULL; + + if( !(systembus = dbus_connection_get()) ) + { + status = "mce has no dbus connection"; + goto EXIT; + } + + if( !cpu_keepalive_attach_to_dbus() ) + { + status = "attaching to dbus connection failed"; + goto EXIT; + } + + clients = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, client_delete_cb); + +EXIT: + + mce_log(LL_NOTICE, "loaded %s, status: %s", module_name, status ?: "ok"); + + return status; +} + +/** Exit function for the cpu-keepalive module + * + * @param module (not used) + */ +void g_module_unload(GModule *module) +{ + (void)module; + + /* If we have active clients, removal expects a valid dbus + * connection -> purge clients first */ + if( clients ) + { + g_hash_table_unref(clients), clients = 0; + } + + if( systembus ) + { + cpu_keepalive_detach_from_dbus(); + dbus_connection_unref(systembus), systembus = 0; + } + + mce_log(LL_NOTICE, "unloaded %s", module_name); + + return; +} --- modules/display.c +++ modules/display.c @@ -120,6 +120,7 @@ #ifdef ENABLE_WAKELOCKS # include "../libwakelock.h" /* API for wakelocks */ +# include "../filewatcher.h" #endif /* These defines are taken from devicelock.h, but slightly modified */ @@ -2802,6 +2803,12 @@ /** Still waiting for desktop ready ?*/ static guint suspend_timer_id = 0; +/** Content change watcher for the init-done flag file */ +static filewatcher_t *init_done_watcher = 0; + +/** Is the init-done flag file present in the file system */ +static gboolean init_done = FALSE; + /** Automatic suspend policy modes */ enum { @@ -2860,9 +2867,9 @@ break; } - /* do not suspend mid bootup */ - if( suspend_timer_id ) { - suspend_want = 0; + /* no late suspend during bootup */ + if( suspend_timer_id || !init_done ) { + wakelock_want = 1; } /* no late suspend during shutdown */ @@ -2969,12 +2976,40 @@ return FALSE; } +/** Content of init-done flag file has changed + * + * @param path directory where flag file is + * @param file name of the flag file + * @param data (not used) + */ +static void init_done_changed_cb(const char *path, + const char *file, + gpointer data) +{ + (void)data; + + char full[256]; + snprintf(full, sizeof full, "%s/%s", path, file); + + gboolean flag = access(full, F_OK) ? FALSE : TRUE; + + if( init_done != flag ) { + init_done = flag; + mce_log(LL_NOTICE, "init_done -> %s", + init_done ? "true" : "false"); + suspend_rethink(); + } +} + + /** Cleanup suspend policy */ static void suspend_quit(void) { suspend_unload = TRUE; + filewatcher_delete(init_done_watcher), init_done_watcher = 0; + if( suspend_timer_id ) { g_source_remove(suspend_timer_id); suspend_timer_id = 0; @@ -2987,29 +3022,37 @@ */ static void suspend_init(void) { - /* FIXME: While we do not have a proper desktop-is-ready - * signal/state we are guestimating that we can - * allow suspend after uptime is large enough */ - time_t uptime = 0; // uptime in seconds time_t ready = 60; // desktop ready at time_t delay = 10; // default wait time - struct timespec ts; + /* wait for flag file to appear */ + init_done_watcher = filewatcher_create("/run/systemd/boot-status", + "init-done", + init_done_changed_cb, 0, 0); + + /* or fall back to waiting for uptime to reach some minimum value */ + if( !init_done_watcher ) { + struct timespec ts; + + /* Assume that monotonic clock == uptime */ + if( clock_gettime(CLOCK_MONOTONIC, &ts) == 0 ) + uptime = ts.tv_sec; - /* Assume that monotonic clock == uptime */ - if( clock_gettime(CLOCK_MONOTONIC, &ts) == 0 ) - uptime = ts.tv_sec; + if( uptime + delay < ready ) + delay = ready - uptime; - if( uptime + delay < ready ) - delay = ready - uptime; + /* do not wait for the init-done flag file */ + init_done = TRUE; + } mce_log(LL_NOTICE, "suspend delay %d seconds", (int)delay); - - if( suspend_timer_id ) - g_source_remove(suspend_timer_id); - suspend_timer_id = g_timeout_add_seconds(delay, suspend_timer_cb, 0); + + if( init_done_watcher ) { + /* evaluate the initial state of init-done flag file */ + filewatcher_force_trigger(init_done_watcher); + } } #endif /* ENABLE_WAKELOCKS */ --- tklock.c +++ tklock.c @@ -171,6 +171,15 @@ .close_on_exit = TRUE, }; +/** Disable automatic dim/blank from tklock */ +static gint tklock_blank_disable = FALSE; + +/** GConf notifier id for tracking tklock_blank_disable changes */ +static guint tklock_blank_disable_id = 0; + +/** Pseudo-forever dim/blank delay to use when tklock_blank_disable is set */ +#define DISABLED_VISUAL_BLANK_DELAY (24*60*60) // 24h + /** Touchscreen double tap gesture policy */ static gint doubletap_gesture_policy = DEFAULT_DOUBLETAP_GESTURE_POLICY; @@ -1308,6 +1317,12 @@ { (void)data; + /* Keep the timeout alive while tklock_blank_disable is set */ + if( tklock_blank_disable ) { + mce_log(LL_INFO, "renew fake visual_blank_timeout"); + return TRUE; + } + cancel_tklock_visual_blank_timeout(); if (saved_tklock_state == MCE_TKLOCK_VISUAL_STATE) @@ -1330,6 +1345,8 @@ datapipe_get_gint(alarm_ui_state_pipe); call_state_t call_state = datapipe_get_gint(call_state_pipe); + gint delay = DEFAULT_VISUAL_BLANK_DELAY; + cancel_tklock_dim_timeout(); cancel_tklock_visual_blank_timeout(); @@ -1340,9 +1357,14 @@ (alarm_ui_state == MCE_ALARM_UI_RINGING_INT32)) goto EXIT; + if( tklock_blank_disable ) { + mce_log(LL_INFO, "set up fake visual_blank_timeout"); + delay = DISABLED_VISUAL_BLANK_DELAY; + } + /* Setup blank timeout */ tklock_visual_blank_timeout_cb_id = - g_timeout_add_seconds(DEFAULT_VISUAL_BLANK_DELAY, tklock_visual_blank_timeout_cb, NULL); + g_timeout_add_seconds(delay, tklock_visual_blank_timeout_cb, NULL); EXIT: return; @@ -1358,6 +1380,12 @@ { (void)data; + /* Keep the timeout alive while tklock_blank_disable is set */ + if( tklock_blank_disable ) { + mce_log(LL_INFO, "renew fake visual_dim_timeout"); + return TRUE; + } + tklock_dim_timeout_cb_id = 0; if (blank_immediately == TRUE) { @@ -1392,11 +1420,18 @@ */ static void setup_tklock_dim_timeout(void) { + gint delay = dim_delay; + cancel_tklock_dim_timeout(); + if( tklock_blank_disable ) { + mce_log(LL_INFO, "set up fake visual_dim_timeout"); + delay = DISABLED_VISUAL_BLANK_DELAY; + } + /* Setup new timeout */ tklock_dim_timeout_cb_id = - g_timeout_add_seconds(dim_delay, tklock_dim_timeout_cb, NULL); + g_timeout_add_seconds(delay, tklock_dim_timeout_cb, NULL); } /** @@ -2069,6 +2104,23 @@ doubletap_gesture_policy = DEFAULT_DOUBLETAP_GESTURE_POLICY; } + } else if(id == tklock_blank_disable_id) { + gint old = tklock_blank_disable; + + tklock_blank_disable = gconf_value_get_int(gcv); + + mce_log(LL_NOTICE, "tklock_blank_disable: %d -> %d", + old, tklock_blank_disable); + + if( tklock_blank_disable == old ) { + // no need to change the timers + } + else if( tklock_visual_blank_timeout_cb_id ) { + setup_tklock_visual_blank_timeout(); + } + else if( tklock_dim_timeout_cb_id ) { + setup_tklock_dim_timeout(); + } } else { mce_log(LL_WARN, "Spurious GConf value received; confused!"); } @@ -3203,6 +3255,17 @@ append_output_trigger_to_datapipe(&heartbeat_pipe, heartbeat_trigger); + /* Config tracking for disabling automatic screen dimming/blanking + * while showing lockscreen. This is demo/debugging feature, so sane + * defaults must be used and no error checking is needed. */ + mce_gconf_notifier_add(MCE_GCONF_LOCK_PATH, + MCE_GCONF_TK_AUTO_BLANK_DISABLE_PATH, + tklock_gconf_cb, + &tklock_blank_disable_id); + + mce_gconf_get_int(MCE_GCONF_TK_AUTO_BLANK_DISABLE_PATH, + &tklock_blank_disable); + /* Touchscreen/keypad autolock */ /* Since we've set a default, error handling is unnecessary */ /*(void)mce_gconf_get_bool(MCE_GCONF_TK_AUTOLOCK_ENABLED_PATH, @@ -3341,6 +3404,11 @@ */ void mce_tklock_exit(void) { + /* Remove gconf change notifiers */ + if( tklock_blank_disable_id ) { + mce_gconf_notifier_remove(GINT_TO_POINTER(tklock_blank_disable_id), 0); + } + /* Remove triggers/filters from datapipes */ remove_output_trigger_from_datapipe(&heartbeat_pipe, heartbeat_trigger); --- tklock.h +++ tklock.h @@ -85,6 +85,9 @@ /** Path to the touchscreen/keypad double tap gesture GConf setting */ #define MCE_GCONF_TK_DOUBLE_TAP_GESTURE_PATH MCE_GCONF_LOCK_PATH "/tklock_double_tap_gesture" +/** Path to the automatic tklock dim/blank disable GConf setting */ +#define MCE_GCONF_TK_AUTO_BLANK_DISABLE_PATH MCE_GCONF_LOCK_PATH "/tklock_blank_disable" + /** Name of D-Bus callback to provide to Touchscreen/Keypad Lock SystemUI */ #define MCE_TKLOCK_CB_REQ "tklock_callback" /** Delay before the touchscreen/keypad is unlocked */ --- tools/mcetool.c +++ tools/mcetool.c @@ -225,6 +225,10 @@ " set the threshold for the power saving mode;\n" " valid values are:\n" " 10, 20, 30, 40, 50\n" + " -t, --set-tklock-noblank=MODE\n" + " set the touchscreen/keypad autoblank mode;\n" + " valid modes are: \"enabled\" and \"disabled\"\n" + " -k, --set-tklock-mode=MODE\n" " set the touchscreen/keypad lock mode;\n" " valid modes are:\n" @@ -1985,6 +1989,16 @@ { NULL, -1 } }; +/** Lookup table for tklock autoblank policy values + * + * @note These must match the hardcoded values in mce itself. + */ +static const symbol_t tklockblank_values[] = { + { "disabled", 1 }, + { "enabled", 0 }, + { NULL, -1 } +}; + /** * Print mce related information * @@ -2416,9 +2430,18 @@ gint policy; retval = mcetool_gconf_get_int(MCE_GCONF_USE_AUTOSUSPEND_PATH, &policy); - fprintf(stdout, " %-40s %s", "Autosuspend policy", + fprintf(stdout, " %-40s %s\n", "Autosuspend policy", !retval ? "<unset>" : rlookup(suspendpol_values, policy) ?: "<unknown>"); } + + /* Get tklock blank policy */ + { + gint policy; + retval = mcetool_gconf_get_int(MCE_GCONF_TK_AUTO_BLANK_DISABLE_PATH, + &policy); + fprintf(stdout, " %-40s %s\n", "tklock autoblank policy", + !retval ? "<unset>" : rlookup(tklockblank_values, policy) ?: "<unknown>"); + } EXIT: fprintf(stdout, "\n"); @@ -2489,6 +2512,7 @@ gint powerkeyevent = INVALID_EVENT; gint newinhibitmode = -1; + gint newtklockblank = -1; #if MCETOOL_USE_DEMOMODE_HACK gint demomode = -1; #endif @@ -2533,7 +2557,7 @@ DBusBusType bus_type = DBUS_BUS_SYSTEM; // Unused short options left .... - // - - - - - - - - i j - - m - o - q - s t u - w x - z + // - - - - - - - - i j - - m - o - q - - - u - w x - z // - - - - - - - - - - - - - - - - Q - - - - - W X - Z const char optline[] = @@ -2575,6 +2599,7 @@ "M:" // --set-doubletap-mode "O:" // --set-dim-timeouts "s:" // --set-suspend-policy + "t:" // --set-tklock-noblank ; struct option const options[] = { @@ -2597,6 +2622,7 @@ { "set-forced-psm", required_argument, 0, 'F' }, { "set-psm-threshold", required_argument, 0, 'T' }, { "set-tklock-mode", required_argument, 0, 'k' }, + { "set-tklock-noblank", required_argument, 0, 't' }, { "enable-led", no_argument, 0, 'l' }, { "disable-led", no_argument, 0, 'L' }, { "activate-led-pattern", required_argument, 0, 'y' }, @@ -2910,6 +2936,15 @@ get_mce_status = FALSE; break; + case 't': + if( (newtklockblank = lookup(tklockblank_values, optarg)) < 0 ) { + fprintf(stderr, "invalid lockscreen blanking policy: %s\n", + optarg); + goto EXIT; + } + get_mce_status = FALSE; + break; + case 'I': if (!strcmp(optarg, BLANKING_INHIBIT_DISABLED)) { newinhibitmode = 0; @@ -3167,6 +3202,13 @@ goto EXIT; } + if (newtklockblank != -1) { + if (mcetool_gconf_set_int(MCE_GCONF_TK_AUTO_BLANK_DISABLE_PATH, + newtklockblank) == FALSE) + goto EXIT; + } + + #if MCETOOL_USE_DEMOMODE_HACK if (demomode != -1) { if (dbus_send(MCE_SERVICE, MCE_REQUEST_PATH,
