powerd-0.14+14.04.20140415/0000755000015301777760000000000012323077721015340 5ustar pbusernogroup00000000000000powerd-0.14+14.04.20140415/cmake/0000755000015301777760000000000012323077721016420 5ustar pbusernogroup00000000000000powerd-0.14+14.04.20140415/cmake/UseGdbusCodegen.cmake0000644000015301777760000000214212323077401022422 0ustar pbusernogroup00000000000000cmake_minimum_required(VERSION 2.6) if(POLICY CMP0011) cmake_policy(SET CMP0011 NEW) endif(POLICY CMP0011) find_program(GDBUS_CODEGEN NAMES gdbus-codegen DOC "gdbus-codegen executable") if(NOT GDBUS_CODEGEN) message(FATAL_ERROR "Excutable gdbus-codegen not found") endif() function(add_gdbus_codegen) set(_one_value OUTFILES NAME PREFIX NAMESPACE SERVICE_XML) set(_multi_value DEPENDS) cmake_parse_arguments (arg "" "${_one_value}" "${_multi_value}" ${ARGN}) if(arg_PREFIX) set(PREFIX --interface-prefix ${arg_PREFIX}) endif() if(arg_NAMESPACE) set(NAMESPACE --c-namespace ${arg_NAMESPACE}) endif() add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${arg_NAME}.h" "${CMAKE_CURRENT_BINARY_DIR}/${arg_NAME}.c" COMMAND "${GDBUS_CODEGEN}" --generate-c-code "${arg_NAME}" ${PREFIX} ${NAMESPACE} "${arg_SERVICE_XML}" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${arg_DEPENDS} "${arg_SERVICE_XML}" ) set(${arg_OUTFILES} ${${arg_OUTFILES}} "${CMAKE_CURRENT_BINARY_DIR}/${arg_NAME}.c" PARENT_SCOPE) endfunction(add_gdbus_codegen) powerd-0.14+14.04.20140415/cmake/COPYING-CMAKE-SCRIPTS0000644000015301777760000000245712323077401021421 0ustar pbusernogroup00000000000000Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. powerd-0.14+14.04.20140415/cmake/UseGSettings.cmake0000644000015301777760000000371612323077401022010 0ustar pbusernogroup00000000000000# GSettings.cmake, CMake macros written for Marlin, feel free to re-use them. option (GSETTINGS_LOCALINSTALL "Install GSettings Schemas locally instead of to the GLib prefix" ${LOCAL_INSTALL}) option (GSETTINGS_COMPILE "Compile GSettings Schemas after installation" ${GSETTINGS_LOCALINSTALL}) if(GSETTINGS_LOCALINSTALL) message(STATUS "GSettings schemas will be installed locally.") endif() if(GSETTINGS_COMPILE) message(STATUS "GSettings schemas will be compiled.") endif() macro(add_schema SCHEMA_NAME) set(PKG_CONFIG_EXECUTABLE pkg-config) # Have an option to not install the schema into where GLib is if (GSETTINGS_LOCALINSTALL) SET (GSETTINGS_DIR "${CMAKE_INSTALL_PREFIX}/share/glib-2.0/schemas/") else (GSETTINGS_LOCALINSTALL) execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} glib-2.0 --variable prefix OUTPUT_VARIABLE _glib_prefix OUTPUT_STRIP_TRAILING_WHITESPACE) SET (GSETTINGS_DIR "${_glib_prefix}/share/glib-2.0/schemas/") endif (GSETTINGS_LOCALINSTALL) # Run the validator and error if it fails execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compile_schemas OUTPUT_VARIABLE _glib_comple_schemas OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process (COMMAND ${_glib_comple_schemas} --dry-run --schema-file=${CMAKE_CURRENT_SOURCE_DIR}/${SCHEMA_NAME} ERROR_VARIABLE _schemas_invalid OUTPUT_STRIP_TRAILING_WHITESPACE) if (_schemas_invalid) message (SEND_ERROR "Schema validation error: ${_schemas_invalid}") endif (_schemas_invalid) # Actually install and recomple schemas message (STATUS "GSettings schemas will be installed into ${GSETTINGS_DIR}") install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/${SCHEMA_NAME} DESTINATION ${GSETTINGS_DIR} OPTIONAL) if (GSETTINGS_COMPILE) install (CODE "message (STATUS \"Compiling GSettings schemas\")") install (CODE "execute_process (COMMAND ${_glib_comple_schemas} ${GSETTINGS_DIR})") endif () endmacro() powerd-0.14+14.04.20140415/cli/0000755000015301777760000000000012323077721016107 5ustar pbusernogroup00000000000000powerd-0.14+14.04.20140415/cli/powerd-cli.h0000644000015301777760000000306312323077401020322 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #ifndef __POWERD_CLI_H__ #define __POWERD_CLI_H__ #include #define do_test(test) \ ({ \ gboolean result; \ printf("Test: " #test "\n"); \ result = (test); \ printf(" result: %s\n", result ? "PASSED" : "FAILED"); \ result; \ }) extern gboolean silent_errors; #define cli_warn(f, a...) do { if (!silent_errors) g_warning(f, ##a); } while (0) #define cli_debug(f, a...) g_debug(f, ##a) gboolean requestSysState(const char *name, int state, powerd_cookie_t *cookie); gboolean clearSysState(powerd_cookie_t cookie); void powerd_cli_client_test(GDBusProxy *proxy); #endif /* __POWERD_CLI_H__ */ powerd-0.14+14.04.20140415/cli/powerd-cli.c0000644000015301777760000011135012323077401020314 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "powerd-cli.h" #define TEST_NUM_SYS_REQUESTS 5 #define TEST_NUM_DISP_REQUESTS (POWERD_NUM_DISPLAY_STATES * 2) /* Set to TRUE during tests to silence expected errors */ gboolean silent_errors = FALSE; GDBusProxy *powerd_proxy = NULL; char test_dbusname[128] = ""; /* For tests */ GDBusConnection *powerd_cli_bus; const char *powerd_cli_bus_name; struct PublicSysRequest { const char *name; const char *owner; int state; }; struct PublicDispRequest { const char *name; const char *owner; enum powerd_display_state state; guint32 flags; }; struct SysRequestStats { const char *owner; const char *name; unsigned active_count; guint64 active_time; guint64 max_active_time; guint64 active_since; }; struct DispRequestStats { const char *owner; const char *name; unsigned active_count; guint64 active_time; guint64 max_active_time; guint64 active_since; guint64 disp_on_time; guint64 disp_on_since; guint64 flag_on_time[POWERD_NUM_DISPLAY_FLAGS]; guint64 flag_on_since[POWERD_NUM_DISPLAY_FLAGS]; }; static GMainLoop *main_loop = NULL; static powerd_cookie_t main_cookie; static gboolean checkForDbusName(const char *dbusname, int count, GArray *requests, gboolean isSys); static void sig_handler(int signum); static void silence_errors(gboolean silent) { silent_errors = silent; } static void on_powerd_signal (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data) { int system_state; int display_state; guint32 display_flags; if (!strcmp(signal_name,"SysPowerStateChange")) { g_variant_get(parameters, "(i)", &system_state); printf("Received %s: state=%d\n", signal_name, system_state); } else if (!strcmp(signal_name,"DisplayPowerStateChange")) { g_variant_get(parameters, "(iu)", &display_state, &display_flags); printf("Received %s: state=%d flags=%#08x\n", signal_name, display_state, display_flags); } else { cli_debug("Unknown signal from %s: %s", sender_name, signal_name); } } gboolean requestSysState(const char *name, int state, powerd_cookie_t *cookie) { GVariant *ret = NULL; GError *error = NULL; const char *cookie_ptr; gboolean success = TRUE; if (cookie == NULL) { cli_warn("NULL cookie passed to %s", __func__); return FALSE; } ret = g_dbus_proxy_call_sync(powerd_proxy, "requestSysState", g_variant_new("(si)", name, state), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (ret == NULL) { cli_warn("requestSysState failed: %s", error->message); g_error_free(error); return FALSE; } g_variant_get(ret, "(&s)", &cookie_ptr); if (strlen(cookie_ptr) != sizeof(powerd_cookie_t) - 1) { cli_warn("Returned cookie has incorrect size"); success = FALSE; } else { strncpy(*cookie, cookie_ptr, sizeof(powerd_cookie_t)); cli_debug("Got cookie: %s", *cookie); } g_variant_unref(ret); return success; } static gboolean requestDisplayState(struct PublicDispRequest pdr, const char *name, powerd_cookie_t *cookie) { GVariant *ret = NULL; GError *error = NULL; const char *cookie_ptr; gboolean success = TRUE; if (cookie == NULL) { cli_warn("NULL cookie passed to %s", __func__); return FALSE; } ret = g_dbus_proxy_call_sync(powerd_proxy, "requestDisplayState", g_variant_new("(siu)", name, pdr.state, pdr.flags), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (ret == NULL) { cli_warn("requestDisplayState failed: %s", error->message); g_error_free(error); return FALSE; } g_variant_get(ret, "(&s)", &cookie_ptr); if (strlen(cookie_ptr) != sizeof(powerd_cookie_t) - 1) { cli_warn("Returned cookie has incorrect size"); success = FALSE; } else { strncpy(*cookie, cookie_ptr, sizeof(powerd_cookie_t)); cli_debug("Got cookie: %s", *cookie); } g_variant_unref(ret); return success; } static gboolean updateDisplayState(struct PublicDispRequest pdr, powerd_cookie_t cookie) { GVariant *ret = NULL; GError *error = NULL; gboolean success = TRUE; if (cookie == NULL) { cli_warn("NULL cookie passed to %s", __func__); return FALSE; } ret = g_dbus_proxy_call_sync(powerd_proxy, "updateDisplayState", g_variant_new("(siu)", cookie, pdr.state, pdr.flags), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!ret) { cli_warn("updateDisplayState failed: %s", error->message); g_error_free(error); return FALSE; } g_variant_unref(ret); return success; } static GArray* listSysRequests() { GVariant *ret, *item = NULL; GVariantIter *iter = NULL; GArray *retarray = g_array_new(FALSE, FALSE, sizeof(struct PublicSysRequest)); GError *error = NULL; struct PublicSysRequest psr; ret = g_dbus_proxy_call_sync(powerd_proxy, "listSysRequests", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (ret == NULL) { cli_warn("listSysRequests failed: %s", error->message); g_error_free(error); } else { g_variant_get(ret, "(a(ssi))", &iter); while ((item = g_variant_iter_next_value (iter))) { g_variant_get_child (item, 0, "s", &psr.name); g_variant_get_child (item, 1, "s", &psr.owner); g_variant_get_child (item, 2, "i", &psr.state); g_array_append_val(retarray, psr); g_variant_unref(item); } g_variant_unref(ret); } return retarray; } static void printSysRequests(GArray *requests) { int i; struct PublicSysRequest *psr; printf("System State Requests:\n"); if (requests->len == 0) { printf(" None\n"); } else { for (i = 0; i < requests->len; i++) { psr = &g_array_index(requests, struct PublicSysRequest, i); printf(" Name: %s, Owner: %s, State: %d\n", psr->name, psr->owner, psr->state); } } } static GArray* listDisplayRequests() { GVariant *ret, *item = NULL; GVariantIter *iter = NULL; GArray *retarray = g_array_new(FALSE, FALSE, sizeof(struct PublicDispRequest)); GError *error = NULL; struct PublicDispRequest pdr; ret = g_dbus_proxy_call_sync(powerd_proxy, "listDisplayRequests", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (ret == NULL) { cli_warn("listDisplayRequests failed: %s", error->message); g_error_free(error); } else { g_variant_get(ret, "(a(ssiu))", &iter); while ((item = g_variant_iter_next_value (iter))) { g_variant_get_child(item, 0, "s", &pdr.name); g_variant_get_child(item, 1, "s", &pdr.owner); g_variant_get_child(item, 2, "i", &pdr.state); g_variant_get_child(item, 3, "u", &pdr.flags); g_array_append_val(retarray, pdr); g_variant_unref(item); } g_variant_unref(ret); } return retarray; } static void printDisplayRequests(GArray *requests) { int i; struct PublicDispRequest *pdr; printf("Display State Requests:\n"); if (requests->len == 0) { printf(" None\n"); } else { for (i = 0; i < requests->len; i++) { pdr = &g_array_index(requests, struct PublicDispRequest, i); printf(" Name: %s, Owner: %s, State: %d, Flags: %#08x\n", pdr->name, pdr->owner, pdr->state, pdr->flags); } } } static GArray * getSysRequestStats(void) { GVariant *ret, *item; GVariantIter *iter; GArray *retarray; GError *error; retarray = g_array_new(FALSE, FALSE, sizeof(struct SysRequestStats)); error = NULL; ret = g_dbus_proxy_call_sync(powerd_proxy, "getSysRequestStats", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (ret == NULL) { cli_warn("getSysRequestStats failed: %s", error->message); g_error_free(error); } else { g_variant_get(ret, "(a(ssuttt))", &iter); while ((item = g_variant_iter_next_value (iter))) { struct SysRequestStats stats; g_variant_get_child(item, 0, "s", &stats.owner); g_variant_get_child(item, 1, "s", &stats.name); g_variant_get_child(item, 2, "u", &stats.active_count); g_variant_get_child(item, 3, "t", &stats.active_time); g_variant_get_child(item, 4, "t", &stats.max_active_time); g_variant_get_child(item, 5, "t", &stats.active_since); g_array_append_val(retarray, stats); g_variant_unref(item); } g_variant_unref(ret); } return retarray; } static void printSysRequestStats(GArray *stats) { int i; struct SysRequestStats *stat; printf("System Request Statistics:\n"); if (stats->len == 0) { printf(" None\n"); } else { printf(" %-16.16s %-20.20s %-8.8s %-16.16s %-16.16s %-16.16s\n", "", "", "Active", "", "Max Active", ""); printf(" %-16.16s %-20.20s %-8.8s %-16.16s %-16.16s %-16.16s\n", "Owner", "Name", "Count", "Active Time", "Time", "Active Since"); for (i = 0; i < stats->len; i++) { stat = &g_array_index(stats, struct SysRequestStats, i); printf(" %-16.16s %-20.20s %-8u %-16.6f %-16.6f %-16.6f\n", stat->owner, stat->name, stat->active_count, (double)stat->active_time / 1000000.0f, (double)stat->max_active_time / 1000000.0f, (double)stat->active_since / 1000000.0f); } } } static GArray * getDispRequestStats(void) { GVariant *ret, *item; GVariantIter *iter; GArray *retarray; GError *error; retarray = g_array_new(FALSE, FALSE, sizeof(struct DispRequestStats)); error = NULL; ret = g_dbus_proxy_call_sync(powerd_proxy, "getDispRequestStats", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (ret == NULL) { cli_warn("getDispRequestStats failed: %s", error->message); g_error_free(error); } else { g_variant_get(ret, "(a(ssutttttatat))", &iter); while ((item = g_variant_iter_next_value (iter))) { struct DispRequestStats stats; GVariantIter *array_iter; guint64 val; int i; g_variant_get_child(item, 0, "s", &stats.owner); g_variant_get_child(item, 1, "s", &stats.name); g_variant_get_child(item, 2, "u", &stats.active_count); g_variant_get_child(item, 3, "t", &stats.active_time); g_variant_get_child(item, 4, "t", &stats.max_active_time); g_variant_get_child(item, 5, "t", &stats.active_since); g_variant_get_child(item, 6, "t", &stats.disp_on_time); g_variant_get_child(item, 7, "t", &stats.disp_on_since); g_variant_get_child(item, 8, "at", &array_iter); i = 0; while (g_variant_iter_loop(array_iter, "t", &val)) { if (i >= POWERD_NUM_DISPLAY_FLAGS) break; stats.flag_on_time[i++] = val; } g_variant_iter_free(array_iter); g_variant_get_child(item, 9, "at", &array_iter); i = 0; while (g_variant_iter_loop(array_iter, "t", &val)) { if (i >= POWERD_NUM_DISPLAY_FLAGS) break; stats.flag_on_since[i++] = val; } g_variant_iter_free(array_iter); g_array_append_val(retarray, stats); g_variant_unref(item); } g_variant_unref(ret); } return retarray; } static void printDispRequestStats(GArray *stats) { int i, j; struct DispRequestStats *stat; printf("Display Request Statistics:\n"); if (stats->len == 0) { printf(" None\n"); } else { printf(" %-16.16s %-20.20s %-8.8s %-16.16s %-16.16s %-16.16s %-16.16s %-16.16s", "", "", "Active", "", "Max Active", "", "Display On", "Display On"); for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++) printf(" Flag %-2d Flag %-2d ", i, i); printf("\n %-16.16s %-20.20s %-8.8s %-16.16s %-16.16s %-16.16s %-16.16s %-16.16s", "Owner", "Name", "Count", "Active Time", "Time", "Active Since", "Time", "Since"); for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++) printf(" On Time On Since "); printf("\n"); for (i = 0; i < stats->len; i++) { stat = &g_array_index(stats, struct DispRequestStats, i); printf(" %-16.16s %-20.20s %-8u %-16.6f %-16.6f %-16.6f %-16.6f %-16.6f", stat->owner, stat->name, stat->active_count, (double)stat->active_time / 1000000.0f, (double)stat->max_active_time / 1000000.0f, (double)stat->active_since / 1000000.0f, (double)stat->disp_on_time / 1000000.0f, (double)stat->disp_on_since / 1000000.0f); for (j = 0; j < POWERD_NUM_DISPLAY_FLAGS; j++) { printf(" %-16.6f", (double)stat->flag_on_time[j] / 1000000.0f); printf(" %-16.6f", (double)stat->flag_on_since[j] / 1000000.0f); } printf("\n"); } } } gboolean clearSysState(powerd_cookie_t cookie) { GVariant *ret = NULL; GError *error = NULL; ret = g_dbus_proxy_call_sync(powerd_proxy, "clearSysState", g_variant_new("(s)", cookie), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (ret == NULL) { cli_warn("clearSysState failed: %s", error->message); g_error_free(error); return FALSE; } g_variant_unref(ret); return TRUE; } static gboolean clearDisplayState(powerd_cookie_t cookie) { GVariant *ret = NULL; GError *error = NULL; ret = g_dbus_proxy_call_sync(powerd_proxy, "clearDisplayState", g_variant_new("(s)", cookie), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (ret == NULL) { cli_warn("clearDisplayState failed: %s", error->message); g_error_free(error); return FALSE; } g_variant_unref(ret); return TRUE; } static void listenForSignals(GDBusProxy *proxy) { main_loop = g_main_loop_new (NULL, FALSE); signal(SIGINT, sig_handler); g_signal_connect(proxy, "g-signal", G_CALLBACK (on_powerd_signal), NULL); printf("Waiting for events, press ctrl-c to quit\n"); g_main_loop_run(main_loop); g_main_loop_unref(main_loop); } static void cb_child_watch(GPid pid, gint status, gpointer *data) { GArray *requests = NULL; // Make sure that clean-up worked if (test_dbusname[0] != 0) { requests = listSysRequests(); do_test(checkForDbusName(test_dbusname, 0, requests, TRUE)); g_array_free(requests, TRUE); requests = listDisplayRequests(); do_test(checkForDbusName(test_dbusname, 0, requests, TRUE)); g_array_free(requests, TRUE); } else { printf("Did not find child's dbus name\n"); printf(" result: FAILED\n"); } // Make sure clean-up didn't remove our request, note we don't pass // the child PID here. requests = listSysRequests(); do_test(checkForDbusName(powerd_cli_bus_name, 1, requests, TRUE)); g_array_free(requests, TRUE); /* Close pid */ g_spawn_close_pid(pid); g_main_loop_quit(main_loop); //cleanup do_test(clearSysState(main_cookie) == TRUE); requests = listSysRequests(); do_test(checkForDbusName(powerd_cli_bus_name, 0, requests, TRUE)); g_array_free(requests, TRUE); } static gboolean cb_out_watch(GIOChannel *channel, GIOCondition cond, gpointer *data) { gchar *string = NULL; gsize size; GIOStatus status; if (cond == G_IO_HUP) { g_io_channel_unref(channel); return FALSE; } status = g_io_channel_read_line(channel, &string, &size, NULL, NULL); if (status == G_IO_STATUS_NORMAL) { if (sscanf(string, "DBUSNAME: %s\n", test_dbusname) == 1) { cli_debug("Parent found child's dbusname as: %s", test_dbusname); } g_free(string); } return TRUE; } static void runDbusNameVanishTests(char *progname) { gint out = -1; gboolean ret; GPid pid; GIOChannel *out_ch = NULL; gchar *argv[] = {progname, "dbusnametest", NULL}; main_loop = g_main_loop_new (NULL, FALSE); // Hold active state request as long we're running tests requestSysState("main-req", POWERD_SYS_STATE_ACTIVE, &main_cookie); ret = g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL, &out, NULL, NULL); if (!ret) { cli_warn("Failed to spawn test app"); return; } g_child_watch_add(pid, (GChildWatchFunc)cb_child_watch, NULL); out_ch = g_io_channel_unix_new(out); g_io_add_watch(out_ch, G_IO_IN|G_IO_HUP, (GIOFunc)cb_out_watch, NULL); g_main_loop_run(main_loop); g_main_loop_unref(main_loop); } static void runSysTests() { int i; powerd_cookie_t cookie, cookies[TEST_NUM_SYS_REQUESTS]; GArray *requests = NULL; silence_errors(TRUE); // Hold active state request as long we're running tests requestSysState("main-req", POWERD_SYS_STATE_ACTIVE, &main_cookie); for (i = 0; i < TEST_NUM_SYS_REQUESTS; i++) { char name[16]; snprintf(name, sizeof(name), "test-cookie-%d", i); do_test(requestSysState(name, POWERD_SYS_STATE_ACTIVE, &cookies[i]) == TRUE); } // Make sure we have at least NUM_REQUESTS + 1 requests = listSysRequests(); do_test(requests->len >= TEST_NUM_SYS_REQUESTS+1); do_test(checkForDbusName(powerd_cli_bus_name, TEST_NUM_SYS_REQUESTS+1, requests, TRUE)); g_array_free(requests, TRUE); for (i = 0; i < TEST_NUM_SYS_REQUESTS; i++) do_test(clearSysState(cookies[i]) == TRUE); // We should still have at least 1 request here requests = listSysRequests(); do_test(requests->len >= 1); do_test(checkForDbusName(powerd_cli_bus_name, 1, requests, TRUE)); g_array_free(requests, TRUE); // Now try and freeing the same cookies again. They should all be // invalid and thus the request fails. for (i = 0; i < TEST_NUM_SYS_REQUESTS; i++) do_test(clearSysState(cookies[i]) == FALSE); // Test releasing an invalid cookie format do_test(clearSysState("bad cookie") == FALSE); // We should still have at least 1 request here requests = listSysRequests(); do_test(requests->len >= 1); do_test(checkForDbusName(powerd_cli_bus_name, 1, requests, TRUE)); g_array_free(requests, TRUE); //cannot request the suspend state, this will fail do_test(requestSysState("test-cookie", POWERD_SYS_STATE_SUSPEND, &cookie) == FALSE); //invalid values do_test(requestSysState("test-cookie", -1, &cookie) == FALSE); do_test(requestSysState("test-cookie", POWERD_NUM_POWER_STATES, &cookie) == FALSE); do_test(requestSysState("test-cookie", POWERD_NUM_POWER_STATES+1, &cookie) == FALSE); //cleanup do_test(clearSysState(main_cookie) == TRUE); do_test(checkForDbusName(powerd_cli_bus_name, 0, requests, TRUE)); silence_errors(FALSE); } static void runDisplayTests() { int i; int state = 0; gboolean bright = FALSE; powerd_cookie_t cookie, cookies[TEST_NUM_DISP_REQUESTS]; GArray *requests = NULL; struct PublicDispRequest pdr = {0,}; silence_errors(TRUE); // Hold active state request as long we're running tests requestSysState("main-req", POWERD_SYS_STATE_ACTIVE, &main_cookie); for (i = 0; i < TEST_NUM_DISP_REQUESTS; i++) { char name[16]; snprintf(name, sizeof(name), "disp-test-%d", i); pdr.state = state++; pdr.flags = 0; if (bright) { pdr.flags |= POWERD_DISPLAY_FLAG_BRIGHT; } do_test(requestDisplayState(pdr, name, &cookies[i]) == TRUE); if (state >= POWERD_NUM_DISPLAY_STATES) { state = 0; } bright = !bright; } // Make sure we have at least NUM_REQUESTS requests = listDisplayRequests(); do_test(requests->len >= TEST_NUM_DISP_REQUESTS); // We should see our PID NUM_REQUESTS times do_test(checkForDbusName(powerd_cli_bus_name, TEST_NUM_DISP_REQUESTS, requests, FALSE)); g_array_free(requests, TRUE); for (i = 0; i < TEST_NUM_DISP_REQUESTS; i++) do_test(clearDisplayState(cookies[i]) == TRUE); requests = listDisplayRequests(); // We aren't holding anymore with this PID do_test(checkForDbusName(powerd_cli_bus_name, 0, requests, FALSE)); g_array_free(requests, TRUE); // Now try and freeing the same cookies again. They should all be // invalid and thus the request fails. for (i = 0; i < TEST_NUM_DISP_REQUESTS; i++) do_test(clearDisplayState(cookies[i]) == FALSE); // Test releasing an invalid cookie format do_test(clearDisplayState("bad cookie") == FALSE); //invalid values pdr.state = -1; do_test(requestDisplayState(pdr, "disp-test", &cookie) == FALSE); pdr.state = POWERD_NUM_DISPLAY_STATES; do_test(requestDisplayState(pdr, "disp-test", &cookie) == FALSE); pdr.state = POWERD_DISPLAY_STATE_ON; pdr.flags = 0xdeadbeef; do_test(requestDisplayState(pdr, "disp-test", &cookie) == FALSE); // updateDisplayState tests pdr.state = POWERD_DISPLAY_STATE_ON; pdr.flags = 0; do_test(requestDisplayState(pdr, "disp-test", &cookie) == TRUE); // update with same state should be okay do_test(updateDisplayState(pdr, cookie) == TRUE); pdr.state = POWERD_DISPLAY_STATE_DONT_CARE; do_test(updateDisplayState(pdr, cookie) == TRUE); pdr.flags = POWERD_DISPLAY_FLAG_DISABLE_AUTOBRIGHTNESS; do_test(updateDisplayState(pdr, cookie) == TRUE); pdr.state = -1; do_test(updateDisplayState(pdr, cookie) == FALSE); do_test(clearDisplayState(cookie) == TRUE); // update with same cookie after clear should fail do_test(updateDisplayState(pdr, cookie) == FALSE); // update with garbage cookie should faile do_test(updateDisplayState(pdr, "bad cookie") == FALSE); //cleanup do_test(clearSysState(main_cookie) == TRUE); do_test(checkForDbusName(powerd_cli_bus_name, 0, requests, TRUE)); silence_errors(FALSE); } // Check that dbus owner appears count times in a list of sys/disp requests static gboolean checkForDbusName(const char *dbusname, int count, GArray *requests, gboolean isSys) { int i; int found = 0; struct PublicSysRequest *psr; struct PublicDispRequest *pdr; if (requests == NULL) { cli_warn("NULL requests passed to %s", __func__); return FALSE; } for (i=0; ilen; i++) { if (isSys) { psr = &g_array_index(requests, struct PublicSysRequest, i); if (!(strcmp(psr->owner, dbusname))) { found++; } } else { pdr = &g_array_index(requests, struct PublicDispRequest, i); if (!(strcmp(pdr->owner, dbusname))) { found++; } } } if (found != count) { cli_debug("Expected %d requests from DbusName (%s), found %d", count, dbusname, found); return FALSE; } return TRUE; } static void sig_handler(int signum) { g_main_loop_quit(main_loop); } /* Caller ensures that we have at least 4 arguments in argv */ static gboolean buildDisplayRequest(char **argv, int argc, struct PublicDispRequest *pdr) { int i; if (!strcasecmp(argv[2],"on")) { pdr->state = POWERD_DISPLAY_STATE_ON; cli_debug("Requesting Display On"); } else if (!strcasecmp(argv[2],"dc")) { pdr->state = POWERD_DISPLAY_STATE_DONT_CARE; cli_debug("Requesting Display Don't Care"); } else { fprintf(stderr,"invalid state %s, must be either on or dc\n", argv[2]); return FALSE; } pdr->flags = 0; for (i=3; iflags |= POWERD_DISPLAY_FLAG_USE_PROXIMITY; cli_debug("Requesting Proximity Sensor Enabled"); } else if (!strcmp(argv[i],"disableab")) { pdr->flags |= POWERD_DISPLAY_FLAG_DISABLE_AUTOBRIGHTNESS; cli_debug("Requesting Proximity Disable AutoBrightness"); } else if (!strcasecmp(argv[i],"bright")) { pdr->flags |= POWERD_DISPLAY_FLAG_BRIGHT; cli_debug("Requesting Bright"); } } return TRUE; } static void setUserAutobrightness(gboolean enable) { GVariant *ret = NULL; GError *error = NULL; ret = g_dbus_proxy_call_sync(powerd_proxy, "userAutobrightnessEnable", g_variant_new("(b)", enable), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!ret) { cli_warn("userAutobrightnessEnable failed: %s", error->message); g_error_free(error); } else { g_variant_unref(ret); } } static gboolean getBrightnessParams(int *min, int *max, int *dflt, gboolean *ab_supported) { GVariant *ret = NULL; GError *error = NULL; ret = g_dbus_proxy_call_sync(powerd_proxy, "getBrightnessParams", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!ret) { cli_warn("getBrightnessParams failed: %s", error->message); g_error_free(error); return FALSE; } g_variant_get(ret, "((iiib))", min, max, dflt, ab_supported); g_variant_unref(ret); return TRUE; } static void setUserBrightness(int brightness) { GVariant *ret = NULL; GError *error = NULL; ret = g_dbus_proxy_call_sync(powerd_proxy, "setUserBrightness", g_variant_new("(i)", brightness), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!ret) { cli_warn("setUserBrightness failed: %s", error->message); g_error_free(error); } else { g_variant_unref(ret); } } static void usage(const char *progname) { printf("\n\n%s Options:\n",progname); printf("active - request the active state, prevent the system from\n"\ "\tsuspending. This does not modify the screen state.\n"); printf("active-nc - same as active, but relies on powerd to cleanup\n"\ "\tthe request. This is only for testing and will immediately\n"\ "exit, causing the request to be dropped.\n"); printf("autobrightness - enable or disable autobrightness.\n"); printf("brightness - set user screen brightness.\n"); printf("brightness-params - get brightness parameters (min, max, etc.)\n"); printf("clear-sys - clear a System state request given a cookie.\n"); printf("clear-disp - clear a Display state request given a cookie.\n"); printf("client-test - test powerd registration / ack API.\n"); printf("display [bright] [proximity] [disableab]\n"\ "\tMake a display state request with the input parameters.\n"\ "\tThe first argument represents the state of the display:\n"\ "\tOn (on) or Don't Care (dc),\n"\ "\tThe final optional arguments respectively:\n"\ "\t * make the screen bright [bright]\n"\ "\t * enable the proximity sensor [proximity]\n"\ "\t * disable autobrightness [disableab]\n"); printf("help\t- display this usage information.\n"); printf("list\t- list outstanding requests.\n"); printf("listen\t- listen for signals from powerd. This runs a\n"\ "\t gmainloop and must be manually killed.\n"); printf("stats\t- print request statistics.\n"); printf("test\t- runs tests.\n"); } static void sigint_quit(int signal) { /* do nothing */ } int main (int argc, char **argv) { GError *error = NULL; uid_t myeuid; GArray *requests = NULL; struct PublicDispRequest pdr; GDBusConnection *bus = NULL; const char *bus_name = NULL; powerd_cookie_t cookie; if ((argc < 2) || (argc > 6)) { fprintf(stderr,"Incorrect number of options\n"); usage(argv[0]); return -1; } if ((strcmp(argv[1],"list")) && (strcmp(argv[1],"stats")) && (strcmp(argv[1],"active")) && (strcmp(argv[1],"active-nc")) && (strcmp(argv[1],"test")) && (strcmp(argv[1],"dbusnametest")) && (strcmp(argv[1],"listen")) && (strcmp(argv[1],"display")) && (strcmp(argv[1],"help")) && (strcmp(argv[1],"clear-disp")) && (strcmp(argv[1],"clear-sys")) && (strcmp(argv[1],"client-test")) && (strcmp(argv[1], "autobrightness")) && (strcmp(argv[1], "brightness-params")) && (strcmp(argv[1], "brightness"))) { fprintf(stderr,"Invalid option %s\n",argv[1]); usage(argv[0]); return -1; } if (!strcmp(argv[1],"help")) { usage(argv[0]); return 0; } myeuid = geteuid(); if (myeuid != 0) { fprintf(stderr,"%s: Running as user is not fully supported.\n", argv[0]); } signal(SIGINT, sigint_quit); powerd_proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, "com.canonical.powerd", "/com/canonical/powerd", "com.canonical.powerd", NULL, &error); if (error != NULL) { cli_warn("could not connect to powerd: %s", error->message); g_error_free(error); return -1; } if (!strcmp(argv[1],"list")) { requests = listSysRequests(); printSysRequests(requests); g_array_free(requests, TRUE); requests = listDisplayRequests(); printDisplayRequests(requests); g_array_free(requests, TRUE); } else if (!strcmp(argv[1], "stats")) { requests = getSysRequestStats(); printSysRequestStats(requests); g_array_free(requests, TRUE); requests = getDispRequestStats(); printDispRequestStats(requests); g_array_free(requests, TRUE); } else if (!strcmp(argv[1],"active-nc")) { requestSysState("active-nc", POWERD_SYS_STATE_ACTIVE,&cookie); printf("Power State requested, cookie is %s\n", cookie); return 0; } else if (!strcmp(argv[1],"active")) { requestSysState("active", POWERD_SYS_STATE_ACTIVE,&cookie); printf("Power State requested, cookie is %s.\nPress ctrl-c to exit.\n", cookie); pause(); /* wait for SIGINT */ clearSysState(cookie); return 0; } else if (!strcmp(argv[1],"display")) { if (argc < 3) { fprintf(stderr,"display requires other arguments\n"); usage(argv[0]); return -1; } if (buildDisplayRequest(argv,argc,&pdr) == FALSE) { usage(argv[0]); return -1; } requestDisplayState(pdr, "disp-test", &cookie); printf("Display State requested, cookie is %s.\nPress ctrl-c to exit.\n", cookie); pause(); /* wait for SIGINT */ clearDisplayState(cookie); return 0; } else if (!strncmp(argv[1],"clear",strlen("clear"))) { if (argc != 3) { fprintf(stderr,"clear requires a cookie\n"); usage(argv[0]); return -1; } else { uuid_t uuid; if (uuid_parse(argv[2], uuid)==0) { if (!strcmp(argv[1],"clear-sys")) { clearSysState(argv[2]); } else if (!strcmp(argv[1],"clear-disp")) { clearDisplayState(argv[2]); } else { // Note: We should never get here due to earlier checks fprintf(stderr,"Invalid option %s\n",argv[1]); usage(argv[0]); return -1; } } else { fprintf(stderr,"invalid cookie, cookie should be a UUID\n"); usage(argv[0]); } } } else if (!strcmp(argv[1],"test")) { powerd_cli_bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); if (!powerd_cli_bus) { fprintf(stderr, "Could not connect to message bus\n"); exit(-1); } powerd_cli_bus_name = g_dbus_connection_get_unique_name(powerd_cli_bus); if (!powerd_cli_bus_name) { fprintf(stderr, "Could not get unique bus name\n"); exit(-1); } runDbusNameVanishTests(argv[0]); runSysTests(); runDisplayTests(); g_object_unref(powerd_cli_bus); powerd_cli_bus = NULL; powerd_cli_bus_name = NULL; } else if (!strcmp(argv[1],"listen")) { listenForSignals(powerd_proxy); } else if (!strcmp(argv[1],"dbusnametest")) { bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); bus_name = g_dbus_connection_get_unique_name(bus); cli_debug("Child dbus name is %s", bus_name); // This printf is read by the parent printf("DBUSNAME: %s\n", bus_name); // Grab some requests so we can see that they get cleared later requestSysState("dbusnametest1", POWERD_SYS_STATE_ACTIVE,&cookie); requestSysState("dbusnametest2", POWERD_SYS_STATE_ACTIVE,&cookie); requestSysState("dbusnametest3", POWERD_SYS_STATE_ACTIVE,&cookie); pdr.state = POWERD_DISPLAY_STATE_DONT_CARE; pdr.flags = 0; requestDisplayState(pdr, "dbusnametest1", &cookie); requestDisplayState(pdr, "dbusnametest2", &cookie); requestDisplayState(pdr, "dbusnametest3", &cookie); g_object_unref(bus); // Exit here without cleanup return 0; } else if (!strcmp(argv[1], "client-test")) { powerd_cli_client_test(powerd_proxy); } else if (!strcmp(argv[1], "autobrightness")) { gboolean enable; if (argc != 3) { usage(argv[0]); exit(-1); } if (!strcmp(argv[2], "enable")) { enable = TRUE; } else if (!strcmp(argv[2], "disable")) { enable = FALSE; } else { fprintf(stderr, "Invalid autobrightness state\n"); usage(argv[0]); exit(-1); } setUserAutobrightness(enable); } else if (!strcmp(argv[1], "brightness-params")) { int min, max, dflt, ab; if (getBrightnessParams(&min, &max, &dflt, &ab)) { printf("Minimum Brightness: %d\n" "Maximum Brightness: %d\n" "Default Brightness: %d\n" "Autobrightness: %ssupported\n", min, max, dflt, ab ? "" : "not "); } } else if (!strcmp(argv[1], "brightness")) { long brightness; char *endp; if (argc != 3) { usage(argv[0]); exit(-1); } errno = 0; brightness = strtol(argv[2], &endp, 10); if (errno == ERANGE || *endp != '\0' || brightness < 0 || brightness > INT_MAX) { fprintf(stderr, "Invalid brightness %s\n", argv[2]); usage(argv[0]); exit(-1); } setUserBrightness((int)brightness); } return 0; } powerd-0.14+14.04.20140415/cli/client-test.c0000644000015301777760000001745612323077401020516 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include #include #include "powerd-cli.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) static struct client_test_data { GDBusProxy *proxy; int state; int ack_delay; guint ack_timeout_id; powerd_cookie_t cookie; } test_data; enum ack_status { NOT_ACKED, ACK_SUCCESS, ACK_FAILURE, ACK_TIMEOUT, } ack_status;; static GMainLoop *main_loop; static gboolean run_next_test_handler(gpointer unused); static void reset_test_state(void) { ack_status = NOT_ACKED; } static void run_next_test(void) { g_timeout_add(0, run_next_test_handler, NULL); } static gboolean register_client(GDBusProxy *proxy) { GVariant *ret; GError *error = NULL; ret = g_dbus_proxy_call_sync(proxy, "registerClient", g_variant_new("(s)", "powerd-cli"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!ret) { cli_warn("registerClient failed: %s", error->message); g_error_free(error); return FALSE; } g_variant_unref(ret); return TRUE; } static gboolean unregister_client(GDBusProxy *proxy) { GVariant *ret; GError *error = NULL; ret = g_dbus_proxy_call_sync(proxy, "unregisterClient", g_variant_new("(s)", "powerd-cli"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!ret) { cli_warn("unregisterClient failed: %s", error->message); g_error_free(error); return FALSE; } g_variant_unref(ret); return TRUE; } static gboolean ack_state_change(GDBusProxy *proxy, int state) { GVariant *ret; GError *error = NULL; ret = g_dbus_proxy_call_sync(proxy, "ackStateChange", g_variant_new("(i)", state), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!ret) { cli_warn("ackStateChange failed: %s", error->message); g_error_free(error); ack_status = ACK_FAILURE; return FALSE; } g_variant_unref(ret); ack_status = ACK_SUCCESS; return TRUE; } static gboolean send_ack_handler(gpointer unused) { if (ack_state_change(test_data.proxy, test_data.state)) run_next_test(); return FALSE; } static gboolean ack_timeout_handler(gpointer unused) { test_data.ack_timeout_id = 0; ack_status = ACK_TIMEOUT; run_next_test(); return FALSE; } static void powerd_signal_handler(GDBusProxy *proxy, gchar *sender, gchar *signal, GVariant *params, gpointer unused) { cli_debug("Received signal %s\n", signal); if (strcmp(signal, "SysPowerStateChange")) return; /* Check if we're trying to time out */ if (test_data.ack_delay < 0) return; if (test_data.ack_timeout_id > 0) { g_source_remove(test_data.ack_timeout_id); test_data.ack_timeout_id = 0; } if (test_data.ack_delay == 0) { /* immediate ack */ ack_state_change(proxy, test_data.state); run_next_test(); } else { /* delayed ack */ g_timeout_add(test_data.ack_delay, send_ack_handler, NULL); } } static gboolean register_test(gpointer unused) { gboolean result; result = do_test(register_client(test_data.proxy) == TRUE); if (!result) g_main_loop_quit(main_loop); else { run_next_test(); } return FALSE; } static gboolean ack_test_part1(gpointer unused) { printf("Testing ack on active state transition\n"); test_data.state = POWERD_SYS_STATE_ACTIVE; test_data.ack_delay = 0; reset_test_state(); test_data.ack_timeout_id = g_timeout_add(5000, ack_timeout_handler, NULL); requestSysState("ack-test", test_data.state, &test_data.cookie); return FALSE; } static gboolean ack_test_part2(gpointer unused) { /* Check result of part 1 */ do_test(ack_status == ACK_SUCCESS); reset_test_state(); printf("Testing ack on suspend state transition\n"); test_data.state = POWERD_SYS_STATE_SUSPEND; test_data.ack_delay = 0; test_data.ack_timeout_id = g_timeout_add(5000, ack_timeout_handler, NULL); clearSysState(test_data.cookie); return FALSE; } static gboolean ack_test_check_result(gpointer unused) { do_test(ack_status == ACK_SUCCESS); run_next_test(); return FALSE; } static gboolean no_ack_test_part1(gpointer unused) { printf("Testing active state transition with no ack\n"); reset_test_state(); test_data.state = POWERD_SYS_STATE_ACTIVE; test_data.ack_delay = -1; test_data.ack_timeout_id = g_timeout_add(5000, ack_timeout_handler, NULL); requestSysState("no-ack-test", test_data.state, &test_data.cookie); return FALSE; } static gboolean no_ack_test_part2(gpointer unused) { printf("Testing suspend state transition with no ack\n"); reset_test_state(); test_data.state = POWERD_SYS_STATE_SUSPEND; test_data.ack_delay = -1; test_data.ack_timeout_id = g_timeout_add(5000, ack_timeout_handler, NULL); clearSysState(test_data.cookie); return FALSE; } static gboolean bad_ack_test(gpointer unused) { printf("Testing acknowledge of wrong state\n"); reset_test_state(); test_data.state = POWERD_SYS_STATE_SUSPEND; test_data.ack_delay = 0; test_data.ack_timeout_id = g_timeout_add(5000, ack_timeout_handler, NULL); requestSysState("bad-ack-test", POWERD_SYS_STATE_ACTIVE, &test_data.cookie); return FALSE; } static gboolean bad_ack_test_result(gpointer unused) { do_test(ack_status == ACK_FAILURE); run_next_test(); return FALSE; } static gboolean unregister_test(gpointer unused) { gboolean result; result = do_test(unregister_client(test_data.proxy) == TRUE); if (!result) g_main_loop_quit(main_loop); else run_next_test(); return FALSE; } static gboolean no_unregister_test(gpointer unused) { printf("Testing exit without unregistering\n"); run_next_test(); return FALSE; } static GSourceFunc client_tests[] = { register_test, ack_test_part1, ack_test_part2, ack_test_check_result, no_ack_test_part1, no_ack_test_part2, bad_ack_test, bad_ack_test_result, unregister_test, register_test, no_unregister_test, }; static gboolean run_next_test_handler(gpointer unused) { static int test_num = 0; if (test_data.ack_timeout_id > 0) { g_source_remove(test_data.ack_timeout_id); test_data.ack_timeout_id = 0; } if (test_num >= ARRAY_SIZE(client_tests)) { test_num = 0; g_main_loop_quit(main_loop); return FALSE; } g_timeout_add(0, client_tests[test_num], NULL); test_num++; return FALSE; } void powerd_cli_client_test(GDBusProxy *proxy) { printf("Running client tests, cannot verify all results\n"); silent_errors = TRUE; test_data.proxy = proxy; main_loop = g_main_loop_new(NULL, FALSE); g_signal_connect(proxy, "g-signal", G_CALLBACK(powerd_signal_handler), NULL); g_timeout_add(0, run_next_test_handler, NULL); g_main_loop_run(main_loop); g_main_loop_unref(main_loop); silent_errors = FALSE; } powerd-0.14+14.04.20140415/cli/CMakeLists.txt0000644000015301777760000000120312323077401020636 0ustar pbusernogroup00000000000000set( SRCS powerd-cli.c client-test.c ) link_directories( ${GLIB_LIBRARY_DIRS} ${GIO_LIBRARY_DIRS} ${GIO-UNIX_LIBRARY_DIRS} ${GUDEV_LIBRARY_DIRS} ${UUID_LIBRARY_DIRS} ) include_directories( ${GLIB_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS} ${GIO-UNIX_INCLUDE_DIRS} ${GUDEV_INCLUDE_DIRS} ${UUID_INCLUDE_DIRS} ../src ) add_executable( powerd-cli ${SRCS} ) target_link_libraries( powerd-cli ${GLIB_LIBRARIES} ${GUDEV_LIBRARIES} ${GIO_LIBRARIES} ${GIO-UNIX_LIBRARIES} ${UUID_LIBRARIES} ) install( TARGETS powerd-cli RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib/static ) powerd-0.14+14.04.20140415/data/0000755000015301777760000000000012323077721016251 5ustar pbusernogroup00000000000000powerd-0.14+14.04.20140415/data/device_configs/0000755000015301777760000000000012323077721021220 5ustar pbusernogroup00000000000000powerd-0.14+14.04.20140415/data/device_configs/config-grouper.xml0000644000015301777760000000566512323077401024677 0ustar pbusernogroup00000000000000 true 5 15 50 100 200 400 1000 2000 3000 5000 10000 30000 5 20 30 40 50 60 70 80 130 180 255 255 255 5 powerd-0.14+14.04.20140415/data/device_configs/config-hammerhead.xml0000644000015301777760000000630512323077401025277 0ustar pbusernogroup00000000000000 true 1 4 40 350 600 1000 1600 3000 10000 13 26 73 88 130 167 204 240 254 255 1 82 1 600 powerd-0.14+14.04.20140415/data/device_configs/config-flo.xml0000644000015301777760000000663612323077401023773 0ustar pbusernogroup00000000000000 true 5 15 50 100 200 400 1000 2000 3000 5000 10000 30000 11 18 27 38 48 55 64 74 120 164 225 255 255 5 87 5 600 powerd-0.14+14.04.20140415/data/device_configs/config-maguro.xml0000644000015301777760000000542612323077401024501 0ustar pbusernogroup00000000000000 true 6 9 14 20 30 46 68 103 154 231 346 519 778 1168 1752 2627 3941 5912 8867 19 23 26 30 34 39 45 51 59 67 77 88 101 116 133 152 174 199 228 250 10 powerd-0.14+14.04.20140415/data/device_configs/config-manta.xml0000644000015301777760000000662412323077401024310 0ustar pbusernogroup00000000000000 true 10 30 90 15000 225000 10 13 65 85 220 255 2 powerd-0.14+14.04.20140415/data/device_configs/config-default.xml0000644000015301777760000000766212323077401024637 0ustar pbusernogroup00000000000000 false 4 680 10 255 102 10 powerd-0.14+14.04.20140415/data/device_configs/config-mako.xml0000644000015301777760000000652612323077401024140 0ustar pbusernogroup00000000000000 true 10 50 100 200 400 500 800 1000 1600 3000 10000 14 28 37 51 71 80 96 108 144 181 254 255 1 87 1 600 powerd-0.14+14.04.20140415/data/com.canonical.powerd.gschema.xml0000644000015301777760000000116512323077401024402 0ustar pbusernogroup00000000000000 60 Timeout in seconds for turning off the screen if there is no user activity 45 Timeout in seconds for dimming the screen if there is no user activity. A value of 0 disables auto-dimming. powerd-0.14+14.04.20140415/data/com.canonical.powerd.xml0000644000015301777760000000507312323077401022776 0ustar pbusernogroup00000000000000 powerd-0.14+14.04.20140415/libsuspend/0000755000015301777760000000000012323077721017510 5ustar pbusernogroup00000000000000powerd-0.14+14.04.20140415/libsuspend/sysfs.h0000644000015301777760000000153412323077401021026 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #ifndef SYSFS_H #define SYSFS_H int sysfs_file_exists(const char *path); int sysfs_read(const char *path, void *buf, int len); int sysfs_write(const char *path, const void *buf, int len); #endif /* SYSFS_H */ powerd-0.14+14.04.20140415/libsuspend/common.h0000644000015301777760000000223012323077401021141 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #ifndef SUSPENDIF_H #define SUSPENDIF_H #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) struct suspend_handler { int (*prepare)(void); int (*enter)(void); int (*exit)(void); int (*acquire_wake_lock)(const char *); int (*release_wake_lock)(const char *); }; const struct suspend_handler *autosleep_detect(void); const struct suspend_handler *earlysuspend_detect(void); const struct suspend_handler *legacy_detect(void); const struct suspend_handler *mocksuspend_detect(void); #endif /* SUSPENDIF_H */ powerd-0.14+14.04.20140415/libsuspend/libsuspend.h0000644000015301777760000000201712323077401022024 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #ifndef LIBSUSPEND_H #define LIBSUSPEND_H #ifdef __cplusplus extern "C" { #endif void libsuspend_init(int force_mock); int libsuspend_prepare_suspend(void); int libsuspend_enter_suspend(void); int libsuspend_exit_suspend(void); int libsuspend_acquire_wake_lock(const char *name); int libsuspend_release_wake_lock(const char *name); #ifdef __cplusplus } #endif #endif /* LIBSUSPEND_H */ powerd-0.14+14.04.20140415/libsuspend/autosleep.c0000644000015301777760000000366012323077401021655 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include "common.h" #include "sysfs.h" static const char autosleep_path[] = "/sys/power/autosleep"; static const char wakelock_path[] = "/sys/power/wake_lock"; static const char wakeunlock_path[] = "/sys/power/wake_unlock"; static const char mem_str[] = "mem"; static const char off_str[] = "off"; static int autosleep_enter(void) { int ret = sysfs_write(autosleep_path, mem_str, ARRAY_SIZE(mem_str) - 1); return ret < 0 ? ret : 0; } static int autosleep_exit(void) { int ret = sysfs_write(autosleep_path, off_str, ARRAY_SIZE(off_str) - 1); return ret < 0 ? ret : 0; } static int autosleep_acquire_wake_lock(const char *name) { int ret = sysfs_write(wakelock_path, name, strlen(name)); return ret < 0 ? ret : 0; } static int autosleep_release_wake_lock(const char *name) { int ret = sysfs_write(wakeunlock_path, name, strlen(name)); return ret < 0 ? ret : 0; } static const struct suspend_handler autosleep_handler = { .enter = autosleep_enter, .exit = autosleep_exit, .acquire_wake_lock = autosleep_acquire_wake_lock, .release_wake_lock = autosleep_release_wake_lock, }; const struct suspend_handler *autosleep_detect(void) { if (!sysfs_file_exists(autosleep_path)) return NULL; return &autosleep_handler; } powerd-0.14+14.04.20140415/libsuspend/earlysuspend.c0000644000015301777760000001056712323077401022376 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include #include #include #include "common.h" #include "sysfs.h" /* Use to suppress unused parameter warnings */ #define __unused __attribute__ ((unused)) enum fb_state { FB_SLEEP, FB_AWAKE, NUM_FB_STATES }; static enum fb_state fb_state = FB_AWAKE; static const char *fb_file_names[] = { [FB_SLEEP] = "/sys/power/wait_for_fb_sleep", [FB_AWAKE] = "/sys/power/wait_for_fb_wake" }; static pthread_t fb_monitor_thread; static pthread_mutex_t fb_state_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t fb_state_cond = PTHREAD_COND_INITIALIZER; static int wait_for_fb = 0; static const char state_path[] = "/sys/power/state"; static const char wakelock_path[] = "/sys/power/wake_lock"; static const char wakeunlock_path[] = "/sys/power/wake_unlock"; static const char autosleep_path[] = "/sys/power/autosleep"; static const char mem_str[] = "mem"; static const char on_str[] = "on"; static int wait_for_file(const char *fname) { int fd, ret; char buf; fd = open(fname, O_RDONLY); if (fd == -1) return -errno; do { ret = read(fd, &buf, 1); } while (ret == -1 && errno == EINTR); close(fd); return ret == -1 ? -errno : 0; } static void *fb_monitor_thread_func(__unused void *unused) { enum fb_state next_state; int ret; while (1) { next_state = fb_state == FB_SLEEP ? FB_AWAKE : FB_SLEEP; ret = wait_for_file(fb_file_names[next_state]); if (ret) continue; pthread_mutex_lock(&fb_state_mutex); fb_state = next_state; pthread_cond_signal(&fb_state_cond); pthread_mutex_unlock(&fb_state_mutex); } wait_for_fb = 0; return NULL; } /* Returns 1 if fb monitor thread started, 0 otherwise */ static int start_fb_monitor_thread(void) { if (access(fb_file_names[FB_SLEEP], F_OK)) return 0; if (access(fb_file_names[FB_AWAKE], F_OK)) return 0; return !pthread_create(&fb_monitor_thread, NULL, fb_monitor_thread_func, NULL); } static int earlysuspend_enter(void) { int ret; int len = ARRAY_SIZE(mem_str) - 1; ret = sysfs_write(state_path, mem_str, len); if (ret == len && wait_for_fb) { pthread_mutex_lock(&fb_state_mutex); while (fb_state != FB_SLEEP) pthread_cond_wait(&fb_state_cond, &fb_state_mutex); pthread_mutex_unlock(&fb_state_mutex); } return ret < 0 ? ret : 0; } static int earlysuspend_exit(void) { int ret; int len = ARRAY_SIZE(on_str) - 1; ret = sysfs_write(state_path, on_str, len); if (ret == len && wait_for_fb) { pthread_mutex_lock(&fb_state_mutex); while (fb_state != FB_AWAKE) pthread_cond_wait(&fb_state_cond, &fb_state_mutex); pthread_mutex_unlock(&fb_state_mutex); } return ret < 0 ? ret : 0; } static int earlysuspend_acquire_wake_lock(const char *name) { int ret = sysfs_write(wakelock_path, name, strlen(name)); return ret < 0 ? ret : 0; } static int earlysuspend_release_wake_lock(const char *name) { int ret = sysfs_write(wakeunlock_path, name, strlen(name)); return ret < 0 ? ret : 0; } static const struct suspend_handler earlysuspend_handler = { .enter = earlysuspend_enter, .exit = earlysuspend_exit, .acquire_wake_lock = earlysuspend_acquire_wake_lock, .release_wake_lock = earlysuspend_release_wake_lock, }; const struct suspend_handler *earlysuspend_detect(void) { if (!sysfs_file_exists(autosleep_path) && sysfs_file_exists(wakelock_path) && sysfs_file_exists(state_path)) { wait_for_fb = start_fb_monitor_thread(); return &earlysuspend_handler; } return NULL; } powerd-0.14+14.04.20140415/libsuspend/legacy.c0000644000015301777760000000426412323077401021121 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include "common.h" #include "sysfs.h" static const char state_path[] = "/sys/power/state"; static const char wakelock_path[] = "/sys/power/wake_lock"; static const char wakeup_count_path[] = "/sys/power/wakeup_count"; static const char mem_str[] = "mem"; #define WAKEUP_COUNT_LEN 64 static int wakeup_count_supported, wakeup_count_valid; static char wakeup_count[WAKEUP_COUNT_LEN]; static int legacy_prepare(void) { int ret; if (wakeup_count_supported) { ret = sysfs_read(wakeup_count_path, wakeup_count, WAKEUP_COUNT_LEN); if (ret < 0) { wakeup_count_valid = 0; return ret; } wakeup_count_valid = 1; } return 0; } static int legacy_enter(void) { int ret; if (wakeup_count_supported && wakeup_count_valid) { wakeup_count_valid = 0; ret = sysfs_write(wakeup_count_path, wakeup_count, strlen(wakeup_count)); if (ret < 0) { /* Wakup happened since reading wakeup_count */ return ret; } } ret = sysfs_write(state_path, mem_str, ARRAY_SIZE(mem_str) - 1); return ret < 0 ? ret : 0; } static const struct suspend_handler legacy_handler = { .prepare = legacy_prepare, .enter = legacy_enter, }; const struct suspend_handler *legacy_detect(void) { if (sysfs_file_exists(state_path) && !sysfs_file_exists(wakelock_path)) { wakeup_count_supported = sysfs_file_exists(wakeup_count_path); return &legacy_handler; } return NULL; } powerd-0.14+14.04.20140415/libsuspend/CMakeLists.txt0000644000015301777760000000023112323077401022237 0ustar pbusernogroup00000000000000set(CMAKE_C_FLAGS "-Wall -Werror -Wextra") add_library( suspend libsuspend.c sysfs.c autosleep.c earlysuspend.c legacy.c mocksuspend.c ) powerd-0.14+14.04.20140415/libsuspend/mocksuspend.c0000644000015301777760000000307012323077401022202 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include "common.h" static int mocksuspend_prepare(void) { printf("mocksuspend prepare\n"); return 0; } static int mocksuspend_enter(void) { printf("mocksuspend enter\n"); return 0; } static int mocksuspend_exit(void) { printf("mocksuspend exit\n"); return 0; } static int mocksuspend_acquire_wake_lock(const char *name) { printf("mocksuspend acquire wake lock %s\n", name); return 0; } static int mocksuspend_release_wake_lock(const char *name) { printf("mocksuspend release wake lock %s\n", name); return 0; } static const struct suspend_handler mocksuspend_handler = { .prepare = mocksuspend_prepare, .enter = mocksuspend_enter, .exit = mocksuspend_exit, .acquire_wake_lock = mocksuspend_acquire_wake_lock, .release_wake_lock = mocksuspend_release_wake_lock, }; const struct suspend_handler *mocksuspend_detect(void) { return &mocksuspend_handler; } powerd-0.14+14.04.20140415/libsuspend/libsuspend.c0000644000015301777760000000401012323077401022012 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include "libsuspend.h" #include "common.h" const struct suspend_handler *handler; void libsuspend_init(int force_mock) { if (!force_mock) { handler = autosleep_detect(); if (handler) return; handler = earlysuspend_detect(); if (handler) return; handler = legacy_detect(); if (handler) return; printf("No suspend interface detected, using mock suspend\n"); } handler = mocksuspend_detect(); } int libsuspend_prepare_suspend(void) { if (!handler) return -ENODEV; if (handler->prepare) return handler->prepare(); return 0; } int libsuspend_enter_suspend(void) { if (!handler) return -ENODEV; if (handler->enter) return handler->enter(); return 0; } int libsuspend_exit_suspend(void) { if (!handler) return -ENODEV; if (handler->exit) return handler->exit(); return 0; } int libsuspend_acquire_wake_lock(const char *name) { if (!handler) return -ENODEV; if (handler->acquire_wake_lock) return handler->acquire_wake_lock(name); return 0; } int libsuspend_release_wake_lock(const char *name) { if (!handler) return -ENODEV; if (handler->release_wake_lock) return handler->release_wake_lock(name); return 0; } powerd-0.14+14.04.20140415/libsuspend/sysfs.c0000644000015301777760000000252512323077401021022 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include #include int sysfs_file_exists(const char *path) { return !access(path, F_OK); } int sysfs_read(const char *path, void *buf, int len) { int fd; ssize_t ret; fd = open(path, O_RDONLY); if (fd == -1) return -errno; ret = read(fd, buf, len); if (ret == -1) ret = -errno; close(fd); return ret; } int sysfs_write(const char *path, const void *buf, int len) { int fd; ssize_t ret; fd = open(path, O_WRONLY); if (fd == -1) return -errno; ret = write(fd, buf, len); if (ret == -1) ret = -errno; close(fd); return ret; } powerd-0.14+14.04.20140415/CMakeLists.txt0000644000015301777760000000546012323077401020100 0ustar pbusernogroup00000000000000project(powerd) cmake_minimum_required(VERSION 2.8.9) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_MODULE_PATH}") if (NOT CMAKE_BUILD_TYPE) message(STATUS "No build type selected, default to Release") set(CMAKE_BUILD_TYPE "Release") endif() set(CMAKE_C_FLAGS "-Wall") set(CMAKE_CXX_FLAGS "-Wall") set(CMAKE_C_FLAGS_DEBUG "-O0 -ggdb -g") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -ggdb -g") set(CMAKE_C_FLAGS_RELEASE "-O3") set(CMAKE_CXX_FLAGS_RELEASE "-O3") set(GDBUS_NAME powerd-dbus) find_package(Threads REQUIRED) find_package(PkgConfig) pkg_check_modules(ANDROID_HEADERS android-headers) pkg_check_modules(HYBRIS_IS libis) pkg_check_modules(HYBRIS_SF libsf) pkg_check_modules(GLIB glib-2.0) pkg_check_modules(GIO gio-2.0) pkg_check_modules(GIO-UNIX gio-unix-2.0) pkg_check_modules(UPOWER_GLIB upower-glib) pkg_check_modules(UUID uuid) set(POWERD_GENERATED_SOURCES ${GDBUS_NAME}.c ) set(POWERD_GENERATED_HEADERS ${GDBUS_NAME}.h ) SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_BINARY_DIR}/src/${POWERD_GENERATED_SOURCES} PROPERTIES GENERATED 1) set(POWERD_DEVICE_CONFIGS_PATH "${CMAKE_INSTALL_PREFIX}/share/powerd/device_configs") FILE(GLOB device_config_files "${CMAKE_CURRENT_SOURCE_DIR}/data/device_configs/*.xml") add_definitions(-DPOWERD_DEVICE_CONFIGS_PATH=\"${POWERD_DEVICE_CONFIGS_PATH}\") set( SRCS src/powerd.cpp src/autobrightness.c src/backlight.c src/device-config.c src/display.c src/display-request.c src/log.c src/power-request.c src/power-source.c src/powerd-client.c src/powerd-object.c src/powerd-sensors.cpp src/spline.c src/stats.c src/util.c src/${GDBUS_NAME}.c ) link_directories( ${GLIB_LIBRARY_DIRS} ${GIO_LIBRARY_DIRS} ${GIO-UNIX_LIBRARY_DIRS} ${UPOWER_GLIB_LIBRARY_DIRS} ${UUID_LIBRARY_DIRS} ) include_directories( ${ANDROID_HEADERS_INCLUDE_DIRS} ${GLIB_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS} ${GIO-UNIX_INCLUDE_DIRS} ${UPOWER_GLIB_INCLUDE_DIRS} ${UUID_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/libsuspend ${CMAKE_CURRENT_BINARY_DIR}/src ) add_subdirectory(libsuspend) add_subdirectory(src) add_subdirectory(cli) include(UseGSettings) SET (POWERD_SCHEMAS "data/com.canonical.powerd.gschema.xml") add_schema(${POWERD_SCHEMAS}) add_executable( powerd ${SRCS} ) add_dependencies( powerd dbus_bindings ${POWERD_SCHEMAS} ) target_link_libraries( powerd suspend ${CMAKE_THREAD_LIBS_INIT} ${HYBRIS_IS_LIBRARIES} ${HYBRIS_SF_LIBRARIES} ${GLIB_LIBRARIES} ${GIO_LIBRARIES} ${GIO-UNIX_LIBRARIES} ${PHABLET_LIBRARIES} "-lubuntu_application_api" "-landroid-properties" "-lhardware" ${UPOWER_GLIB_LIBRARIES} ${UUID_LIBRARIES} ) install( TARGETS powerd RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib/static ) install( FILES ${device_config_files} DESTINATION ${POWERD_DEVICE_CONFIGS_PATH} ) powerd-0.14+14.04.20140415/COPYING0000644000015301777760000010451312323077401016372 0ustar pbusernogroup00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . powerd-0.14+14.04.20140415/src/0000755000015301777760000000000012323077721016127 5ustar pbusernogroup00000000000000powerd-0.14+14.04.20140415/src/powerd.cpp0000644000015301777760000005276312323077401020143 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * Authors: * Michael Frey: michael.frey@canonical.com * Matthew Fischer: matthew.fischer@canonical.com * Seth Forshee: seth.forshee@canonical.com * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "powerd-internal.h" #include "powerd-object.h" #include "powerd-dbus.h" #include "powerd-sensors.h" #include "device-config.h" #include "log.h" #include #include #include #include #include "libsuspend.h" #include static GThread *powerd_mainloop_thread; namespace { #define SHUTDOWN_HOLD_SECS 2 static uint button_timer = 0; /* * Do not modify activity_timer directly. Instead, call * reset_activity_timer(); */ static uint activity_timer = 0; GMutex activity_timer_mutex; static time_t curtime; static int lasttime; struct tm *tm; /* The real default for this is set in the gschema file, but set 60 here * as a sanity check */ static const int activity_timeout = 60; static const int dim_timeout = 45; static GMainLoop *main_loop = NULL; static guint name_id; enum state { BUTTON_DOWN, BUTTON_UP, SHUTDOWN }; static state button_state = BUTTON_UP; enum { SCREEN_STATE_OFF = 0, SCREEN_STATE_DIM, SCREEN_STATE_BRIGHT }; /* Assume screen starts off when powerd starts */ int activity_timer_screen_state = SCREEN_STATE_OFF; static int g_exit_code = 0; /* * Display request for activity timer * * Stupid C++ compiler won't let me use designated initializers ... */ static struct powerd_display_request activity_timer_req = { {0,}, /* cookie */ POWERD_DISPLAY_STATE_ON, /* state */ POWERD_DISPLAY_FLAG_BRIGHT /* flags */ }; /* Display request for use of proximity sensor during phone calls */ static struct powerd_display_request prox_sensor_req = { {0,}, /* cookie */ POWERD_DISPLAY_STATE_ON, /* state */ POWERD_DISPLAY_FLAG_USE_PROXIMITY /* flags */ }; /* Default display request in case running in emulator */ static struct powerd_display_request emulator_req = { {0,}, /* cookie */ POWERD_DISPLAY_STATE_ON, /* state */ POWERD_DISPLAY_FLAG_BRIGHT /* flags */ }; /* List with detected modems */ static GSList *g_modems = NULL; static void free_call_data(void *data) { struct call_data *call = (struct call_data *) data; g_free(call->obj_name); if (call->ofono_proxy) g_object_unref(call->ofono_proxy); } static gint call_data_cmp(gconstpointer a, gconstpointer b) { const struct call_data *call = (const struct call_data *) a; const char *call_name = (const char *) b; return strcmp(call->obj_name, call_name); } /* List with existing calls */ static GSList *g_calls = NULL; /* Flag set when proximity sensor is on */ static gboolean g_proximity_on = FALSE; gboolean activity_monitor(gpointer data); void update_screen_state(int state); gboolean call_shutdown(gpointer data) { if (button_state == BUTTON_DOWN) { button_state = SHUTDOWN; powerd_set_brightness(0); display_set_power_mode(0, "off"); powerd_shutdown(); } return FALSE; } gboolean activity_monitor(gpointer data) { int new_state; g_mutex_lock(&activity_timer_mutex); if (activity_timer_screen_state > SCREEN_STATE_OFF) { if (dim_timeout > 0 && activity_timeout > dim_timeout) new_state = activity_timer_screen_state - 1; else new_state = SCREEN_STATE_OFF; update_screen_state(new_state); if (new_state != SCREEN_STATE_OFF && activity_timer > 0) activity_timer = g_timeout_add_seconds(activity_timeout - dim_timeout, activity_monitor, NULL); } g_mutex_unlock(&activity_timer_mutex); return FALSE; } static struct power_module* _power_module; void on_new_event(Event* event, void* context) { switch(event->type) { case KEY_EVENT_TYPE: if (event->details.key.key_code == ISCL_KEYCODE_POWER && button_state != SHUTDOWN) { if (event->action == 1) { powerd_debug("power button pressed"); button_state = BUTTON_UP; curtime = time(0); tm = localtime (&curtime); if (tm->tm_sec - lasttime < SHUTDOWN_HOLD_SECS) { gboolean enabled = powerd_display_enabled(); if (enabled) powerd_display_set_override(POWERD_OVERRIDE_REASON_POWER_BUTTON); else powerd_display_clear_override(POWERD_OVERRIDE_REASON_POWER_BUTTON); powerd_reset_activity_timer(!enabled); } } else if (event->action == 0) { button_state = BUTTON_DOWN; curtime = time(0); tm = localtime (&curtime); lasttime = tm->tm_sec; if (button_timer > 0) { g_source_remove(button_timer); button_timer = 0; } button_timer = g_timeout_add_seconds(SHUTDOWN_HOLD_SECS, call_shutdown, NULL); } } break; default: if (powerd_display_enabled()) powerd_reset_activity_timer(1); break; } } static void sigterm_quit(int signal) { powerd_warn("SIGTERM recieved, cleaning up"); powerd_exit(0); } int update_screen_state_worker(gpointer data) { long new_state = (long)data; int ret; if (new_state == activity_timer_screen_state) return 0; if (new_state == SCREEN_STATE_OFF) { powerd_remove_display_request(activity_timer_req.cookie); } else { if (new_state == SCREEN_STATE_DIM) activity_timer_req.flags &= ~POWERD_DISPLAY_FLAG_BRIGHT; else activity_timer_req.flags |= POWERD_DISPLAY_FLAG_BRIGHT; if (activity_timer_screen_state == SCREEN_STATE_OFF) ret = powerd_add_display_request(&activity_timer_req, "activity-timer"); else ret = powerd_update_display_request(&activity_timer_req); if (ret) powerd_warn("Error adding display state request for activity timer"); } activity_timer_screen_state = new_state; return 0; } void update_screen_state(int new_state) { long data = new_state; if (new_state < SCREEN_STATE_OFF || new_state > SCREEN_STATE_BRIGHT) return; powerd_run_mainloop_sync(update_screen_state_worker, (gpointer)data); } } //namespace void powerd_hal_signal_activity(void) { powerd_warn("signalling activity via HAL"); if (_power_module && _power_module->powerHint) _power_module->powerHint(_power_module, POWER_HINT_INTERACTION, NULL); } void powerd_reset_activity_timer(int add) { int timeout; g_mutex_lock(&activity_timer_mutex); if (activity_timer > 0) { g_source_remove(activity_timer); activity_timer = 0; } if (add) { if (dim_timeout > 0 && dim_timeout < activity_timeout) timeout = dim_timeout; else timeout = activity_timeout; update_screen_state(SCREEN_STATE_BRIGHT); activity_timer = g_timeout_add_seconds(timeout, activity_monitor, NULL); } else { update_screen_state(SCREEN_STATE_OFF); } g_mutex_unlock(&activity_timer_mutex); } /* * Once dbus is ready, force the screen on to make the system state * match the powerd iternal state */ void powerd_dbus_init_complete(void) { powerd_reset_activity_timer(1); } static void watch_modem(const char *modem) { /* for incoming SMS signals */ g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, "org.ofono", modem, "org.ofono.MessageManager", NULL, (GAsyncReadyCallback)ofono_proxy_connect_cb, NULL); /* for incoming calls Added/Removed signals */ g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, "org.ofono", modem, "org.ofono.VoiceCallManager", NULL, (GAsyncReadyCallback)ofono_proxy_connect_cb, NULL); /* for USSD notifications/network requests */ g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, "org.ofono", modem, "org.ofono.SupplementaryServices", NULL, (GAsyncReadyCallback)ofono_proxy_connect_cb, NULL); } void ofono_get_modems_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) { GDBusProxy *client = G_DBUS_PROXY(source_object); GVariant *result; GVariant *item; GError *error = NULL; GVariantIter *iter; result = g_dbus_proxy_call_finish(client, res, &error); if (result == NULL) { powerd_warn("%s: call error", __func__); return; } g_variant_get(result, "(a(oa{sv}))", &iter); while ((item = g_variant_iter_next_value(iter))) { const char *obj_path; g_variant_get_child(item, 0, "&o", &obj_path); if (g_slist_find_custom(g_modems, obj_path, (GCompareFunc) strcmp) == NULL) { powerd_debug("active ofono modem %s", obj_path); g_modems = g_slist_prepend(g_modems, g_strdup(obj_path)); watch_modem(obj_path); } g_variant_unref(item); } g_variant_unref(result); } static void notify_proximity_sensor(const char *call_state) { /* * This function enables the proximity sensor in case the new state of the * call is: * * Active - meaning, the call is in progress --> Proximity On * Dialing - a signal made on outbound calls only which means that * we have started to talk to the network, but the call has not * been picked up. --> Proximity On (this signal is not * sent on inbound calls) * Note that the proximity sensor is not activated for "incoming" state * For more details on states see voicecall-api.txt in the ofono source */ if (g_proximity_on) return; if (strcmp("active", call_state) == 0 || strcmp("dialing", call_state) == 0) { if (powerd_add_display_request(&prox_sensor_req, "prox-sensor")) powerd_warn("Request to use proximity sensor failed"); g_proximity_on = TRUE; } } static void deactivate_proximity_sensor(void) { if (!g_proximity_on) return; powerd_remove_display_request(prox_sensor_req.cookie); g_proximity_on = FALSE; } void ofono_voicecall_get_props_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) { GDBusProxy *client = G_DBUS_PROXY(source_object); GVariant *result; GVariant *item; GError *error = NULL; GVariantIter *iter = NULL; result = g_dbus_proxy_call_finish(client, res, &error); if (result == NULL) { powerd_warn("%s: call error", __func__); return; } g_variant_get(result, "(a{sv})", &iter); while ((item = g_variant_iter_next_value(iter))) { GVariant *value = NULL; const char *prop_name = NULL; const char *call_state = NULL; g_variant_get(item, "{&sv}", &prop_name, &value); if (strcmp("State", prop_name) == 0) { g_variant_get(value, "&s", &call_state); notify_proximity_sensor(call_state); powerd_info("GetProperties. Call State = %s", call_state); } g_variant_unref(value); g_variant_unref(item); } g_variant_unref(result); } void on_ofono_manager_signal(GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data) { const gchar *object_path; GVariant *tmp; char *modem; GSList *node; tmp = g_variant_get_child_value(parameters, 0); object_path = g_variant_get_string(tmp, NULL); node = g_slist_find_custom(g_modems, object_path, (GCompareFunc) strcmp); if (strcmp("ModemAdded", signal_name) == 0) { /* Add if not already in list */ if (node == NULL) { powerd_debug("watching ofono modem %s", object_path); modem = g_strdup(object_path); g_modems = g_slist_prepend(g_modems, modem); watch_modem(modem); } } else if(strcmp("ModemRemoved", signal_name) == 0) { /* Remove if found in list */ if (node != NULL) { powerd_debug("stop watch on ofono modem %s", object_path); g_free(g_modems->data); g_modems = g_slist_delete_link(g_modems, node); } } g_variant_unref(tmp); } void on_ofono_voicecallmanager_signal (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data) { GVariant *tmp; if (!strcmp(signal_name, "CallAdded")) { struct call_data *call; powerd_display_clear_override(POWERD_OVERRIDE_REASON_POWER_BUTTON); powerd_reset_activity_timer(1); call = (struct call_data *) calloc(1, sizeof(*call)); tmp = g_variant_get_child_value(parameters, 0); call->obj_name = g_variant_dup_string(tmp, NULL); g_calls = g_slist_prepend(g_calls, call); powerd_info("%s incoming call", call->obj_name); /* Connect to voicecall interface */ g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, "org.ofono", call->obj_name, "org.ofono.VoiceCall", NULL, (GAsyncReadyCallback) ofono_voicecall_proxy_connect_cb, call); g_variant_unref(tmp); } else if (!strcmp(signal_name, "CallRemoved")) { const gchar *object_path; GSList *node; tmp = g_variant_get_child_value(parameters, 0); object_path = g_variant_get_string(tmp, NULL); node = g_slist_find_custom(g_calls, object_path, call_data_cmp); if (node != NULL) { free_call_data(node->data); g_calls = g_slist_delete_link(g_calls, node); powerd_info("%s call removed", object_path); } if (g_calls == NULL) deactivate_proximity_sensor(); g_variant_unref(tmp); } } void on_ofono_voicecall_signal (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data) { GVariant *value = NULL; const char *prop_name = NULL; const char *call_state = NULL; if (!strcmp(signal_name, "PropertyChanged")) { g_variant_get(parameters, "(&sv)", &prop_name, &value); if (!strcmp(prop_name, "State")) { g_variant_get(value, "&s", &call_state); powerd_info("Call State = %s", call_state); notify_proximity_sensor(call_state); } g_variant_unref(value); } } void on_ofono_messagemanager_signal (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data) { powerd_debug("we get signal from %s: %s", sender_name, signal_name); /* from org.ofono.MessageManager */ if ((!strcmp(signal_name, "IncomingMessage")) || (!strcmp(signal_name, "ImmediateMessage"))) { powerd_debug("Waking up the device - Incoming SMS"); powerd_display_clear_override(POWERD_OVERRIDE_REASON_POWER_BUTTON); powerd_reset_activity_timer(1); } } void on_ofono_ussd_signal(GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data) { powerd_debug("we get signal from %s: %s", sender_name, signal_name); /* from org.ofono.MessageManager */ if ((!strcmp(signal_name, "NotificationReceived")) || (!strcmp(signal_name, "RequestReceived"))) { powerd_debug("Waking up the device - Incoming USSD"); powerd_display_clear_override(POWERD_OVERRIDE_REASON_POWER_BUTTON); powerd_reset_activity_timer(1); } } /* Must be first to run on main loop */ static gboolean main_init(gpointer unused) { powerd_mainloop_thread = g_thread_self(); return FALSE; } /* Returns TRUE if current thread of execution is the default main loop */ int powerd_is_mainloop(void) { return (g_thread_self() == powerd_mainloop_thread); } /* If running in emulator, make sure we keep the display on and * block suspend (can easily corrupt the data, not properly supported) */ static gboolean check_emulator(gpointer unused) { char propbuf[PROP_VALUE_MAX]; static uuid_t emulator_request_cookie; if (property_get("ro.kernel.qemu", propbuf, NULL) > 0) { powerd_info("Running in emulator, forcing display, blocking suspend."); /* Request screen to be always on */ if (powerd_add_display_request(&emulator_req, "emulator-display")) powerd_warn("Request to force screen on for emulator failed"); /* Block suspend */ request_sys_state_internal("emulator-request", POWERD_SYS_STATE_ACTIVE, &emulator_request_cookie, NULL); } return FALSE; } void powerd_shutdown(void) { static char poweroff_cmd[] = "poweroff"; char *argv[] = {poweroff_cmd, NULL}; g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL); } void powerd_exit(int exit_code) { g_exit_code = exit_code; g_main_loop_quit(main_loop); } int main(int argc, char** argv) { powerd_log_init(); powerd_info("Initializing powerd."); name_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, "com.canonical.powerd", G_BUS_NAME_OWNER_FLAGS_REPLACE, powerd_bus_acquired_cb, powerd_name_acquired_cb, powerd_name_lost_cb, NULL, NULL); powerd_debug("owner id: %u", name_id); /* Listen to modem creation */ g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, "org.ofono", "/", "org.ofono.Manager", NULL, (GAsyncReadyCallback) ofono_manager_proxy_connect_cb, NULL); /* Init this first, data is used by other inits */ device_config_init(); libsuspend_init(0); powerd_stats_init(); powerd_client_init(); power_request_init(); display_request_init(); dbus_name_watch_init(); powerd_backlight_init(); powerd_autobrightness_init(); powerd_display_init(); powerd_sensors_init(); powerd_ps_init(); main_loop = g_main_loop_new (NULL, FALSE); signal(SIGTERM, sigterm_quit); AndroidEventListener listener; listener.on_new_event = on_new_event; listener.context = NULL; InputStackConfiguration config = { enable_touch_point_visualization : false, default_layer_for_touch_point_visualization : 10000, input_area_width : 2048, input_area_height : 2048 }; int err = hw_get_module(POWER_HARDWARE_MODULE_ID, (hw_module_t const**)&_power_module); if (!err) _power_module->init(_power_module); /* Compat input is needed otherwise there's no way to know when * the user is actively using the system */ if (!android_input_check_availability()) { powerd_error("Android compat input library not found, aborting"); g_exit_code = -1; goto fail_compat_init; } android_input_stack_initialize(&listener, &config); android_input_stack_start(); /* Config use should be done during init, okay to free now */ device_config_deinit(); /* This needs to be the first thing to run on the main loop */ g_idle_add_full(G_PRIORITY_HIGH, main_init, NULL, NULL); g_idle_add(check_emulator, NULL); g_main_loop_run(main_loop); g_bus_unown_name(name_id); g_main_loop_unref(main_loop); android_input_stack_stop(); android_input_stack_shutdown(); fail_compat_init: powerd_ps_deinit(); dbus_name_watch_deinit(); powerd_autobrightness_deinit(); powerd_backlight_deinit(); display_request_deinit(); power_request_deinit(); powerd_client_deinit(); powerd_stats_deinit(); g_slist_free_full(g_modems, g_free); g_slist_free_full(g_calls, free_call_data); return g_exit_code; } powerd-0.14+14.04.20140415/src/util.c0000644000015301777760000000567212323077401017255 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include "powerd-internal.h" struct sync_work_data { GCond cond; GMutex mutex; gboolean done; int (*func)(gpointer); gpointer data; int return_val; }; static gboolean run_mainloop_sync_worker(gpointer data) { struct sync_work_data *work_data = data; g_mutex_lock(&work_data->mutex); work_data->return_val = work_data->func(work_data->data); work_data->done = TRUE; g_cond_signal(&work_data->cond); g_mutex_unlock(&work_data->mutex); return FALSE; } static int __powerd_run_mainloop_sync(int (*func)(gpointer), gpointer data) { struct sync_work_data work_data; g_cond_init(&work_data.cond); g_mutex_init(&work_data.mutex); work_data.done = FALSE; work_data.func = func; work_data.data = data; g_mutex_lock(&work_data.mutex); g_timeout_add(0, run_mainloop_sync_worker, &work_data); while (!work_data.done) g_cond_wait(&work_data.cond, &work_data.mutex); g_mutex_unlock(&work_data.mutex); g_mutex_clear(&work_data.mutex); g_cond_clear(&work_data.cond); return work_data.return_val; } /* * Run the supplied function on the main loop, passing it the * supplied data, and wait for it to complete. If currently * executing on the main loop then the function is simply called * inline. */ int powerd_run_mainloop_sync(int (*func)(gpointer), gpointer data) { if (powerd_is_mainloop()) return func(data); return __powerd_run_mainloop_sync(func, data); } /* * Calculate a hash from a UUID. For use with GHashTables. */ guint powerd_uuid_hash(gconstpointer key) { guint *data = (guint *)key; guint hash, i; /* * Treat the UUID as an array of guints and do a simple xor * hash. uuid_t is an even multiple of the size of guint for * 32- or 64-bit integers, but even if it weren't this * implementation would just leave off the trailing bytes and * still produce a reasonable hash. */ hash = data[0]; for (i = 1; i < sizeof(uuid_t) / sizeof(hash); i++) hash ^= data[i]; return hash; } /* * Compare two UUIDs and return TRUE if equal and FALSE * otherwise. For use with GHashTables. */ gboolean powerd_uuid_equal(gconstpointer a, gconstpointer b) { return !memcmp(a, b, sizeof(uuid_t)); } powerd-0.14+14.04.20140415/src/powerd-sensors.h0000644000015301777760000000163712323077401021274 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * Authors: * Ricardo Mendoza: ricardo.mendoza@canonical.com * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #ifndef __POWERD_SENSORS_H__ #define __POWERD_SENSORS_H__ void powerd_sensors_init(void); void powerd_sensors_proximity_enable(void); void powerd_sensors_proximity_disable(void); #endif /* __POWERD_SENSORS_H */ powerd-0.14+14.04.20140415/src/display.c0000644000015301777760000003052512323077410017740 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * Authors: * Michael Frey: michael.frey@canonical.com * Matthew Fischer: matthew.fischer@canonical.com * Seth Forshee: seth.forshee@canonical.com * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include #include #include #include #include #include #include "powerd.h" #include "powerd-internal.h" #include "log.h" enum bl_state { BL_OFF, BL_DIM, BL_BRIGHT, BL_AUTO, NUM_BL_STATES }; static enum bl_state target_bl_state = BL_BRIGHT; /* Treat "don't care" display state as off */ #define DISPLAY_STATE_OFF POWERD_DISPLAY_STATE_DONT_CARE /* Autobrightness only enabled when bright && !disabled */ #define AB_ENABLED_MASK (POWERD_DISPLAY_FLAG_BRIGHT | \ POWERD_DISPLAY_FLAG_DISABLE_AUTOBRIGHTNESS) #define AB_ENABLED POWERD_DISPLAY_FLAG_BRIGHT static int user_brightness; static gboolean user_ab_enabled; /* Assume screen is off to start; we will force it on */ static uuid_t internal_request_cookie; /* Currently requested state of the display */ struct powerd_display_request internal_state = { .state = DISPLAY_STATE_OFF, .flags = 0, }; /* * When using the proximity sensor, our actual state of the screen * may not match the requested state in internal_state. Therefore * this variable keeps track of the actual state of the screen. */ enum powerd_display_state actual_screen_state = DISPLAY_STATE_OFF; /* * Screen state overrides. These are used when powerd needs to force * the screen off regardless of the requested display state. */ static unsigned screen_off_overrides; static const char *display_state_strs[POWERD_NUM_DISPLAY_STATES] = { [POWERD_DISPLAY_STATE_DONT_CARE] = "off", [POWERD_DISPLAY_STATE_ON] = "on", }; static const char *display_state_to_str(enum powerd_display_state state) { if (state >= POWERD_NUM_DISPLAY_STATES) return "invalid"; return display_state_strs[state]; } static gboolean ab_enabled(guint32 flags) { if (!powerd_autobrightness_available()) return FALSE; if (!user_ab_enabled) return FALSE; return (flags & AB_ENABLED_MASK) == AB_ENABLED; } static void set_backlight(enum bl_state state) { static enum bl_state current_state = BL_BRIGHT; static int restore_brightness = 0; if (state >= NUM_BL_STATES) { powerd_warn("Unknown backlight state %d\n", state); return; } if (state != BL_AUTO) powerd_autobrightness_disable(); /* * When we're enabling autobrightness it takes a second to * settle on a brightness level after enabling. This delay * becomes very noticible when going from the dim to bright * state. To avoid this lag, save off the current brightness * any time the state goes from BRIGHT or AUTO and restore * it if transitioning back to AUTO. */ if (current_state >= BL_BRIGHT && state < BL_BRIGHT) restore_brightness = powerd_get_brightness(); switch (state) { case BL_OFF: powerd_set_brightness(0); break; case BL_DIM: powerd_dim_screen(); break; case BL_BRIGHT: powerd_set_user_brightness(user_brightness); break; case BL_AUTO: if (current_state < BL_BRIGHT && restore_brightness > 0) powerd_set_brightness(restore_brightness); powerd_autobrightness_enable(); break; default: /* default case is to satisfy static analysis tools, should * never actually get here */ powerd_error("Unknwown backlight state %d\n", state); return; } current_state = state; } gboolean display_set_power_mode(int display, const char *power_mode) { GError *error = NULL; GDBusProxy *unity_proxy = NULL; powerd_debug("display_set_power_mode(%s)", power_mode); if (strcmp(power_mode, "on") == 0) powerd_hal_signal_activity(); unity_proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, "com.canonical.Unity.Screen", "/com/canonical/Unity/Screen", "com.canonical.Unity.Screen", NULL, &error); if (error != NULL) { powerd_warn("could not connect to Unity: %s", error->message); g_error_free(error); return FALSE; } GVariant *ret = g_dbus_proxy_call_sync(unity_proxy, "setScreenPowerMode", g_variant_new("(s)", power_mode), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (ret == NULL) { powerd_warn("screen power setting failed: %s", error->message); g_error_free(error); g_object_unref(unity_proxy); return FALSE; } g_object_unref(unity_proxy); return TRUE; } static void turn_on_display(void) { powerd_debug("turning on display"); display_set_power_mode(0, "on"); set_backlight(target_bl_state); } gboolean powerd_display_enabled(void) { return actual_screen_state == POWERD_DISPLAY_STATE_ON; } static void update_display_state(struct powerd_display_request *req) { enum powerd_display_state state = req->state; int applied_state; int ret; applied_state = screen_off_overrides ? DISPLAY_STATE_OFF : state; switch (applied_state) { case DISPLAY_STATE_OFF: if (actual_screen_state == DISPLAY_STATE_OFF) { /* Nothing to do */ return; } powerd_debug("turning off display"); set_backlight(BL_OFF); if (!display_set_power_mode(0, "off")) { powerd_warn("failed to set display power mode, not clearing state"); return; } powerd_debug("Releasing internal active state request"); ret = clear_sys_state_internal(internal_request_cookie); if (!ret) { char cookie_str[UUID_STR_LEN]; uuid_unparse(internal_request_cookie, cookie_str); powerd_warn("Internal system state request cookie invalid: %s", cookie_str); } break; case POWERD_DISPLAY_STATE_ON: if (ab_enabled(req->flags)) target_bl_state = BL_AUTO; else if (req->flags & POWERD_DISPLAY_FLAG_BRIGHT) target_bl_state = BL_BRIGHT; else target_bl_state = BL_DIM; if (actual_screen_state != POWERD_DISPLAY_STATE_ON) { powerd_debug("Requesting active state internally"); ret = request_sys_state_internal("display-request", POWERD_SYS_STATE_ACTIVE, &internal_request_cookie, NULL); if (!ret) powerd_warn("Request for active state failed"); /* * If currently in the suspend state we need to wait for * notification of entering the active state, otherwise the * screen may fail to turn on for devices using earlysuspend. * Otherwise we can turn on the screen right away. */ if (!powerd_suspend_active()) turn_on_display(); } else { /* Only updating backlight state */ set_backlight(target_bl_state); } break; default: powerd_warn("Invalid display state %d", applied_state); return; } actual_screen_state = applied_state; powerd_display_state_signal_emit(actual_screen_state, req->flags); } static void update_flags(guint32 flags) { int prox_enabled, internal_prox_enabled; prox_enabled = (flags & POWERD_DISPLAY_FLAG_USE_PROXIMITY); internal_prox_enabled = (internal_state.flags & POWERD_DISPLAY_FLAG_USE_PROXIMITY); if (prox_enabled != internal_prox_enabled) { if (prox_enabled) { powerd_sensors_proximity_enable(); } else { powerd_sensors_proximity_disable(); powerd_display_clear_override(POWERD_OVERRIDE_REASON_PROXIMITY); } } } /* * *** WARNING *** * * This should now only be called by display request code. Everyone * else must use powerd_{add,remove}_display_request()!!! */ void powerd_set_display_state(struct powerd_display_request *req) { powerd_debug("powerd_set_display_state: %s -> %s, 0x%x -> 0x%x", display_state_to_str(internal_state.state), display_state_to_str(req->state), internal_state.flags, req->flags); /* Update flags before display state to ensure proximity flag is * taken into account */ update_flags(req->flags); update_display_state(req); internal_state = *req; } static void user_ab_enable(gboolean enable) { user_ab_enabled = enable; update_display_state(&internal_state); } static gboolean prox_update_worker(gpointer data) { unsigned long near = (unsigned long)data; if (near) powerd_display_set_override(POWERD_OVERRIDE_REASON_PROXIMITY); else powerd_display_clear_override(POWERD_OVERRIDE_REASON_PROXIMITY); return FALSE; } void powerd_proximity_event(gboolean near) { unsigned long data = (unsigned long)near; g_timeout_add(0, prox_update_worker, (gpointer)data); } struct apply_override_data { enum powerd_override_reason reason; int set; }; static int apply_override_worker(gpointer data) { struct apply_override_data *ovr_data = data; unsigned orig_overrides = screen_off_overrides; unsigned mask; mask = 1 << ovr_data->reason; if (ovr_data->set) screen_off_overrides |= mask; else screen_off_overrides &= ~mask; if (orig_overrides != screen_off_overrides) update_display_state(&internal_state); return 0; } void powerd_display_set_override(enum powerd_override_reason reason) { struct apply_override_data ovr_data; if (reason >= POWERD_NUM_OVERRIDE_REASONS) { powerd_debug("Refusing to set invalid override reason (%d)", reason); return; } ovr_data.reason = reason; ovr_data.set = 1; powerd_run_mainloop_sync(apply_override_worker, &ovr_data); } void powerd_display_clear_override(enum powerd_override_reason reason) { struct apply_override_data ovr_data; if (reason >= POWERD_NUM_OVERRIDE_REASONS) { powerd_debug("Refusing to clear invalid override reason (%d)", reason); return; } ovr_data.reason = reason; ovr_data.set = 0; powerd_run_mainloop_sync(apply_override_worker, &ovr_data); } static void set_user_brightness(int brightness) { int max = powerd_get_max_brightness(); if (brightness > max) brightness = max; user_brightness = brightness; update_display_state(&internal_state); } static gboolean exit_suspend_worker(gpointer unused) { /* Make sure suspend is still disabled */ if (!powerd_suspend_active() && powerd_display_enabled()) turn_on_display(); return FALSE; } void powerd_display_exit_suspend(void) { g_timeout_add(0, exit_suspend_worker, NULL); } int powerd_display_init(void) { struct stat stats; /* Use the current brightness until we're told another value */ user_brightness = powerd_get_brightness(); return 0; } /** dbus interfaces **/ gboolean handle_user_autobrightness_enable(PowerdSource *obj, GDBusMethodInvocation *invocation, gboolean enable) { user_ab_enable(enable); g_dbus_method_invocation_return_value(invocation, NULL); return TRUE; } gboolean handle_set_user_brightness(PowerdSource *obj, GDBusMethodInvocation *invocation, gint brightness) { set_user_brightness(brightness); g_dbus_method_invocation_return_value(invocation, NULL); return TRUE; } powerd-0.14+14.04.20140415/src/powerd-internal.h0000644000015301777760000002233312323077410021410 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #ifndef __POWERD_INTERNAL_H__ #define __POWERD_INTERNAL_H__ #include #include #include #include "powerd-object.h" #include "powerd.h" #ifdef __cplusplus extern "C" { #endif /* Length of a UUID string, including trailing nul */ #define UUID_STR_LEN 37 struct powerd_display_request { uuid_t cookie; enum powerd_display_state state; guint32 flags; }; enum powerd_override_reason { POWERD_OVERRIDE_REASON_POWER_BUTTON, POWERD_OVERRIDE_REASON_PROXIMITY, POWERD_NUM_OVERRIDE_REASONS }; struct DbusNameWatch { uint watch_id; uint ref_count; }; /* Used to track call state */ struct call_data { char *obj_name; GDBusProxy *ofono_proxy; }; void powerd_shutdown(void); void powerd_exit(int exit_code); void powerd_hal_signal_activity(void); void powerd_reset_activity_timer(int add); void powerd_dbus_init_complete(void); int powerd_is_mainloop(void); /* Autobrightness functions */ void powerd_new_als_event(double lux); void powerd_autobrightness_enable(void); void powerd_autobrightness_disable(void); gboolean powerd_autobrightness_available(void); int powerd_autobrightness_init(void); void powerd_autobrightness_deinit(void); /* Backlight functions */ int powerd_backlight_init(void); void powerd_backlight_deinit(void); int powerd_get_brightness(void); int powerd_get_max_brightness(void); int powerd_set_brightness(int brightness); int powerd_set_user_brightness(int brightness); void powerd_dim_screen(void); gboolean handle_get_brightness_params(PowerdSource *obj, GDBusMethodInvocation *invocation); /* Display functions */ void powerd_brightness_set_value(gint value); gboolean powerd_display_enabled(void); gboolean display_set_power_mode(int display, const char *powerd_mode); void powerd_set_display_state(struct powerd_display_request *req); int powerd_display_init(void); void powerd_proximity_event(gboolean near); void powerd_display_set_override(enum powerd_override_reason reason); void powerd_display_clear_override(enum powerd_override_reason reason); gboolean handle_user_autobrightness_enable(PowerdSource *obj, GDBusMethodInvocation *invocation, gboolean enable); gboolean handle_set_user_brightness(PowerdSource *obj, GDBusMethodInvocation *invocation, gint brightness); /* Display request functions */ int powerd_add_display_request(struct powerd_display_request *request, const char *name); int powerd_update_display_request(struct powerd_display_request *request); int powerd_remove_display_request(uuid_t cookie); void display_request_init(void); void display_request_deinit(void); gboolean handle_add_display_request(PowerdSource *obj, GDBusMethodInvocation *invocation, const char *name, int state, guint32 flags); gboolean handle_update_display_request(PowerdSource *obj, GDBusMethodInvocation *invocation, const gchar *ext_cookie, int state, guint32 flags); gboolean handle_clear_display_request(PowerdSource *obj, GDBusMethodInvocation *invocation, const gchar *ext_cookie); gboolean handle_list_display_requests(PowerdSource *obj, GDBusMethodInvocation *invocation); void clear_disp_state_by_owner(const char *owner); void powerd_display_exit_suspend(void); /* System power state requests */ gboolean handle_list_sys_requests(PowerdSource *obj, GDBusMethodInvocation *invocation); gboolean handle_request_sys_state(PowerdSource *obj, GDBusMethodInvocation *invocation, const char *name, int state); gboolean handle_clear_sys_state(PowerdSource *obj, GDBusMethodInvocation *invocation, gchar *cookie); gboolean request_sys_state_internal(const char *name, int state, uuid_t *cookie, const char *owner); gboolean clear_sys_state_internal(uuid_t cookie); void power_request_transition_acked(void); void update_system_state(void); void power_request_init(void); void power_request_deinit(void); enum SysPowerStates current_system_power_state(void); const gchar * state_to_string(int state); void powerd_sys_state_signal_emit(enum SysPowerStates state); void powerd_display_state_signal_emit(enum powerd_display_state state, guint32 flags); void clear_sys_state_by_owner(const char *owner); gboolean powerd_suspend_active(void); /* dbus name watch functions */ void dbus_name_watch_init(void); void dbus_name_watch_deinit(void); void powerd_dbus_name_watch_add(const char *owner); void powerd_dbus_name_watch_remove(const char *owner); /* dbus callbacks */ void powerd_bus_acquired_cb(GDBusConnection *connection, const gchar *name, gpointer user_data); void powerd_name_acquired_cb(GDBusConnection *connection, const gchar *name, gpointer user_data); void powerd_name_lost_cb(GDBusConnection *connection, const gchar *name, gpointer user_data); void powerd_name_vanished_cb(GDBusConnection *connection, const gchar *name, gpointer user_data); void ofono_get_modems_cb(GObject *source_object, GAsyncResult *res, gpointer user_data); void ofono_voicecall_get_props_cb(GObject *source_object, GAsyncResult *res, gpointer user_data); /* dbus signal handlers */ void on_ofono_manager_signal(GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data); void on_ofono_voicecall_signal(GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data); void on_ofono_voicecallmanager_signal(GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data); void on_ofono_messagemanager_signal(GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data); void on_ofono_ussd_signal(GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data); /* dbus proxy async setup */ void ofono_manager_proxy_connect_cb(GObject *source_object, GAsyncResult *res, gpointer user_data); void ofono_voicecall_proxy_connect_cb(GObject *source_object, GAsyncResult *res, gpointer user_data); void ofono_proxy_connect_cb(GObject *source_object, GAsyncResult *res, gpointer user_data); /* Sensor functions */ void powerd_sensors_proximity_enable(void); void powerd_sensors_proximity_disable(void); void powerd_sensors_als_enable(void); void powerd_sensors_als_disable(void); /* Power source functions */ int powerd_ps_init(void); void powerd_ps_deinit(void); /* Client functions */ int powerd_client_register(const char *dbus_name, const char *name); void powerd_client_unregister(const char *dbus_name); gboolean powerd_client_transition_start(int state); void powerd_client_transition_finish(int state); int powerd_client_ack(const char *dbus_name, int state); int powerd_client_init(void); void powerd_client_deinit(void); /* Statistics functions */ void powerd_account_request_sys_state(const char *dbus_name, const char *name); void powerd_account_clear_sys_state(const char *dbus_name, const char *name); void powerd_account_add_display_req(const char *dbus_name, const char *name, const struct powerd_display_request *req); void powerd_account_update_display_req(const char *dbus_name, const char *name, const struct powerd_display_request *req); void powerd_account_clear_display_req(const char *dbus_name, const char *name); void powerd_log_stats(void); int powerd_stats_init(void); void powerd_stats_deinit(void); gboolean handle_get_sys_request_stats(PowerdSource *obj, GDBusMethodInvocation *invocation); gboolean handle_get_disp_request_stats(PowerdSource *obj, GDBusMethodInvocation *invocation); /* Utility functions */ int powerd_run_mainloop_sync(int (*func)(gpointer), gpointer data); guint powerd_uuid_hash(gconstpointer key); gboolean powerd_uuid_equal(gconstpointer a, gconstpointer b); #ifdef __cplusplus } #endif #endif powerd-0.14+14.04.20140415/src/device-config.h0000644000015301777760000000164112323077401020777 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #ifndef __DEVICE_CONFIG_H__ #define __DEVICE_CONFIG_H__ #ifdef __cplusplus extern "C" { #endif void device_config_init(void); void device_config_deinit(void); int device_config_get(const char *name, GValue *value); #ifdef __cplusplus } #endif #endif /* __DEVICE_CONFIG_H__ */ powerd-0.14+14.04.20140415/src/powerd-object.h0000644000015301777760000000343612323077401021045 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #ifndef __POWERD_OBJECT_H__ #define __POWERD_OBJECT_H__ #include #ifdef __cplusplus extern "C" { #endif G_BEGIN_DECLS #define POWERD_TYPE_SOURCE (powerd_source_get_type ()) #define POWERD_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), POWERD_TYPE_SOURCE, PowerdSource)) #define POWERD_IS_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), POWERD_TYPE_SOURCE)) #define POWERD_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), POWERD_TYPE_SOURCE, PowerdSourceClass)) #define POWERD_IS_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), POWERD_TYPE_SOURCE)) #define POWERD_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), POWERD_TYPE_SOURCE, PowerdSourceClass)) struct _PowerdSourcePrivate; typedef struct _PowerdSource PowerdSource; typedef struct _PowerdSourceClass PowerdSourceClass; typedef struct _PowerdSourcePrivate PowerdSourcePrivate; struct _PowerdSourceClass { GObjectClass parent_class; }; struct _PowerdSource { GObject parent; PowerdSourcePrivate * priv; }; GType powerd_source_get_type (void); G_END_DECLS #ifdef __cplusplus } #endif #endif powerd-0.14+14.04.20140415/src/display-request.c0000644000015301777760000003304112323077410021422 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * Authors: * Michael Frey: michael.frey@canonical.com * Matthew Fischer: matthew.fischer@canonical.com * Seth Forshee: seth.forshee@canonical.com * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include #include #include #include "powerd.h" #include "powerd-internal.h" #include "log.h" struct display_request_internal { struct powerd_display_request req; const char *name; const char *owner; }; static struct { unsigned int state[POWERD_NUM_DISPLAY_STATES]; unsigned int flags[POWERD_NUM_DISPLAY_FLAGS]; } display_state_count; static struct powerd_display_request internal_state = { .state = POWERD_DISPLAY_STATE_DONT_CARE, .flags = 0, }; static GHashTable *display_request_hash; static void update_internal_state(void) { struct powerd_display_request new_state; int i; memset(&new_state, 0, sizeof(new_state)); /* Default to off if no request for display to be on */ if (display_state_count.state[POWERD_DISPLAY_STATE_ON] > 0) new_state.state = POWERD_DISPLAY_STATE_ON; else new_state.state = POWERD_DISPLAY_STATE_DONT_CARE; for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++) { if (display_state_count.flags[i] > 0) new_state.flags |= (1U << i); } if (!memcmp(&internal_state, &new_state, sizeof(new_state))) { powerd_debug("display state not changed"); return; } powerd_debug("Internal state updated: state %d flags 0x%08x", new_state.state, new_state.flags); internal_state = new_state; powerd_set_display_state(&internal_state); } static void __add_request(struct powerd_display_request *req) { unsigned int flags; int i; display_state_count.state[req->state]++; flags = req->flags; for (i = 0; flags && i < POWERD_NUM_DISPLAY_FLAGS; flags >>= 1, i++) { if (flags & 1) display_state_count.flags[i]++; } } static void __remove_request(struct powerd_display_request *req) { unsigned int flags; int i; if (display_state_count.state[req->state] > 0) display_state_count.state[req->state]--; flags = req->flags; for (i = 0; flags && i < POWERD_NUM_DISPLAY_FLAGS; flags >>= 1, i++) { if ((flags & 1) && display_state_count.flags[i] > 0) display_state_count.flags[i]--; } } static void add_request(struct display_request_internal *request) { g_hash_table_insert(display_request_hash, request->req.cookie, request); powerd_dbus_name_watch_add(request->owner); __add_request(&request->req); update_internal_state(); powerd_account_add_display_req(request->owner, request->name, &request->req); } static gboolean update_request(struct powerd_display_request *new_req) { struct display_request_internal *ireq; ireq = g_hash_table_lookup(display_request_hash, new_req->cookie); if (!ireq) { powerd_debug("Display request to update not found"); return FALSE; } __remove_request(&ireq->req); __add_request(new_req); ireq->req.state = new_req->state; ireq->req.flags = new_req->flags; update_internal_state(); powerd_account_update_display_req(ireq->owner, ireq->name, &ireq->req); return TRUE; } static gboolean remove_request(uuid_t cookie) { struct display_request_internal *ireq; struct powerd_display_request req; gboolean found = FALSE; /* * This involves two lookups into the hash, one to find the * request so we can retrieve the state and another to remove * it. GHashTable doesn't seem to provide any more efficient * way to do this; too bad g_hash_table_steal() doesn't return * a pointer to the data. */ ireq = g_hash_table_lookup(display_request_hash, cookie); if (ireq) { req = ireq->req; /* We need to remove it from our watch hash and do accounting * before we remove it from the state hash or the ireq->owner * memory will be freed before we try to use it. */ powerd_dbus_name_watch_remove(ireq->owner); powerd_account_clear_display_req(ireq->owner, ireq->name); found = g_hash_table_remove(display_request_hash, cookie); if (!found) powerd_warn("Display request found on lookup but not on remove"); } if (found) { __remove_request(&req); update_internal_state(); } return found; } static gboolean request_valid(struct powerd_display_request *request) { if ((unsigned)request->state >= POWERD_NUM_DISPLAY_STATES) { powerd_warn("Invalid display state requested: %d", request->state); return FALSE; } /* * XXX: This will warn if we get up to 32 flags, but in that * case the check should just be removed. */ if (request->flags & (-1U << POWERD_NUM_DISPLAY_FLAGS)) { powerd_warn("Invalid display flags requested: 0x%08x", request->flags); return FALSE; } return TRUE; } static int add_request_worker(gpointer data) { struct display_request_internal *req = data; add_request(req); return 0; } /* * @request need not refer to persistent memory, as the data from * the struct will be copied into internally-managed storage. */ static int __powerd_add_display_request(struct powerd_display_request *request, const char *name, const char *owner) { struct display_request_internal *hash_req; if (!request_valid(request)) return -EINVAL; uuid_generate(request->cookie); hash_req = g_new(struct display_request_internal, 1); hash_req->req = *request; hash_req->name = g_strdup(name); hash_req->owner = g_strdup(owner); powerd_run_mainloop_sync(add_request_worker, hash_req); return 0; } /* * @request need not refer to persistent memory, as the data from * the struct will be copied into internally-managed storage. */ int powerd_add_display_request(struct powerd_display_request *request, const char *name) { if (!request || !name) { powerd_warn("powerd_add_display_request() called with invalid args"); return -EINVAL; } return __powerd_add_display_request(request, name, "internal"); } static int update_request_worker(gpointer data) { struct powerd_display_request *req = data; return update_request(req); } int powerd_update_display_request(struct powerd_display_request *request) { gboolean found; if (!request_valid(request)) return -EINVAL; found = powerd_run_mainloop_sync(update_request_worker, request); return found ? 0 : -EINVAL; } static int remove_request_worker(gpointer data) { unsigned char *uuid = data; return remove_request(uuid); } int powerd_remove_display_request(uuid_t cookie) { gboolean found; found = powerd_run_mainloop_sync(remove_request_worker, cookie); return found ? 0 : -EINVAL; } /** dbus method handling **/ gboolean handle_add_display_request(PowerdSource *obj, GDBusMethodInvocation *invocation, const char *name, int state, guint32 flags) { struct powerd_display_request req; const char *owner; char ext_cookie[UUID_STR_LEN]; int ret; memset(&req, 0, sizeof(req)); owner = g_dbus_method_invocation_get_sender(invocation); powerd_debug("%s from %s: state %d flags %#08x", __func__, owner, state, flags); req.state = state; req.flags = flags; ret = __powerd_add_display_request(&req, name, owner); if (ret) { g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid display request"); } else { powerd_debug("%s: SUCCESS", __func__); uuid_unparse(req.cookie, ext_cookie); g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", ext_cookie)); } return TRUE; } gboolean handle_update_display_request(PowerdSource *obj, GDBusMethodInvocation *invocation, const gchar *ext_cookie, int state, guint32 flags) { struct powerd_display_request req; uuid_t cookie; int ret; memset(&req, 0, sizeof(req)); powerd_debug("%s from %s: cookie: %s state %d flags %#08x", __func__, g_dbus_method_invocation_get_sender(invocation), ext_cookie, state, flags); if (uuid_parse(ext_cookie, cookie)) { g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid cookie: %s", ext_cookie); return TRUE; } memcpy(req.cookie, cookie, sizeof(uuid_t)); req.state = state; req.flags = flags; ret = powerd_update_display_request(&req); if (ret) { g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid display request"); } else { powerd_debug("%s: SUCCESS", __func__); g_dbus_method_invocation_return_value(invocation, NULL); } return TRUE; } gboolean handle_clear_display_request(PowerdSource *obj, GDBusMethodInvocation *invocation, const gchar *ext_cookie) { int ret; uuid_t cookie; powerd_debug("%s from %s, cookie: %s", __func__, g_dbus_method_invocation_get_sender(invocation), ext_cookie); if (uuid_parse(ext_cookie, cookie)) { g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid cookie: %s", ext_cookie); return TRUE; } ret = powerd_remove_display_request(cookie); if (ret) { g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid cookie: %s", ext_cookie); } else { g_dbus_method_invocation_return_value(invocation, NULL); } return TRUE; } static int build_display_request_list(GVariantBuilder *builder) { GHashTableIter iter; gpointer key, value; int count = 0; g_hash_table_iter_init(&iter, display_request_hash); while (g_hash_table_iter_next(&iter, &key, &value)) { struct display_request_internal *ireq = value; struct powerd_display_request *req = &ireq->req; g_variant_builder_add(builder, "(ssiu)", ireq->name, ireq->owner, req->state, req->flags); count++; } return count; } gboolean handle_list_display_requests(PowerdSource *obj, GDBusMethodInvocation *invocation) { GVariant *list, *tuple; GVariantBuilder *builder; int count; builder = g_variant_builder_new(G_VARIANT_TYPE("a(ssiu)")); count = build_display_request_list(builder); if (count > 0) { list = g_variant_builder_end(builder); } else { g_variant_builder_clear(builder); list = g_variant_new_array(G_VARIANT_TYPE("(ssiu)"), NULL, 0); } tuple = g_variant_new_tuple(&list, 1); g_dbus_method_invocation_return_value(invocation, tuple); g_variant_builder_unref(builder); return TRUE; } /* Destructor for hash table */ static void display_request_destroy(gpointer data) { struct display_request_internal *req = data; g_free((gpointer)req->name); g_free((gpointer)req->owner); g_free(req); } void display_request_init(void) { display_request_hash = g_hash_table_new_full(powerd_uuid_hash, powerd_uuid_equal, NULL, display_request_destroy); } void display_request_deinit(void) { g_hash_table_destroy(display_request_hash); } /* * Callers may use this to clear all requests that the specified owner * is holding. This is used when the owner drops off of dbus */ void clear_disp_state_by_owner(const char *owner) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, display_request_hash); while (g_hash_table_iter_next(&iter, &key, &value)) { struct display_request_internal *ireq = value; if (!strcmp(owner, ireq->owner)) { powerd_account_clear_display_req(ireq->owner, ireq->name); __remove_request(&ireq->req); powerd_dbus_name_watch_remove(ireq->owner); g_hash_table_iter_remove(&iter); } } update_internal_state(); } powerd-0.14+14.04.20140415/src/power-request.c0000644000015301777760000005053012323077401021113 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include "powerd-internal.h" #include "powerd-dbus.h" #include "log.h" #include "libsuspend.h" #define NOTIFICATION_TIMEOUT_MSECS 2000 struct SysStateRequest { const char *name; const char *owner; uuid_t cookie; enum SysPowerStates state; }; /* * Current system power state. Assume we are suspended to start. We will * be forced into the active state during initialization, and this * ensures that the system state is consistent with our internal state. */ static enum SysPowerStates current_system_state = POWERD_SYS_STATE_SUSPEND; /* * System state requests are stored in a hash. This doesn't allow for * quickly finding requests corresponding to a given state, so a * per-state count of active requests is also maintained. */ static GHashTable *state_request_hash; static gint state_request_count[POWERD_NUM_POWER_STATES - 1]; /* * States for "state transition" state machine. * * When no state transition is in progress we're in the idle state. When * a state transition is initiated we proceed through the other states * in order. When finished, we start at the begining if another state * transition is pending or return to idle otherwise. */ enum state_transition_state { STATE_TRANSITION_IDLE, /* No transition in progress */ STATE_TRANSITION_DEQUEUE, /* Dequeueing next state transition */ STATE_TRANSITION_NOTIFY, /* Notifying clients / waiting for acks */ STATE_TRANSITION_COMPLETE, /* Completing the state transition */ NUM_STATE_TRANSITIONS }; static enum state_transition_state cur_state_transition_state = STATE_TRANSITION_IDLE; #define INVALID_STATE -1 static GQueue queued_state_changes; static enum SysPowerStates pending_system_state = INVALID_STATE; static guint state_machine_source_id; static gint update_pending; static gboolean ack_pending; static gboolean suspend_active = FALSE; static const char power_request_wakelock_name[] = "powerd_power_request"; gint suspend_block_count; static void state_transition_process(void); enum SysPowerStates current_system_power_state(void) { return current_system_state; } /* simple helper for debugging */ const gchar * state_to_string(int state) { switch (state) { case POWERD_SYS_STATE_ACTIVE: return "ACTIVE"; case POWERD_SYS_STATE_SUSPEND: return "SUSPEND"; default: return "UNKNOWN"; } } /* * block_suspend and unblock_suspend are only for use when new requests * are received, to ensure that autosuspend doesn't suspend the system * before the main loop can disable it after an active state request * is received. */ static void block_suspend(void) { int ret; if (g_atomic_int_add(&suspend_block_count, 1) == 0) { powerd_debug("Blocking suspend"); ret = libsuspend_acquire_wake_lock(power_request_wakelock_name); if (ret) powerd_warn("Could not acquire wake lock"); } } static void unblock_suspend(void) { gint old_count; int ret; gboolean retry; do { old_count = g_atomic_int_get(&suspend_block_count); if (old_count == 0) { powerd_warn("Suspend not blocked, refusing to unblock"); return; } retry = !g_atomic_int_compare_and_exchange(&suspend_block_count, old_count, old_count - 1); if (!retry && old_count == 1) { powerd_debug("Unblocking suspend"); ret = libsuspend_release_wake_lock(power_request_wakelock_name); if (ret) powerd_warn("Could not release wake lock"); } } while (retry); } /* Returns TRUE if the state request is valid, FALSE otherwise. * Nobody can request the SUSPEND state. */ static gboolean is_valid_state_request(int state) { return state > POWERD_SYS_STATE_SUSPEND && state < POWERD_NUM_POWER_STATES; } /* * Must only be called from main loop. * * internal callers should pass in NULL for the builder, its only used * by dbus callers. Returns a count of the total number of active * requests. */ static guint list_sys_requests_internal(GVariantBuilder *builder) { GHashTableIter iter; gpointer key, value; guint count = 0; g_hash_table_iter_init(&iter, state_request_hash); while (g_hash_table_iter_next(&iter, &key, &value)) { struct SysStateRequest *req = value; g_variant_builder_add(builder, "(ssi)", req->name, req->owner, req->state); count++; } return count; } /* Dump the requests to stdout and return the list via dbus */ gboolean handle_list_sys_requests(PowerdSource *obj, GDBusMethodInvocation *invocation) { GVariant *list, *tuple = NULL; GVariantBuilder *builder; guint count = 0; builder = g_variant_builder_new(G_VARIANT_TYPE("a(ssi)")); count = list_sys_requests_internal(builder); if (count>0) { list = g_variant_builder_end(builder); } else { g_variant_builder_clear(builder); list = g_variant_new_array(G_VARIANT_TYPE("(ssi)"), NULL, 0); } tuple = g_variant_new_tuple(&list,1); g_dbus_method_invocation_return_value(invocation, tuple); g_variant_builder_unref(builder); return TRUE; } /* Should be called only on main loop */ static int request_sys_state_worker(gpointer data) { struct SysStateRequest *sr = data; g_hash_table_insert(state_request_hash, sr->cookie, sr); state_request_count[sr->state - 1] += 1; powerd_dbus_name_watch_add(sr->owner); powerd_account_request_sys_state(sr->owner, sr->name); return 0; } /* Note: owner is NULL for internal usage */ gboolean request_sys_state_internal(const char *name, int state, uuid_t *cookie, const gchar *owner) { struct SysStateRequest *sr = NULL; if (cookie == NULL) { powerd_error("you need to pass in memory for a cookie"); return FALSE; } if (!is_valid_state_request(state)) { powerd_warn("invalid state requested: %d",state); return FALSE; } sr = g_new(struct SysStateRequest, 1); sr->name = g_strdup(name); sr->state = (enum SysPowerStates)state; if (owner) { sr->owner = g_strdup(owner); } else { sr->owner = g_strdup("internal"); } uuid_generate(sr->cookie); memcpy(*cookie, sr->cookie, sizeof(sr->cookie)); powerd_run_mainloop_sync(request_sys_state_worker, sr); update_system_state(); return TRUE; } gboolean handle_request_sys_state(PowerdSource *obj, GDBusMethodInvocation *invocation, const char *name, int state) { gboolean retval; const char *owner; uuid_t cookie; char ext_cookie[UUID_STR_LEN]; owner = g_dbus_method_invocation_get_sender(invocation); powerd_debug("handle_requestSysState from %s - %s (%d)", owner, state_to_string(state), state); retval = request_sys_state_internal(name, state, &cookie, owner); if (retval == TRUE) { powerd_debug("handle_requestSysState - SUCCESS"); uuid_unparse(cookie, ext_cookie); g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", ext_cookie)); } else { g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid state request"); } return TRUE; } static void __clear_sys_request(struct SysStateRequest *req) { powerd_dbus_name_watch_remove(req->owner); state_request_count[req->state - 1] -= 1; powerd_account_clear_sys_state(req->owner, req->name); } /* Must only be called from main loop */ static int clear_sys_state_worker(gpointer data) { unsigned char *uuid = data; struct SysStateRequest *sr; int found = FALSE; /* * This involves two lookups into the hash, one to find the * request so we can retrieve the state and another to remove * it. GHashTable doesn't seem to provide any more efficient * way to do this; too bad g_hash_table_steal() doesn't return * a pointer to the data. */ sr = g_hash_table_lookup(state_request_hash, uuid); if (sr) { /* We need to remove it from our watch hash before we remove it * from the state hash or the sr->owner memory will be freed * before we try to use it. */ __clear_sys_request(sr); found = g_hash_table_remove(state_request_hash, uuid); if (!found) powerd_warn("State request found on lookup but not on remove"); } return found; } gboolean clear_sys_state_internal(uuid_t cookie) { char cookie_str[UUID_STR_LEN]; int found; found = powerd_run_mainloop_sync(clear_sys_state_worker, cookie); if (!found) { uuid_unparse(cookie, cookie_str); powerd_warn("request (%s) not found", cookie_str); return FALSE; } update_system_state(); return TRUE; } gboolean handle_clear_sys_state(PowerdSource *obj, GDBusMethodInvocation *invocation, gchar *ext_cookie) { gboolean retval; uuid_t cookie; powerd_debug("handle_clearSysState from %s, cookie: %s", g_dbus_method_invocation_get_sender(invocation), ext_cookie); if (uuid_parse(ext_cookie, cookie)) { g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid cookie: %s", ext_cookie); return TRUE; } retval = clear_sys_state_internal(cookie); if (retval == TRUE) { g_dbus_method_invocation_return_value(invocation, NULL); } else { g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid cookie: %s", ext_cookie); } return TRUE; } static int enter_suspend(void) { int ret = libsuspend_enter_suspend(); if (!ret) suspend_active = TRUE; return ret; } static int exit_suspend(void) { int ret = libsuspend_exit_suspend(); if (!ret) { suspend_active = FALSE; powerd_display_exit_suspend(); } return ret; } gboolean powerd_suspend_active(void) { return suspend_active; } /* Must only be called from main loop */ static enum SysPowerStates max_requested_state(void) { enum SysPowerStates ret = POWERD_SYS_STATE_SUSPEND; int i; for (i = POWERD_NUM_POWER_STATES - 1; i > POWERD_SYS_STATE_SUSPEND; i--) { if (state_request_count[i - 1] != 0) { ret = i; break; } }; return ret; } static gboolean delayed_state_transition_process(gpointer unused) { state_transition_process(); return FALSE; } static enum state_transition_state process_state_dequeue(guint *delay) { int ret; *delay = 0; pending_system_state = (enum SysPowerStates)g_queue_pop_head(&queued_state_changes); if (pending_system_state == current_system_state) { powerd_debug("pending state == current state, discarding"); pending_system_state = INVALID_STATE; return STATE_TRANSITION_IDLE; } if (current_system_state == POWERD_SYS_STATE_SUSPEND) { powerd_debug("exiting suspend"); ret = exit_suspend(); if (ret) powerd_warn("Failed to exit suspend: %d", ret); } /* * XXX: If going to suspend we may want to delay a few seconds here * in case the user immediately tries to wake the device again after * an activity timeout. */ /* Must call libsuspend_prepare_suspend() _before_ emitting signal */ if (pending_system_state == POWERD_SYS_STATE_SUSPEND) { powerd_debug("preparing for suspend"); ret = libsuspend_prepare_suspend(); if (ret) powerd_warn("Failed to prepare for suspend: %d", ret); } ack_pending = powerd_client_transition_start(pending_system_state); powerd_sys_state_signal_emit(pending_system_state); *delay = ack_pending ? NOTIFICATION_TIMEOUT_MSECS : 0; return STATE_TRANSITION_NOTIFY; } static enum state_transition_state process_state_notify(guint *delay) { if (ack_pending) { powerd_warn("Timeout waiting for acks on state transition"); ack_pending = FALSE; } powerd_client_transition_finish(pending_system_state); *delay = 0; return STATE_TRANSITION_COMPLETE; } static enum state_transition_state process_state_complete(guint *delay) { int ret; gboolean allow_suspend; *delay = 0; /* * If more requests are in the queue or new requests are present * above our current target state, do not suspend so that those * requests can be processed. */ allow_suspend = !(!g_queue_is_empty(&queued_state_changes) || max_requested_state() > pending_system_state); if (pending_system_state == POWERD_SYS_STATE_SUSPEND && allow_suspend) { powerd_debug("entering suspend"); ret = enter_suspend(); if (ret) powerd_warn("Failed to enter suspend: %d", ret); } powerd_debug("Transition to %s complete", state_to_string(pending_system_state)); current_system_state = pending_system_state; pending_system_state = INVALID_STATE; /* * If state changes are queued, go back to the dequeue state. If * nothing is queued and max_requested_state() > pending_system_state * there should be another run of update_system_state() scheduled to * take care of this. */ return g_queue_is_empty(&queued_state_changes) ? STATE_TRANSITION_IDLE : STATE_TRANSITION_DEQUEUE; } static void state_transition_process(void) { guint delay = 0; state_machine_source_id = 0; do { switch (cur_state_transition_state) { case STATE_TRANSITION_DEQUEUE: cur_state_transition_state = process_state_dequeue(&delay); break; case STATE_TRANSITION_NOTIFY: cur_state_transition_state = process_state_notify(&delay); break; case STATE_TRANSITION_COMPLETE: cur_state_transition_state = process_state_complete(&delay); break; default: powerd_error("Invalid transition state, setting to idle"); cur_state_transition_state = STATE_TRANSITION_IDLE; break; } } while (delay == 0 && cur_state_transition_state != STATE_TRANSITION_IDLE); if (delay != 0 && cur_state_transition_state != STATE_TRANSITION_IDLE) { state_machine_source_id = g_timeout_add(delay, delayed_state_transition_process, NULL); } } /* Must be called from main loop */ void power_request_transition_acked(void) { if (cur_state_transition_state != STATE_TRANSITION_NOTIFY) { powerd_error("%s called while not waiting for acks", __func__); return; } powerd_debug("All acks received for state transition"); ack_pending = FALSE; if (state_machine_source_id) { g_source_remove(state_machine_source_id); state_machine_source_id = g_timeout_add(0, delayed_state_transition_process, NULL); } } static void check_queued_state_changes(void) { /* * If state change in progress we'll process the next queued state * change when finished. */ if (cur_state_transition_state != STATE_TRANSITION_IDLE) { powerd_debug("state change in progress, delaying"); return; } if (g_queue_is_empty(&queued_state_changes)) { powerd_debug("no pending state changes"); return; } /* Start processing state transition */ cur_state_transition_state = STATE_TRANSITION_DEQUEUE; state_transition_process(); } /* * XXX: Currently the coalescing is very simplistic and may need * improvement. */ static void enqueue_state_change(enum SysPowerStates state) { gboolean queue_empty; powerd_debug("Enqueue state change to %s", state_to_string(state)); /* * If the new request is the same as the one we're currently * processing we can drop all pending requests (including the * new one). */ if (pending_system_state == state) { powerd_debug("State == pending state, discarding"); g_queue_clear(&queued_state_changes); return; } else if (pending_system_state == INVALID_STATE) { queue_empty = g_queue_is_empty(&queued_state_changes); /* * If no state changes are pending or in progress and the * requested state is the same as the current state, do nothing. */ if (queue_empty && state == current_system_state) { powerd_debug("queue empty && state == current, discarding"); return; } /* * If we aren't processing a request and the current request is * the same as the next reques in the queue, all intermediate * requests can be dropped. We do this by clearing the queue and * then adding in the new request. * * XXX: This situation is probably impossible currently. */ if (!queue_empty && (enum SysPowerStates)g_queue_peek_tail(&queued_state_changes) == state) { powerd_debug("new state == next queued state, discarding all intermediate requests"); g_queue_clear(&queued_state_changes); } } g_queue_push_tail(&queued_state_changes, (gpointer)state); check_queued_state_changes(); } static gboolean update_system_state_handler(gpointer unused) { /* * We must clear update_pending just before calling max_requested_state() * to avoid races with adding new requests. We may end up with an * occassional redundant system state update, but that is harmless. */ g_atomic_int_set(&update_pending, 0); enqueue_state_change(max_requested_state()); /* * We assume that after enqueue_state_change() completes autosuspend * will have been disabled if any active requests are present. This * is currently true, but if the assumption is ever broken it will * expose us to potential races. */ unblock_suspend(); return FALSE; } void update_system_state(void) { if (g_atomic_int_compare_and_exchange(&update_pending, 0, 1)) { block_suspend(); g_timeout_add(0, update_system_state_handler, NULL); } } /* Destructor for the state request object */ static void sys_state_request_destroy(gpointer data) { struct SysStateRequest *sr = (struct SysStateRequest *)data; g_free((gpointer)sr->name); g_free((gpointer)sr->owner); g_free(sr); } void power_request_init(void) { state_request_hash = g_hash_table_new_full(powerd_uuid_hash, powerd_uuid_equal, NULL, sys_state_request_destroy); g_queue_init(&queued_state_changes); } void power_request_deinit(void) { g_hash_table_destroy(state_request_hash); g_queue_clear(&queued_state_changes); } /* * Callers may use this to clear all requests that the specified owner * is holding. This is used when the owner drops off of dbus */ void clear_sys_state_by_owner(const char *owner) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, state_request_hash); while (g_hash_table_iter_next(&iter, &key, &value)) { struct SysStateRequest *req = value; if (!strcmp(owner, req->owner)) { __clear_sys_request(req); g_hash_table_iter_remove(&iter); } } update_system_state(); } powerd-0.14+14.04.20140415/src/backlight.c0000644000015301777760000001220112323077401020212 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * Authors: * Michael Frey: michael.frey@canonical.com * Matthew Fischer: matthew.fischer@canonical.com * Seth Forshee: seth.forshee@canonical.com * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include #include #include "powerd-internal.h" #include "device-config.h" #include "log.h" struct light_device_t *light_dev; /* * Defaults are Android default settings. They may not work if we * can't read the per-device values, but they're as good as * anything else. */ int min_user_brightness = 10; int max_brightness = 255; int default_brightness = 102; int dim_brightness = 10; int current_brightness = -1; int powerd_get_brightness(void) { return current_brightness; } int powerd_get_max_brightness(void) { return max_brightness; } int powerd_set_brightness(int brightness) { struct light_state_t state; int scaled_brightness; int ret; if (!light_dev) return -ENODEV; if (brightness < 0 || brightness > max_brightness) return -EINVAL; if (brightness == current_brightness) return 0; /* Scale brightness to range of 0 to 255 */ scaled_brightness = (brightness * 255) / max_brightness; if (scaled_brightness > 255) scaled_brightness = 255; state.flashMode = LIGHT_FLASH_NONE; state.brightnessMode = BRIGHTNESS_MODE_USER; /* color is ARGB, docs specifiy that alpha should be 0xff, * although it also instructs callers to ignore it. */ state.color = (int)((0xffU << 24) | (scaled_brightness << 16) | (scaled_brightness << 8) | scaled_brightness); ret = light_dev->set_light(light_dev, &state); if (!ret) current_brightness = brightness; return ret; } int powerd_set_user_brightness(int brightness) { return powerd_set_brightness(brightness < min_user_brightness ? min_user_brightness : brightness); } void powerd_dim_screen(void) { powerd_set_brightness(dim_brightness); } int powerd_backlight_init(void) { GValue v = G_VALUE_INIT; const struct hw_module_t *hwmod = NULL; if (!device_config_get("screenBrightnessDim", &v)) { if (G_VALUE_HOLDS_UINT(&v)) dim_brightness = (int)g_value_get_uint(&v); g_value_unset(&v); } if (!device_config_get("screenBrightnessSettingMinimum", &v)) { if (G_VALUE_HOLDS_UINT(&v)) min_user_brightness = (int)g_value_get_uint(&v); g_value_unset(&v); } if (!device_config_get("screenBrightnessSettingMaximum", &v)) { if (G_VALUE_HOLDS_UINT(&v)) max_brightness = (int)g_value_get_uint(&v); g_value_unset(&v); } if (!device_config_get("screenBrightnessSettingDefault", &v)) { if (G_VALUE_HOLDS_UINT(&v)) default_brightness = (int)g_value_get_uint(&v); g_value_unset(&v); } /* Sanity check default brightness value */ if (default_brightness < min_user_brightness) default_brightness = min_user_brightness; else if (default_brightness > max_brightness) default_brightness = max_brightness; if (hw_get_module(LIGHTS_HARDWARE_MODULE_ID, &hwmod)) { powerd_warn("Could not get lights module"); return -ENODEV; } if (hwmod->methods->open(hwmod, LIGHT_ID_BACKLIGHT, (hw_device_t **)&light_dev)) { powerd_warn("Could not open backlight, brightness adjustment will be disabled"); light_dev = NULL; return -ENODEV; } /* XXX: May want to eliminate this when integration with shell * is complete and just wait for it to apply user settings. * Unfortunately the HAL provides no way for us to read the * current brightness when we start. */ powerd_set_brightness(default_brightness); return 0; } void powerd_backlight_deinit(void) { light_dev = NULL; } /** dbus interfaces **/ gboolean handle_get_brightness_params(PowerdSource *obj, GDBusMethodInvocation *invocation) { gboolean ab_available = powerd_autobrightness_available(); g_dbus_method_invocation_return_value(invocation, g_variant_new("((iiib))", min_user_brightness, max_brightness, default_brightness, ab_available)); return TRUE; } powerd-0.14+14.04.20140415/src/log.c0000644000015301777760000000235312323077401017052 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include #include "log.h" static int debug = 0; void powerd_log(int level, const char *fmt, ...) { va_list args; if (level == POWERD_LOG_DEBUG && !debug) return; va_start(args, fmt); vsyslog(level, fmt, args); va_end(args); } void powerd_log_init(void) { char *debug_str; openlog("powerd", LOG_PID, LOG_DAEMON); debug_str = getenv("POWERD_DEBUG"); if (debug_str) { if (!strcmp(debug_str, "1")) { debug = 1; powerd_debug("debug enabled"); } } } powerd-0.14+14.04.20140415/src/spline.c0000644000015301777760000000717412323077401017571 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include struct spline_params { double k, m; }; struct spline { double (*points)[2]; int n; struct spline_params params[0]; }; int spline_sort_comp(const void *a, const void *b) { double x1 = *(double *)a; double x2 = *(double *)b; return (int)(x1 - x2); } /* * Initializes a monotone cubic Hermite spline for interpolating * between the input points, using the Fritsch–Carlson method. * * http://en.wikipedia.org/wiki/Monotone_cubic_interpolation * * The interpolated points are guaranteed to have piecewise * monotonicity, and will be fully monotone if the input points * are monotone. */ struct spline *spline_new(double (*points)[2], int num_points) { struct spline *s; struct spline_params *param; double (*p)[2]; int i; double alpha, beta, prev_beta; s = malloc(sizeof(struct spline) + sizeof(struct spline_params) * num_points); if (!s) return NULL; s->points = malloc(2 * sizeof(double) * num_points); if (!s->points) { free(s); return NULL; } s->n = num_points; /* * Input points are likely to already be in order of increasing * x, but sort them just in case. */ memcpy(s->points, points, 2 * sizeof(double) * num_points); qsort(s->points, num_points, 2 * sizeof(double), spline_sort_comp); p = s->points; param = s->params; for (i = 0; i < s->n - 1; i++) param[i].k = (p[i+1][1] - p[i][1]) / (p[i+1][0] - p[i][0]); param[0].m = param[0].k; for (i = 1; i < s->n; i++) param[i].m = (param[i-1].k + param[i].k) / 2; param[s->n - 1].m = param[s->n - 2].k; beta = 0; for (i = 0; i < s->n - 1; i++) { if (p[i][1] == p[i+1][1]) { param[i].m = 0; param[++i].m = 0; beta = 0; continue; } prev_beta = beta; alpha = param[i].m / param[i].k; beta = param[i+1].m / param[i].k; if (alpha < 0 || prev_beta < 0) { param[i].m = 0; continue; } if (alpha > 3) param[i].m = 3 * param[i].k; if (beta > 3) param[i+1].m = 3 * param[i].k; } return s; } void spline_free(struct spline *s) { free(s->points); free(s); } double spline_interpolate(struct spline *s, double x) { int i; double h, t, ret; for (i = 0; i < s->n - 1; i++) { if (x >= s->points[i][0] && x < s->points[i+1][0]) break; } /* Handle out-of-bounds and boundary cases */ if (i == 0 && x <= s->points[0][0]) return s->points[0][1]; if (i >= s->n - 1) return s->points[s->n - 1][1]; h = s->points[i+1][0] - s->points[i][0]; t = (x - s->points[i][0]) / h; ret = s->points[i][1] * (1 + t * t * (2 * t - 3)); ret += h * s->params[i].m * t * (1 + t * (t - 2)); ret += s->points[i+1][1] * t * t * (3 - 2 * t); ret += h * s->params[i+1].m * t * t * (t - 1); return ret; } powerd-0.14+14.04.20140415/src/stats.c0000644000015301777760000004401112323077401017424 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include #include #include #include #include #include #include "powerd-internal.h" #include "powerd-dbus.h" #include "log.h" #define USECS_PER_SEC 1000000 #define NSECS_PER_USEC 1000 struct client_stats { const char *dbus_name; GSList *sys_stats; GSList *disp_stats; }; struct sys_request_stats { const char *name; unsigned active_count; guint64 active_time; guint64 max_active_time; guint64 active_since; }; struct disp_request_stats { const char *name; unsigned active_count; guint64 active_time; guint64 max_active_time; guint64 active_since; guint64 disp_on_time; guint64 disp_on_since; guint64 flag_on_time[POWERD_NUM_DISPLAY_FLAGS]; guint64 flag_on_since[POWERD_NUM_DISPLAY_FLAGS]; struct powerd_display_request last_req; }; GHashTable *stats_hash; static guint64 get_usecs(void) { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts)) { powerd_error("Could not get monotonic time: %s", strerror(errno)); return 0; } return (guint64)ts.tv_sec * USECS_PER_SEC + (ts.tv_nsec + NSECS_PER_USEC / 2) / NSECS_PER_USEC; } static struct client_stats *alloc_client(const char *dbus_name) { struct client_stats *client = calloc(1, sizeof(*client)); if (!client) { powerd_error("Error allocating client for statistics"); return NULL; } client->dbus_name = strdup(dbus_name); if (!client->dbus_name) { powerd_error("Error duplicating client dbus name for statistics"); free(client); return NULL; } return client; } static struct sys_request_stats *alloc_sys_stats(const char *name) { struct sys_request_stats *stats = calloc(1, sizeof(*stats)); if (!stats) { powerd_error("Error allocating memory for system request statistics"); return NULL; } stats->name = strdup(name); if (!stats->name) { powerd_error("Error duplicating request name for statistics"); free(stats); return NULL; } return stats; } static struct disp_request_stats *alloc_disp_stats(const char *name) { struct disp_request_stats *stats = calloc(1, sizeof(*stats)); if (!stats) { powerd_error("Error allocating memory for display request statistics"); return NULL; } stats->name = strdup(name); if (!stats->name) { powerd_error("Error duplicating request name for statistics"); free(stats); return NULL; } return stats; } static gint sys_stats_comp(gconstpointer a, gconstpointer b) { const struct sys_request_stats *stats = a; const char *name = b; return strcmp(stats->name, name); } static gint disp_stats_comp(gconstpointer a, gconstpointer b) { const struct disp_request_stats *stats = a; const char *name = b; return strcmp(stats->name, name); } static struct client_stats *lookup_client(const char *dbus_name, gboolean alloc) { struct client_stats *client; if (!stats_hash) return NULL; client = g_hash_table_lookup(stats_hash, dbus_name); if (!client && alloc) { client = alloc_client(dbus_name); if (client) g_hash_table_insert(stats_hash, (gpointer)client->dbus_name, client); } return client; } void powerd_account_request_sys_state(const char *dbus_name, const char *name) { struct client_stats *client; struct sys_request_stats *stats; GSList *item; client = lookup_client(dbus_name, TRUE); if (!client) return; item = g_slist_find_custom(client->sys_stats, name, sys_stats_comp); if (item) { stats = item->data; } else { stats = alloc_sys_stats(name); if (!stats) return; client->sys_stats = g_slist_prepend(client->sys_stats, stats); } stats->active_count++; stats->active_since = get_usecs(); } void powerd_account_clear_sys_state(const char *dbus_name, const char *name) { struct client_stats *client; struct sys_request_stats *stats; GSList *item; guint64 duration; if (!stats_hash) return; client = lookup_client(dbus_name, FALSE); if (!client) return; item = g_slist_find_custom(client->sys_stats, name, sys_stats_comp); if (!item) return; stats = item->data; duration = get_usecs() - stats->active_since; stats->active_since = 0; stats->active_time += duration; if (duration > stats->max_active_time) stats->max_active_time = duration; } static void aggregate_disp_stats(struct disp_request_stats *stats, const struct powerd_display_request *new) { guint64 us; int i; us = get_usecs(); if (new->state != stats->last_req.state) { if (new->state == POWERD_DISPLAY_STATE_ON) { stats->disp_on_since = us; } else { stats->disp_on_time += us - stats->disp_on_since; stats->disp_on_since = 0; } } for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++) { guint32 mask = 1 << i; if ((new->flags & mask) != (stats->last_req.flags & mask)) { if (new->flags & mask) { stats->flag_on_since[i] = us; } else { stats->flag_on_time[i] += us - stats->flag_on_since[i]; stats->flag_on_since[i] = 0; } } } } void powerd_account_add_display_req(const char *dbus_name, const char *name, const struct powerd_display_request *req) { struct client_stats *client; struct disp_request_stats *stats; GSList *item; guint64 us; int i; if (!stats_hash) return; client = lookup_client(dbus_name, TRUE); if (!client) return; item = g_slist_find_custom(client->disp_stats, name, disp_stats_comp); if (item) { stats = item->data; } else { stats = alloc_disp_stats(name); if (!stats) return; client->disp_stats = g_slist_prepend(client->disp_stats, stats); } us = get_usecs(); stats->active_count++; stats->active_since = us; if (req->state == POWERD_DISPLAY_STATE_ON) stats->disp_on_since = us; for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++) { if (req->flags & (1 << i)) stats->flag_on_since[i] = us; } stats->last_req = *req; } void powerd_account_update_display_req(const char *dbus_name, const char *name, const struct powerd_display_request *req) { struct client_stats *client; struct disp_request_stats *stats; GSList *item; client = lookup_client(dbus_name, FALSE); if (!client) return; item = g_slist_find_custom(client->disp_stats, name, disp_stats_comp); if (!item) return; stats = item->data; aggregate_disp_stats(stats, req); stats->last_req = *req; } void powerd_account_clear_display_req(const char *dbus_name, const char *name) { struct client_stats *client; struct disp_request_stats *stats; GSList *item; struct powerd_display_request req; guint64 duration; client = lookup_client(dbus_name, FALSE); if (!client) return; item = g_slist_find_custom(client->disp_stats, name, disp_stats_comp); if (!item) return; stats = item->data; memset(&req, 0, sizeof(req)); aggregate_disp_stats(stats, &req); duration = get_usecs() - stats->active_since; stats->active_time += duration; stats->active_since = 0; if (duration > stats->max_active_time) stats->max_active_time = duration; stats->last_req = req; } static void log_sys_req_stats(struct client_stats *client) { const char *dbus_name = client->dbus_name; GSList *cur; guint64 us; us = get_usecs(); for (cur = client->sys_stats; cur; cur = g_slist_next(cur)) { struct sys_request_stats *stats = cur->data; guint64 active_time, max_active_time; /* * Aggregate currently held requests into active_time, and * consider whether this is greater than the current * max_active_time. */ active_time = stats->active_time; max_active_time = stats->max_active_time; if (stats->active_since != 0) { guint64 duration = us - stats->active_since; active_time += duration; if (duration > max_active_time) max_active_time = duration; } powerd_info(" Name: %s", stats->name); powerd_info(" Owner: %s", dbus_name); powerd_info(" Active Count: %u", stats->active_count); powerd_info(" Active Time: %f", (double)active_time / 1000000.0f); powerd_info(" Max Active Time: %f", (double)max_active_time / 1000000.0f); powerd_info(" Active Since: %f", (double)stats->active_since / 1000000.0f); } } static void log_disp_req_stats(struct client_stats *client) { const char *dbus_name = client->dbus_name; GSList *cur; guint64 us; us = get_usecs(); for (cur = client->disp_stats; cur; cur = g_slist_next(cur)) { struct disp_request_stats *stats = cur->data; guint64 active_time, max_active_time; guint64 disp_on_time; int i; active_time = stats->active_time; max_active_time = stats->max_active_time; if (stats->active_since != 0) { guint64 duration = us - stats->active_since; active_time += duration; if (duration > max_active_time) max_active_time = duration; } disp_on_time = stats->disp_on_time; if (stats->disp_on_since != 0) disp_on_time += us - stats->disp_on_since; powerd_info(" Name: %s", stats->name); powerd_info(" Owner: %s", dbus_name); powerd_info(" Active Count: %u", stats->active_count); powerd_info(" Active Time: %f", (double)active_time / 1000000.0f); powerd_info(" Max Active Time: %f", (double)max_active_time / 1000000.0f); powerd_info(" Active Since: %f", (double)stats->active_since / 1000000.0f); powerd_info(" Display On Time: %f", (double)disp_on_time / 1000000.0f); powerd_info(" Display On Since: %f", (double)stats->disp_on_since / 1000000.0f); for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++) { guint64 on_time = stats->flag_on_time[i]; if (stats->flag_on_since[i] != 0) on_time += us - stats->flag_on_since[i]; powerd_info(" Flag %d On Time: %f", i, (double)on_time / 1000000.0f); powerd_info(" Flag %d On Since: %f", i, (double)stats->flag_on_since[i] / 1000000.0f); } } } void powerd_log_stats(void) { struct sysinfo si; GHashTableIter iter; gpointer key, value; if (!sysinfo(&si)) powerd_info("Uptime: %ld", si.uptime); if (stats_hash) { powerd_info("System Request Statistics:"); g_hash_table_iter_init(&iter, stats_hash); while (g_hash_table_iter_next(&iter, &key, &value)) log_sys_req_stats(value); powerd_info("Display Request Statistics:"); g_hash_table_iter_init(&iter, stats_hash); while (g_hash_table_iter_next(&iter, &key, &value)) log_disp_req_stats(value); } } static void sys_stats_list_destroy(gpointer data) { struct sys_request_stats *stats = data; free((void *)stats->name); free(stats); } static void disp_stats_list_destroy(gpointer data) { struct disp_request_stats *stats = data; free((void *)stats->name); free(stats); } static void stats_hash_destroy(gpointer data) { struct client_stats *client = data; g_slist_free_full(client->sys_stats, sys_stats_list_destroy); g_slist_free_full(client->disp_stats, disp_stats_list_destroy); free((void *)client->dbus_name); free(client); } int powerd_stats_init(void) { stats_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, stats_hash_destroy); if (!stats_hash) { powerd_warn("Unable to allocate stats hash table"); return -ENOMEM; } return 0; } void powerd_stats_deinit(void) { if (stats_hash) g_hash_table_destroy(stats_hash); } /* Dbus interface support */ static int build_sys_request_list(GVariantBuilder *builder, struct client_stats *client) { const char *dbus_name = client->dbus_name; GSList *cur; guint64 us; int count = 0; us = get_usecs(); for (cur = client->sys_stats; cur; cur = g_slist_next(cur)) { struct sys_request_stats *stats = cur->data; guint64 active_time, max_active_time; /* * Aggregate currently held requests into active_time, and * consider whether this is greater than the current * max_active_time. */ active_time = stats->active_time; max_active_time = stats->max_active_time; if (stats->active_since != 0) { guint64 duration = us - stats->active_since; active_time += duration; if (duration > max_active_time) max_active_time = duration; } g_variant_builder_add(builder, "(ssuttt)", dbus_name, stats->name, stats->active_count, active_time, max_active_time, stats->active_since); count++; } return count; } gboolean handle_get_sys_request_stats(PowerdSource *obj, GDBusMethodInvocation *invocation) { GVariantBuilder *builder; GVariant *list, *tuple; GHashTableIter iter; gpointer key, value; int count = 0; builder = g_variant_builder_new(G_VARIANT_TYPE("a(ssuttt)")); if (stats_hash) { g_hash_table_iter_init(&iter, stats_hash); while (g_hash_table_iter_next(&iter, &key, &value)) count += build_sys_request_list(builder, value); } if (count == 0) { g_variant_builder_clear(builder); list = g_variant_new_array(G_VARIANT_TYPE("a(ssuttt)"), NULL, 0); } else { list = g_variant_builder_end(builder); } tuple = g_variant_new_tuple(&list, 1); g_dbus_method_invocation_return_value(invocation, tuple); g_variant_builder_unref(builder); return TRUE; } static int build_disp_request_list(GVariantBuilder *builder, struct client_stats *client) { const char *dbus_name = client->dbus_name; GSList *cur; guint64 us; int count = 0; us = get_usecs(); for (cur = client->disp_stats; cur; cur = g_slist_next(cur)) { struct disp_request_stats *stats = cur->data; guint64 active_time, max_active_time; guint64 disp_on_time; GVariantBuilder *fot_builder, *fos_builder; int i; active_time = stats->active_time; max_active_time = stats->max_active_time; if (stats->active_since != 0) { guint64 duration = us - stats->active_since; active_time += duration; if (duration > max_active_time) max_active_time = duration; } disp_on_time = stats->disp_on_time; if (stats->disp_on_since != 0) disp_on_time += us - stats->disp_on_since; fot_builder = g_variant_builder_new(G_VARIANT_TYPE("at")); fos_builder = g_variant_builder_new(G_VARIANT_TYPE("at")); for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++) { guint64 flag_on_time = stats->flag_on_time[i]; if (stats->flag_on_since[i] != 0) flag_on_time += us - stats->flag_on_since[i]; g_variant_builder_add(fot_builder, "t", flag_on_time); g_variant_builder_add(fos_builder, "t", stats->flag_on_since[i]); } g_variant_builder_add(builder, "(ssutttttatat)", dbus_name, stats->name, stats->active_count, active_time, max_active_time, stats->active_since, disp_on_time, stats->disp_on_since, fot_builder, fos_builder); g_variant_builder_unref(fot_builder); g_variant_builder_unref(fos_builder); count++; } return count; } gboolean handle_get_disp_request_stats(PowerdSource *obj, GDBusMethodInvocation *invocation) { GVariantBuilder *builder; GVariant *list, *tuple; GHashTableIter iter; gpointer key, value; int count = 0; builder = g_variant_builder_new(G_VARIANT_TYPE("a(ssutttttatat)")); if (stats_hash) { g_hash_table_iter_init(&iter, stats_hash); while (g_hash_table_iter_next(&iter, &key, &value)) count += build_disp_request_list(builder, value); } if (count == 0) { g_variant_builder_clear(builder); list = g_variant_new_array(G_VARIANT_TYPE("a(ssutttttatat)"), NULL, 0); } else { list = g_variant_builder_end(builder); } tuple = g_variant_new_tuple(&list, 1); g_dbus_method_invocation_return_value(invocation, tuple); g_variant_builder_unref(builder); return TRUE; } powerd-0.14+14.04.20140415/src/power-source.c0000644000015301777760000000773112323077401020730 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include "powerd-internal.h" #include "device-config.h" #include "log.h" static UpClient *up_client; static double shutdown_temp = 68.0f; static double battery_log_level = 4.0f; static gboolean stats_logged; static void up_device_changed_cb(UpClient *client, UpDevice *device, gpointer unused) { gboolean on_battery; GPtrArray *devices; int i; gboolean log_stats = TRUE; gboolean low_batt_shutdown = TRUE; gboolean thermal_shutdown = FALSE; g_object_get(up_client, "on_battery", &on_battery, NULL); if (!on_battery) low_batt_shutdown = FALSE; devices = up_client_get_devices(up_client); for (i = 0; i < devices->len; i++) { UpDevice *device = g_ptr_array_index(devices, i); UpDeviceKind kind; gboolean is_present; g_object_get(device, "kind", &kind, "is-present", &is_present, NULL); if (kind == UP_DEVICE_KIND_BATTERY && is_present) { double percentage, temp; g_object_get(device, "percentage", &percentage, "temperature", &temp, NULL); /* * Log battery stats a little before emergency * shutdown is triggered to avoid any delays * when we actually need to shut down. */ if (percentage > battery_log_level) { log_stats = FALSE; stats_logged = FALSE; } /* * Android shuts down when percentage reaches 0. We * use 1% to leave a little more headroom. */ if (percentage > 1.0) low_batt_shutdown = FALSE; if (temp >= shutdown_temp) { powerd_warn("Critical battery temperature %f\n", temp); thermal_shutdown = TRUE; break; } } } if (log_stats && !stats_logged) { powerd_log_stats(); stats_logged = TRUE; } if (low_batt_shutdown || thermal_shutdown) { powerd_warn("Initiating emergency shutdown"); powerd_shutdown(); } } int powerd_ps_init(void) { GValue v = G_VALUE_INIT; gboolean ret; /* * Override default shutdown temperature with device-specific * value, if avaialable */ if (!device_config_get("shutdownBatteryTemperature", &v) && G_VALUE_HOLDS_UINT(&v)) { shutdown_temp = (double)g_value_get_uint(&v) / 10.0f; g_value_unset(&v); } if (!device_config_get("criticalBatteryWarningLevel", &v) && G_VALUE_HOLDS_UINT(&v)) { battery_log_level = (double)g_value_get_uint(&v); g_value_unset(&v); } up_client = up_client_new(); if (!up_client) { powerd_warn("Could not allocate upower client"); return -ENODEV; } ret = up_client_enumerate_devices_sync(up_client, NULL, NULL); if (!ret) powerd_warn("Could not enumerate upower devices"); g_signal_connect(up_client, "device-changed", G_CALLBACK(up_device_changed_cb), NULL); return 0; } void powerd_ps_deinit(void) { if (up_client) g_object_unref(up_client); } powerd-0.14+14.04.20140415/src/device-config.c0000644000015301777760000002547212323077401021002 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include #include #include #include #include #include #include #include "powerd-internal.h" #include "device-config.h" #include "log.h" /* * Android defines various device-specific parameters in files named * config.xml. The general structure of the tags is: * * ... * * This will define a config option named "foo" of type "type". Types * for arrays are included. For example, the following defines an * array of integers: * * * 0 * 1 * * * In powerd we are only interested in a few specific variables. The * code here offers fairly general parsing of the config files, but * handling of only a few specific types is present. */ enum elem_type { ELEM_TYPE_NONE, ELEM_TYPE_BOOL, ELEM_TYPE_INT, ELEM_TYPE_STRING, ELEM_TYPE_FRACTION, ELEM_TYPE_DIMENSION, ELEM_TYPE_INT_ARRAY, ELEM_TYPE_STRING_ARRAY, ELEM_TYPE_ITEM, NUM_ELEM_TYPES }; static const char *elem_strs[NUM_ELEM_TYPES] = { [ELEM_TYPE_BOOL] = "bool", [ELEM_TYPE_INT] = "integer", [ELEM_TYPE_STRING] = "string", [ELEM_TYPE_FRACTION] = "fraction", [ELEM_TYPE_DIMENSION] = "dimen", [ELEM_TYPE_INT_ARRAY] = "integer-array", [ELEM_TYPE_STRING_ARRAY] = "string-array", [ELEM_TYPE_ITEM] = "item", }; struct parser_state { enum elem_type type; gboolean in_item; gchar *name; GValue value; }; static struct parser_state state = { .value = G_VALUE_INIT, }; static GHashTable *config_hash; static enum elem_type get_elem_type(const gchar *name) { int i; for (i = 0; i < NUM_ELEM_TYPES; i++) { if (elem_strs[i] && !strcmp(name, elem_strs[i])) break; } if (i >= NUM_ELEM_TYPES) return ELEM_TYPE_NONE; return i; } static const gchar *get_elem_name(const gchar **attr_names, const gchar **attr_values) { int i; for (i = 0; attr_names[i]; i++) { if (!strcmp(attr_names[i], "name")) break; } if (!attr_names[i]) return NULL; return attr_values[i]; } static void on_start_elem(GMarkupParseContext *context, const gchar *name, const gchar **attr_names, const gchar **attr_values, gpointer user_data, GError **error) { const gchar *config_name; enum elem_type type; type = get_elem_type(name); if (type == ELEM_TYPE_NONE) return; if (type == ELEM_TYPE_ITEM) { if (state.type == ELEM_TYPE_INT_ARRAY || state.type == ELEM_TYPE_STRING_ARRAY) state.in_item = TRUE; return; } if (state.type != ELEM_TYPE_NONE) { g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Nested elements not supported"); return; } config_name = get_elem_name(attr_names, attr_values); if (!config_name) return; /* Strip leading "config_" if present, not all vars have it */ if (!strncmp(config_name, "config_", 7)) config_name += 7; switch (type) { case ELEM_TYPE_BOOL: g_value_init(&state.value, G_TYPE_BOOLEAN); break; case ELEM_TYPE_INT: g_value_init(&state.value, G_TYPE_UINT); break; case ELEM_TYPE_INT_ARRAY: { GArray *a; g_value_init(&state.value, G_TYPE_ARRAY); a = g_array_new(FALSE, FALSE, sizeof(guint32)); g_value_set_boxed(&state.value, a); break; } default: /* Type not supported at this time */ return; } state.type = type; state.in_item = FALSE; state.name = g_strdup(config_name); } static void on_end_elem(GMarkupParseContext *context, const gchar *name, gpointer user_data, GError **error) { enum elem_type type; GValue *value; if (state.type == ELEM_TYPE_NONE) return; type = get_elem_type(name); if (type == ELEM_TYPE_NONE) return; if (type == ELEM_TYPE_ITEM) { state.in_item = FALSE; return; } if (type == state.type) { value = g_new0(GValue, 1); g_value_init(value, G_VALUE_TYPE(&state.value)); g_value_copy(&state.value, value); g_hash_table_replace(config_hash, state.name, value); /* Note: state.name is not freed as it is used for hash table key */ } else { g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Start and end element types don't match"); g_free(state.name); } state.type = ELEM_TYPE_NONE; state.name = NULL; g_value_unset(&state.value); } /* WARNING: pt_text is not nul-terminated */ static void on_text(GMarkupParseContext *context, const gchar *pt_text, gsize text_len, gpointer user_data, GError **error) { gchar *text = g_strndup(pt_text, text_len); switch (state.type) { case ELEM_TYPE_BOOL: { gboolean value; if (!G_VALUE_HOLDS_BOOLEAN(&state.value)) { g_warning("Incorrect GValue type"); break; } value = !g_ascii_strcasecmp(text, "true"); g_value_set_boolean(&state.value, value); break; } case ELEM_TYPE_INT: { guint value; gchar *endp; if (!G_VALUE_HOLDS_UINT(&state.value)) { g_warning("Incorrect GValue type"); break; } value = (guint)g_ascii_strtoll(text, &endp, 0); if (endp - text == text_len) g_value_set_uint(&state.value, value); break; } case ELEM_TYPE_INT_ARRAY: { GArray *a; guint value; gchar *endp; if (!state.in_item) break; if (!G_VALUE_HOLDS_BOXED(&state.value)) { g_warning("Incorrect GValue type"); break; } value = (guint)g_ascii_strtoll(text, &endp, 0); if (endp - text == text_len) { a = g_value_get_boxed(&state.value); g_array_append_val(a, value); } } break; default: break; } g_free(text); } static GMarkupParser parser = { .start_element = on_start_elem, .end_element = on_end_elem, .text = on_text, }; static void config_hash_destroy_key(gpointer data) { g_free(data); } static void config_hash_destroy_value(gpointer data) { GValue *v = data; if (G_VALUE_HOLDS_BOXED(v)) { /* Currently only boxed data is arrays */ GArray *a = g_value_get_boxed(v); g_array_free(a, TRUE); } g_value_unset(v); g_free(v); } static int process_config(const char *fname) { int fd, ret = 0; gchar *buf = NULL; long page_size; int buf_len; ssize_t len; GMarkupParseContext *context = NULL; GError *error; fd = open(fname, O_RDONLY); if (fd == -1) { powerd_warn("Could not open device config %s: %s", fname, strerror(errno)); return -errno; } buf_len = 4096; page_size = sysconf(_SC_PAGESIZE); if (page_size > 0) buf_len = (int)page_size; buf = malloc(buf_len); if (!buf) { ret = -ENOMEM; goto out; } context = g_markup_parse_context_new(&parser, 0, NULL, NULL); do { len = read(fd, buf, buf_len); if (len == 0) break; if (len == -1) { if (errno == EINTR) continue; powerd_warn("Error reading %s: %s", fname, strerror(errno)); ret = -errno; break; } error = NULL; if (!g_markup_parse_context_parse(context, buf, len, &error)) { powerd_warn("Failed parsing device config %s\n", fname); if (error) { powerd_warn("%s", error->message); g_error_free(error); } ret = -EIO; } } while (!ret); out: if (context) g_markup_parse_context_free(context); if (buf) free(buf); close(fd); return ret; } /* * Get the device config option specified by @name. The leading * "config_" characters should be stripped from the name if present. * Returns 0 if the given variable is found. * * @value should be an initialized GValue. On success, the value of * the variable will be copied to the GValue, and the caller is * responsible for freeing the memory. E.g.: * * GValue v = G_VALUE_INIT; * if (!device_config_get("myVar", &v)) { * do_something_with_value(&v); * g_value_unset(&v); * } */ int device_config_get(const char *name, GValue *value) { GValue *v; if (!config_hash) return -ENODEV; v = g_hash_table_lookup(config_hash, name); if (!v) return -ENOENT; g_value_init(value, G_VALUE_TYPE(v)); g_value_copy(v, value); return 0; } void device_config_init(void) { char device[PROP_VALUE_MAX]; char *xml_path; if (!property_get("ro.product.device", device, NULL)) { powerd_warn("Could not determine device, running without config"); return; } powerd_info("Running on %s\n", device); config_hash = g_hash_table_new_full(g_str_hash, g_str_equal, config_hash_destroy_key, config_hash_destroy_value); /* * Always start with config-default.xml for defaults, then * the device-specific config */ if (process_config(POWERD_DEVICE_CONFIGS_PATH "/config-default.xml")) goto error; xml_path = g_strdup_printf("%s/config-%s.xml", POWERD_DEVICE_CONFIGS_PATH, device); if (process_config(xml_path)) goto error; g_free(xml_path); return; error: device_config_deinit(); } void device_config_deinit(void) { if (config_hash) { g_hash_table_destroy(config_hash); config_hash = NULL; } } powerd-0.14+14.04.20140415/src/powerd-sensors.cpp0000644000015301777760000000471512323077401021627 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * Authors: * Ricardo Mendoza: ricardo.mendoza@canonical.com * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include "powerd-internal.h" #include "powerd-sensors.h" #include "log.h" #include #include UASensorsProximity* prox_sensor; UASensorsLight* light_sensor; void on_new_proximity_event(UASProximityEvent *event, void *context) { switch (uas_proximity_event_get_distance(event)) { case U_PROXIMITY_NEAR: { powerd_proximity_event(TRUE); break; } case U_PROXIMITY_FAR: { powerd_proximity_event(FALSE); break; } } } void powerd_sensors_proximity_enable(void) { ua_sensors_proximity_enable(prox_sensor); } void powerd_sensors_proximity_disable(void) { ua_sensors_proximity_disable(prox_sensor); } void on_new_als_event(UASLightEvent *event, void *context) { float lux = uas_light_event_get_light(event); powerd_new_als_event(lux); } void powerd_sensors_als_enable(void) { if (light_sensor) ua_sensors_light_enable(light_sensor); } void powerd_sensors_als_disable(void) { if (light_sensor) ua_sensors_light_disable(light_sensor); } void powerd_sensors_init(void) { prox_sensor = ua_sensors_proximity_new(); if (prox_sensor != NULL) { ua_sensors_proximity_set_reading_cb(prox_sensor, on_new_proximity_event, NULL); } else { powerd_warn("Failed to allocate proximity sensor"); } light_sensor = ua_sensors_light_new(); if (light_sensor) { ua_sensors_light_set_reading_cb(light_sensor, on_new_als_event, NULL); } else { powerd_warn("Failed to allocate ambient light sensor"); } } powerd-0.14+14.04.20140415/src/spline.h0000644000015301777760000000154712323077401017574 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #ifndef __SPLINE_H__ #define __SPLINE_H__ struct spline; struct spline *spline_new(double (*points)[2], int num_points); void spline_free(struct spline *s); double spline_interpolate(struct spline *s, double x); #endif powerd-0.14+14.04.20140415/src/powerd.h0000644000015301777760000000333712323077401017601 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #ifndef __POWERD_H__ #define __POWERD_H__ #ifdef __cplusplus extern "C" { #endif enum SysPowerStates { //Note that callers will be notified of suspend state changes //but may not request this state. POWERD_SYS_STATE_SUSPEND = 0, //The Active state will prevent system suspend POWERD_SYS_STATE_ACTIVE, POWERD_NUM_POWER_STATES }; enum powerd_display_state { POWERD_DISPLAY_STATE_DONT_CARE = 0, POWERD_DISPLAY_STATE_ON, /* Keep last */ POWERD_NUM_DISPLAY_STATES }; /* Use proximity sensor to override screen state */ #define POWERD_DISPLAY_FLAG_USE_PROXIMITY (1 << 0) /* Force autobrightness to be disabled */ #define POWERD_DISPLAY_FLAG_DISABLE_AUTOBRIGHTNESS (1 << 1) /* Request the screen to stay bright */ #define POWERD_DISPLAY_FLAG_BRIGHT (1 << 2) /* Must be updated when new flags are added */ #define POWERD_NUM_DISPLAY_FLAGS 3 /* * Clients should treat this as an opaque type. Though this isn't really * possible until the client library is written. */ typedef char powerd_cookie_t[37]; #ifdef __cplusplus } #endif #endif powerd-0.14+14.04.20140415/src/powerd-client.c0000644000015301777760000001000512323077401021036 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include "powerd-internal.h" #include "log.h" struct powerd_client { const char *dbus_name; const char *name; gboolean ack_pending; }; static GHashTable *client_hash; static int acks_needed; static int ack_state; static void client_ack(struct powerd_client *client) { if (!client->ack_pending) return; client->ack_pending = FALSE; if (acks_needed <= 0) { powerd_error("Client needs ack while total acks needed is %d", acks_needed); return; } if (--acks_needed == 0) power_request_transition_acked(); } /* Must be called from main loop */ int powerd_client_register(const char *dbus_name, const char *name) { struct powerd_client *client; if (!dbus_name || !name) return -EINVAL; client = g_new0(struct powerd_client, 1); if (!client) return -ENOMEM; client->dbus_name = g_strdup(dbus_name); client->name = g_strdup(name); g_hash_table_insert(client_hash, (gpointer)client->dbus_name, client); powerd_dbus_name_watch_add(dbus_name); return 0; } /* Must be called from main loop */ void powerd_client_unregister(const char *dbus_name) { struct powerd_client *client; client = g_hash_table_lookup(client_hash, dbus_name); if (!client) return; client_ack(client); powerd_dbus_name_watch_remove(client->dbus_name); g_hash_table_remove(client_hash, client->dbus_name); } /* * Must be called from main loop. Returns TRUE if caller should * wait for acks. */ gboolean powerd_client_transition_start(int state) { GHashTableIter iter; gpointer key, value; acks_needed = 0; ack_state = state; g_hash_table_iter_init(&iter, client_hash); while (g_hash_table_iter_next(&iter, &key, &value)) { struct powerd_client *client = value; client->ack_pending = TRUE; acks_needed++; } return acks_needed != 0; } /* Must be called from main loop */ void powerd_client_transition_finish(int state) { GHashTableIter iter; gpointer key, value; if (state != ack_state) { powerd_warn("Attempt to finish client transition with wrong state"); return; } if (acks_needed) return; acks_needed = 0; g_hash_table_iter_init(&iter, client_hash); while (g_hash_table_iter_next(&iter, &key, &value)) { struct powerd_client *client = value; if (client->ack_pending) { powerd_warn("Client %s (%s) failed to acknowledge state change", client->name, client->dbus_name); client->ack_pending = FALSE; } } } /* Must be called from main loop */ int powerd_client_ack(const char *dbus_name, int state) { struct powerd_client *client; if (state != ack_state) return -EINVAL; client = g_hash_table_lookup(client_hash, dbus_name); if (!client) return -ENODEV; client_ack(client); return 0; } static void client_hash_destroy(gpointer data) { struct powerd_client *client = data; g_free((gpointer)client->dbus_name); g_free((gpointer)client->name); g_free(client); } int powerd_client_init(void) { client_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, client_hash_destroy); return 0; } void powerd_client_deinit(void) { g_hash_table_destroy(client_hash); } powerd-0.14+14.04.20140415/src/CMakeLists.txt0000644000015301777760000000070512323077401020664 0ustar pbusernogroup00000000000000include(UseGdbusCodegen) include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(GDBUS_NAME powerd-dbus) set(POWERD_GENERATED_SOURCES ${GDBUS_NAME}.c ) set(POWERD_GENERATED_HEADERS ${GDBUS_NAME}.h ) add_gdbus_codegen( OUTFILES POWERD_GENERATED_SOURCES NAME ${GDBUS_NAME} SERVICE_XML ${CMAKE_CURRENT_SOURCE_DIR}/../data/com.canonical.powerd.xml ) ADD_CUSTOM_TARGET(dbus_bindings DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${POWERD_GENERATED_SOURCES}) powerd-0.14+14.04.20140415/src/autobrightness.c0000644000015301777760000002546212323077401021340 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include #include #include "powerd-internal.h" #include "device-config.h" #include "log.h" #include "spline.h" /* * Autobrightness Control Algorithm * * Automatic backlight adjustments should respond quickly to changes in * the ambient brightness without constantly adjusting for minor or * transient changes in ambient brightness. The following mechanisms are * employed to maintain this balance. * * EXPONENTIAL SMOOTHING * * Rather than processing incoming lux values directly, an exponential * moving average is calculated using an algorithm based off of the * formulation known as "Brown's simple exponential smoothing" [1]: * * S(n) = a * x(n) + (1 - a) * S(n - 1) * = S(n - 1) + a * (x(n) - S(n - 1)) * * where 0 <= a <= 1. To make smoothing time-based, a is calculated * based on the amount of time spent at a given lux value. * * delta_t = t(n) - t(n - 1) * a(n) = delta_t / (C + delta_t) * * This gives decreasing weight to older samples. Smaller values of C * will cause faster decay of old samples, giving a faster response * time, while larger values of C create slower decay and give more * filtering of transient events. * * This code usese two moving averages, one which responds slowly to * changes and another which responds quickly. The slow average is used * to detect the trend with less sensitivity to transient events, while * the fast average is used for picking the brightness once we've * decided to change it. * * HYSTERESIS * * To avoid making constant adjustments to screen brightness in response * to small changes in ambient lighting conditions, the change in lux is * required to exceed some threshold before changing screen brightness. * * DEBOUNCING * * Changes in ambient brightness which exceed the hysteresis threshold * must do so for some minimum amount of time before any changes in * screen brightness will be made. This "debounces" the incoming lux * values and also helps to filter out transient changes in ambient * conditions. * * [1] http://en.wikipedia.org/wiki/Exponential_smoothing */ #define SMOOTHING_FACTOR_SLOW 2000.0f #define SMOOTHING_FACTOR_FAST 200.0f #define HYSTERESIS_FACTOR 0.10f #define DEBOUNCE_MS 4000 enum ab_state { AB_STATE_DISABLED, AB_STATE_INITIALIZING, AB_STATE_IDLE, /* Idle, nothing to do */ AB_STATE_DEBOUNCE, /* Debouncing lux change */ }; struct ab_status { enum ab_state state; guint source_id; double last_lux, applied_lux; double average_slow, average_fast; gint64 last_lux_ms; struct ab_spline *spline; }; static gboolean ab_supported = FALSE; static struct ab_status ab_status; struct spline *ab_spline; static gint64 get_ms(void) { return g_get_monotonic_time() / 1000; } static double aggregate_lux(double old_average, double new_lux, double smoothing_factor, double time_delta) { double alpha; if (time_delta < 0.0f) time_delta = 0.0f; alpha = time_delta / (smoothing_factor + time_delta); return old_average + alpha * (new_lux - old_average); } static enum ab_state process_state_debounce(guint *delay_ms) { gint64 now; double time_delta; double slow_lux, fast_lux; double hysteresis, slow_delta, fast_delta; enum ab_state next; next = AB_STATE_IDLE; *delay_ms = 0; now = get_ms(); time_delta = (double)(now - ab_status.last_lux_ms); slow_lux = aggregate_lux(ab_status.average_slow, ab_status.last_lux, SMOOTHING_FACTOR_SLOW, time_delta); fast_lux = aggregate_lux(ab_status.average_fast, ab_status.last_lux, SMOOTHING_FACTOR_FAST, time_delta); powerd_debug("%" PRId64 " slow avg %f fast avg %f last %f", now, slow_lux, fast_lux, ab_status.last_lux); /* * Require that both slow and fast averages exceed hysteresis, * and in the same direction. */ hysteresis = ab_status.applied_lux * HYSTERESIS_FACTOR; if (hysteresis < 2) hysteresis = 2; slow_delta = slow_lux - ab_status.applied_lux; fast_delta = fast_lux - ab_status.applied_lux; if ((slow_delta >= hysteresis && fast_delta >= hysteresis) || (-slow_delta >= hysteresis && -fast_delta >= hysteresis)) { int brightness = (int)(spline_interpolate(ab_spline, fast_lux) + 0.5); powerd_debug("set brightness %d", brightness); powerd_set_brightness(brightness); ab_status.applied_lux = fast_lux; } hysteresis = ab_status.last_lux * HYSTERESIS_FACTOR; if (hysteresis < 2) hysteresis = 2; if (fabs(fast_lux - ab_status.last_lux) >= hysteresis) { /* * Average should settle near the last reported lux. If * it hasn't made it there yet, continue debouncing. */ next = AB_STATE_DEBOUNCE; *delay_ms = DEBOUNCE_MS; } return next; } static gboolean ab_process(gpointer unused) { guint delay = 0; ab_status.source_id = 0; switch (ab_status.state) { case AB_STATE_DISABLED: case AB_STATE_INITIALIZING: case AB_STATE_IDLE: /* Nothing to do */ break; case AB_STATE_DEBOUNCE: ab_status.state = process_state_debounce(&delay); break; default: powerd_warn("Unexpected autobrightness state %d\n", ab_status.state); ab_status.state = AB_STATE_IDLE; break; } if (delay != 0 && ab_status.state != AB_STATE_IDLE) ab_status.source_id = g_timeout_add(delay, ab_process, NULL); return FALSE; } static gboolean handle_new_lux(gpointer data) { gint64 now; double lux; if (!ab_supported) return FALSE; now = get_ms(); lux = *(double *)data; g_free(data); if (ab_status.state == AB_STATE_DISABLED) return FALSE; if (ab_status.state == AB_STATE_INITIALIZING) { int brightness = (int)(spline_interpolate(ab_spline, lux) + 0.5); powerd_debug("set brightness %d", brightness); powerd_set_brightness(brightness); ab_status.average_slow = lux; ab_status.average_fast = lux; ab_status.applied_lux = lux; ab_status.state = AB_STATE_IDLE; } else { double time_delta; /* Ignore duplicates */ if (lux == ab_status.last_lux) return FALSE; time_delta = (double)(now - ab_status.last_lux_ms); ab_status.average_slow = aggregate_lux(ab_status.average_slow, lux, SMOOTHING_FACTOR_SLOW, time_delta); ab_status.average_fast = aggregate_lux(ab_status.average_fast, lux, SMOOTHING_FACTOR_FAST, time_delta); } ab_status.last_lux = lux; ab_status.last_lux_ms = now; if (ab_status.state == AB_STATE_IDLE) { ab_status.state = AB_STATE_DEBOUNCE; ab_status.source_id = g_timeout_add(DEBOUNCE_MS, ab_process, NULL); } return FALSE; } void powerd_new_als_event(double lux) { double *p_lux = g_memdup(&lux, sizeof(lux)); g_timeout_add(0, handle_new_lux, p_lux); } /* Must be run from main loop */ void powerd_autobrightness_enable(void) { /* Must not leave disabled state if we can't map lux to brightness */ if (!ab_supported) return; if (ab_status.state == AB_STATE_DISABLED) { ab_status.state = AB_STATE_INITIALIZING; powerd_sensors_als_enable(); } } /* Must be run from main loop */ void powerd_autobrightness_disable(void) { if (ab_status.state != AB_STATE_DISABLED) { ab_status.state = AB_STATE_DISABLED; powerd_sensors_als_disable(); if (ab_status.source_id != 0) g_source_remove(ab_status.source_id); } } gboolean powerd_autobrightness_available(void) { return ab_supported; } int powerd_autobrightness_init(void) { GValue v = G_VALUE_INIT; GArray *levels = NULL, *lux = NULL; double (*mappings)[2] = NULL; int i; int ret = -ENODEV; if (device_config_get("automatic_brightness_available", &v)) { powerd_info("Could not determine platform autobrightness capability, disabling"); goto error; } if (!G_VALUE_HOLDS_BOOLEAN(&v) || !g_value_get_boolean(&v)) { powerd_info("Platform does not support autobrightness, disabling"); goto error; } g_value_unset(&v); if (device_config_get("autoBrightnessLevels", &v)) { powerd_info("No device-specific autobrightness lux table defined, disabling"); goto error; } if (G_VALUE_HOLDS_BOXED(&v)) lux = g_value_dup_boxed(&v); g_value_unset(&v); if (!lux || lux->len == 0) { powerd_info("Invalid autobrighness lux levels, disabling"); goto error; } if (device_config_get("autoBrightnessLcdBacklightValues", &v)) { powerd_info("No device-specific autobrightness backlight levels defined, disabling"); goto error; } if (G_VALUE_HOLDS_BOXED(&v)) levels = g_value_dup_boxed(&v); g_value_unset(&v); if (!levels || levels->len == 0) { powerd_info("Invalid autobrighness backlight levels, disabling"); goto error; } /* We should have one more backlight level than lux values */ if (levels->len != lux->len + 1) { powerd_info("Invalid lux->brightness mappings, autobrightness disabled"); goto error; } mappings = g_malloc(2 * sizeof(double) * levels->len); for (i = 0; i < levels->len; i++) { mappings[i][0] = (double)((i == 0) ? 0 : g_array_index(lux, guint, i-1)); mappings[i][1] = (double)g_array_index(levels, guint, i); } ab_status.state = AB_STATE_DISABLED; ab_spline = spline_new(mappings, levels->len); if (!ab_spline) { ret = -ENOMEM; goto error; } g_free(mappings); ab_supported = TRUE; return 0; error: if (levels) g_array_unref(levels); if (lux) g_array_unref(lux); if (mappings) g_free(mappings); return ret; } void powerd_autobrightness_deinit(void) { powerd_autobrightness_disable(); ab_supported = FALSE; if (ab_spline) spline_free(ab_spline); } powerd-0.14+14.04.20140415/src/log.h0000644000015301777760000000251112323077401017053 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #ifndef __LOG_H__ #define __LOG_H__ #include #ifdef __cplusplus extern "C" { #endif #define POWERD_LOG_ERROR LOG_ERR #define POWERD_LOG_WARN LOG_WARNING #define POWERD_LOG_INFO LOG_INFO #define POWERD_LOG_DEBUG LOG_DEBUG #define powerd_error(f, a...) powerd_log(POWERD_LOG_ERROR, f, ##a) #define powerd_warn(f, a...) powerd_log(POWERD_LOG_WARN, f, ##a) #define powerd_info(f, a...) powerd_log(POWERD_LOG_INFO, f, ##a) #define powerd_debug(f, a...) powerd_log(POWERD_LOG_DEBUG, f, ##a) void powerd_log(int level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); void powerd_log_init(void); #ifdef __cplusplus } #endif #endif /* __LOG_H__ */ powerd-0.14+14.04.20140415/src/powerd-object.c0000644000015301777760000003656512323077410021051 0ustar pbusernogroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of powerd. * * powerd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * powerd 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 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 . */ #include #include #include #include #include #include #include "powerd-internal.h" #include "powerd-dbus.h" #include "log.h" struct _PowerdSourcePrivate { GDBusConnection * system_bus; const gchar * path; ComCanonicalPowerd * skel; }; static void powerd_source_class_init (PowerdSourceClass * klass); static void powerd_source_init (PowerdSource * self); static void powerd_source_dispose (GObject * object); static void powerd_source_finalize (GObject * object); G_DEFINE_TYPE (PowerdSource, powerd_source, G_TYPE_OBJECT); #define POWERD_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), POWERD_TYPE_SOURCE, PowerdSourcePrivate)) /* File-local reference to the singleton object */ static PowerdSource *powerd_source = NULL; /* This hash table is used to track which dbus names we are watching */ static GHashTable *dbus_name_watch_hash = NULL; /* Instance */ static void powerd_source_init (PowerdSource *self) { self->priv = POWERD_SOURCE_GET_PRIVATE(self); self->priv->system_bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); return; } /* Class Init */ static void powerd_source_class_init (PowerdSourceClass *self) { GObjectClass *object_class = G_OBJECT_CLASS (self); g_type_class_add_private (self, sizeof (PowerdSourcePrivate)); object_class->dispose = powerd_source_dispose; object_class->finalize = powerd_source_finalize; return; } /* Clean up references */ static void powerd_source_dispose (GObject *object) { PowerdSource * self = POWERD_SOURCE(object); if (self->priv->skel != NULL) { g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(self->priv->skel)); g_clear_object(&self->priv->skel); } g_clear_object(&self->priv->system_bus); return; } /* Free memory */ static void powerd_source_finalize (GObject *object) { return; } static gboolean handle_register_client(PowerdSource *obj, GDBusMethodInvocation *invocation, const gchar *name) { const char *owner; int ret; owner = g_dbus_method_invocation_get_sender(invocation); ret = powerd_client_register(owner, name); if (ret) g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Could not register client"); else g_dbus_method_invocation_return_value(invocation, NULL); return TRUE; } static gboolean handle_unregister_client(PowerdSource *obj, GDBusMethodInvocation *invocation, const gchar *name) { const char *owner = g_dbus_method_invocation_get_sender(invocation); powerd_client_unregister(owner); g_dbus_method_invocation_return_value(invocation, NULL); return TRUE; } static gboolean handle_ack_state_change(PowerdSource *obj, GDBusMethodInvocation *invocation, int state) { const char *owner; int ret; owner = g_dbus_method_invocation_get_sender(invocation); ret = powerd_client_ack(owner, state); if (ret) g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, ret == -EINVAL ? "Invalid state" : "Client not registered"); else g_dbus_method_invocation_return_value(invocation, NULL); return TRUE; } void powerd_bus_acquired_cb (GDBusConnection *connection, const gchar *name, gpointer user_data) { GError *error = NULL; powerd_debug("Bus acquired (guid %s)", g_dbus_connection_get_guid (connection)); powerd_source = (PowerdSource *)g_object_new(POWERD_TYPE_SOURCE, NULL); powerd_source->priv->skel = com_canonical_powerd_skeleton_new(); powerd_source->priv->path = "/com/canonical/powerd"; if (!g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(powerd_source->priv->skel), powerd_source->priv->system_bus, powerd_source->priv->path, &error)) { powerd_error("Unable to export skeleton on path '%s': %s", powerd_source->priv->path, error->message); g_error_free(error); error = NULL; powerd_exit(-1); } g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-request-sys-state", G_CALLBACK(handle_request_sys_state), powerd_source); g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-clear-sys-state", G_CALLBACK(handle_clear_sys_state), powerd_source); g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-list-sys-requests", G_CALLBACK(handle_list_sys_requests), powerd_source); g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-request-display-state", G_CALLBACK(handle_add_display_request), powerd_source); g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-update-display-state", G_CALLBACK(handle_update_display_request), powerd_source); g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-clear-display-state", G_CALLBACK(handle_clear_display_request), powerd_source); g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-register-client", G_CALLBACK(handle_register_client), powerd_source); g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-unregister-client", G_CALLBACK(handle_unregister_client), powerd_source); g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-ack-state-change", G_CALLBACK(handle_ack_state_change), powerd_source); g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-list-display-requests", G_CALLBACK(handle_list_display_requests), powerd_source); g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-get-sys-request-stats", G_CALLBACK(handle_get_sys_request_stats), powerd_source); g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-get-disp-request-stats", G_CALLBACK(handle_get_disp_request_stats), powerd_source); g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-user-autobrightness-enable", G_CALLBACK(handle_user_autobrightness_enable), powerd_source); g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-get-brightness-params", G_CALLBACK(handle_get_brightness_params), powerd_source); g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-set-user-brightness", G_CALLBACK(handle_set_user_brightness), powerd_source); powerd_dbus_init_complete(); } void powerd_name_acquired_cb (GDBusConnection *connection, const gchar *name, gpointer user_data) { powerd_debug("name acquired (%s)", name); } void powerd_name_lost_cb (GDBusConnection *connection, const gchar *name, gpointer user_data) { fprintf(stderr, "dbus name lost or unable to acquire dbus name, is another copy of powerd running?\n"); powerd_exit(-2); } void powerd_sys_state_signal_emit (enum SysPowerStates state) { GError *error = NULL; /* Make sure dbus has been set up */ if (!powerd_source) return; powerd_debug("Emitting signal for transition to state %s (%d)", state_to_string(state), state); g_dbus_connection_emit_signal( POWERD_SOURCE_GET_PRIVATE(powerd_source)->system_bus, NULL, /* destination */ "/com/canonical/powerd", "com.canonical.powerd", "SysPowerStateChange", g_variant_new("(i)", state), &error); if (error) { powerd_warn("Unable to signal a state change update: %s", error->message); g_error_free(error); } } void powerd_display_state_signal_emit(enum powerd_display_state state, guint32 flags) { GError *error = NULL; /* Make sure dbus has been set up */ if (!powerd_source) return; powerd_debug("Emitting signal for display state change: state=%d flags=0x%08x", state, flags); g_dbus_connection_emit_signal( POWERD_SOURCE_GET_PRIVATE(powerd_source)->system_bus, NULL, /* destination */ "/com/canonical/powerd", "com.canonical.powerd", "DisplayPowerStateChange", g_variant_new("(iu)", state, flags), &error); if (error) { powerd_warn("Unable to signal a state change update: %s", error->message); g_error_free(error); } } void ofono_manager_proxy_connect_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; GDBusProxy *ofono_proxy; ofono_proxy = g_dbus_proxy_new_finish(res, &error); if (error) { powerd_warn("%s failed: %s", __func__, error->message); g_error_free(error); return; } /* Register for insertion and removal of modems */ g_signal_connect(ofono_proxy, "g-signal", G_CALLBACK(on_ofono_manager_signal), NULL); /* Get current modems */ g_dbus_proxy_call(ofono_proxy, "GetModems", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, ofono_get_modems_cb, NULL); } void ofono_voicecall_proxy_connect_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; GDBusProxy *ofono_proxy; struct call_data *call; ofono_proxy = g_dbus_proxy_new_finish(res, &error); if (error) { powerd_warn("%s failed: %s", __func__, error->message); g_error_free(error); return; } call = user_data; call->ofono_proxy = ofono_proxy; /* Register for voicecall signals */ g_signal_connect(ofono_proxy, "g-signal", G_CALLBACK(on_ofono_voicecall_signal), NULL); /* Get current voice call state */ g_dbus_proxy_call(ofono_proxy, "GetProperties", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, ofono_voicecall_get_props_cb, NULL); } void ofono_proxy_connect_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; GDBusProxy *ofono_proxy = NULL; const char *interface_name = ""; ofono_proxy = g_dbus_proxy_new_finish (res, &error); if (error) { powerd_warn("ofono_proxy_connect_cb failed: %s", error->message); g_error_free(error); } else { interface_name = g_dbus_proxy_get_interface_name(ofono_proxy); powerd_debug("ofono_proxy_connect_cb: proxy is %s", interface_name); if (!strcmp(interface_name, "org.ofono.VoiceCallManager")) { g_signal_connect(ofono_proxy, "g-signal", G_CALLBACK (on_ofono_voicecallmanager_signal), NULL); } else if (!strcmp(interface_name, "org.ofono.MessageManager")) { g_signal_connect(ofono_proxy, "g-signal", G_CALLBACK (on_ofono_messagemanager_signal), NULL); } else if (!strcmp(interface_name, "org.ofono.SupplementaryServices")) { g_signal_connect(ofono_proxy, "g-signal", G_CALLBACK (on_ofono_ussd_signal), NULL); } else { powerd_warn("unknown interface name for this proxy, ignoring"); } powerd_debug("ofono_proxy_connect_cb succeeded"); } } void powerd_name_vanished_cb(GDBusConnection *connection, const gchar *name, gpointer user_data) { powerd_warn("%s vanished from dbus, clearing associated requests", name); clear_sys_state_by_owner(name); clear_disp_state_by_owner(name); powerd_client_unregister(name); // This can return false if the hash entry was removed because the // last request was dropped, in other words, this call will fail // if the callers cleaned-up properly, so ignore any FALSE return // values. // Make sure this is called AFTER the clear_* calls to avoid spurious // warnings. g_hash_table_remove(dbus_name_watch_hash, name); } void powerd_dbus_name_watch_add(const char *owner) { struct DbusNameWatch *dbnw = NULL; char *hash_owner = NULL; if (strcmp(owner,"internal") == 0) { return; } /* XXX: For debug, remove later */ powerd_debug("name_watch_add: looking for %s", owner); dbnw = g_hash_table_lookup(dbus_name_watch_hash, owner); if (dbnw == NULL) { hash_owner = strdup(owner); powerd_debug("watching %s to see when it disappears on dbus", hash_owner); dbnw = g_new(struct DbusNameWatch, 1); dbnw->watch_id = g_bus_watch_name(G_BUS_TYPE_SYSTEM, hash_owner, G_BUS_NAME_WATCHER_FLAGS_NONE, NULL, (GBusNameVanishedCallback)powerd_name_vanished_cb, NULL, NULL); dbnw->ref_count = 1; g_hash_table_insert(dbus_name_watch_hash, hash_owner, dbnw); } else { dbnw->ref_count++; /* XXX: For debug, remove later */ powerd_debug("name_watch: ref_count for %s is now %d", owner, dbnw->ref_count); } } void powerd_dbus_name_watch_remove(const char *owner) { struct DbusNameWatch *dbnw = NULL; if (strcmp(owner,"internal") == 0) { return; } /* XXX: For debug, remove later */ powerd_debug("name_watch_remove: looking for %s", owner); dbnw = g_hash_table_lookup(dbus_name_watch_hash, owner); if (dbnw == NULL) { powerd_warn("removal request for a non-existant name: %s\n", owner); return; } else { dbnw->ref_count--; /* XXX: For debug, remove later */ powerd_debug("name_watch: ref_count for %s is now %d", owner, dbnw->ref_count); if (dbnw->ref_count <= 0) { powerd_debug("no longer watching %s, there are no more "\ "requests", owner); g_bus_unwatch_name(dbnw->watch_id); if (g_hash_table_remove(dbus_name_watch_hash, (char *)owner) == FALSE) { powerd_warn("could not remove %s from dbus_name_watch_hash", owner); } } } } /* Destructor for key */ static void dbus_name_watch_key_destroy(gpointer data) { if (data) g_free(data); } void dbus_name_watch_init(void) { dbus_name_watch_hash = g_hash_table_new_full(g_str_hash, g_str_equal, dbus_name_watch_key_destroy, NULL); } void dbus_name_watch_deinit(void) { g_hash_table_destroy(dbus_name_watch_hash); }