From 4e7f9709ec47547f903705c086dd1fd5cfd4315e Mon Sep 17 00:00:00 2001 From: Simo Piiroinen Date: Mon, 7 Nov 2016 10:01:23 +0200 Subject: [PATCH] [dsme] Make device state tracking asynchronous. MER#1694 Usb-moded is making repeated blocking device state queries even when the component providing the service is not running. Use cached device state and keep it up to date fully asynchronously by tracking dsme service availability and state changes it signals. Signed-off-by: Simo Piiroinen --- src/usb_moded-dbus.c | 2 +- src/usb_moded-dsme.c | 370 ++++++++++++++++++++++++++++++++++++++----- src/usb_moded-dsme.h | 7 +- src/usb_moded.c | 7 + 4 files changed, 347 insertions(+), 39 deletions(-) diff --git a/src/usb_moded-dbus.c b/src/usb_moded-dbus.c index 2dd40df..137e0d3 100644 --- a/src/usb_moded-dbus.c +++ b/src/usb_moded-dbus.c @@ -689,7 +689,7 @@ static void usb_moded_get_name_owner_cb(DBusPendingCall *pc, void *aptr) DBUS_TYPE_INVALID) ) { if( strcmp(err.name, DBUS_ERROR_NAME_HAS_NO_OWNER) ) - log_warning("parse error: %s: %s", err.name, err.message); + log_err("parse error: %s: %s", err.name, err.message); goto EXIT; } diff --git a/src/usb_moded-dsme.c b/src/usb_moded-dsme.c index fbb2ec1..0b04414 100644 --- a/src/usb_moded-dsme.c +++ b/src/usb_moded-dsme.c @@ -1,20 +1,21 @@ /** @file usb_moded-dsme.c - Copyright (C) 2013 Jolla. All rights reserved. + Copyright (C) 2013-2016 Jolla. All rights reserved. @author: Philippe De Swert @author: Jonni Rainisto + @author: Simo Piiroinen This program is free software; you can redistribute it and/or - modify it under the terms of the Lesser GNU General Public License - version 2 as published by the Free Software Foundation. + modify it under the terms of the Lesser GNU General Public License + version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 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 Lesser GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA @@ -25,46 +26,343 @@ #include #include "usb_moded-dsme.h" +#include "usb_moded-dbus-private.h" #include "usb_moded-log.h" +/* ========================================================================= * + * dsme dbus constants + * ========================================================================= */ + +#define DSME_DBUS_SERVICE "com.nokia.dsme" + +#define DSME_DBUS_REQUEST_PATH "/com/nokia/dsme/request" +#define DSME_DBUS_REQUEST_IFACE "com.nokia.dsme.request" +#define DSME_DBUS_GET_STATE_REQ "get_state" + +#define DSME_DBUS_SIGNAL_PATH "/com/nokia/dsme/signal" +#define DSME_DBUS_SIGNAL_IFACE "com.nokia.dsme.signal" +#define DSME_STATE_CHANGE_SIG "state_change_ind" + +#define DSME_STATE_CHANGE_MATCH\ + "type='signal'"\ + ",interface='"DSME_DBUS_SIGNAL_IFACE"'"\ + ",member='"DSME_STATE_CHANGE_SIG"'" + +#define DSME_OWNER_CHANGE_MATCH\ + "type='signal'"\ + ",interface='"DBUS_INTERFACE_DBUS"'"\ + ",member='"DBUS_NAME_OWNER_CHANGED_SIG"'"\ + ",arg0='"DSME_DBUS_SERVICE"'" + +/* ========================================================================= * + * state data + * ========================================================================= */ + +/* SystemBus connection ref used for dsme ipc */ +static DBusConnection *dsme_con = NULL; + +/* Flag for: device state == "USER" */ +static gboolean in_user_state = FALSE; + +/* Flag for: dsme is available on system bus */ +static gboolean dsme_is_available = FALSE; + /** Checks if the device is is USER-state. * * @return 1 if it is in USER-state, 0 for not * */ -int is_in_user_state(void) +gboolean is_in_user_state(void) +{ + return in_user_state; +} + +/* ========================================================================= * + * device state queries + * ========================================================================= */ + +static void device_state_changed(const char *state) +{ + gboolean to_user_state = state && !strcmp(state, "USER"); + + log_debug("device state: %s", state ?: "(null)"); + + if( in_user_state != to_user_state ) { + in_user_state = to_user_state; + log_debug("in user state: %s", + in_user_state ? "true" : "false"); + } +} + +static DBusPendingCall *device_state_query_pc = 0; + +static void device_state_cancel(void) +{ + if( device_state_query_pc ) { + dbus_pending_call_cancel(device_state_query_pc); + dbus_pending_call_unref(device_state_query_pc), + device_state_query_pc = 0; + } +} + +static void device_state_query_cb(DBusPendingCall *pending, void *aptr) +{ + DBusMessage *rsp = 0; + const char *dta = 0; + DBusError err = DBUS_ERROR_INIT; + + (void)aptr; + + if( !(rsp = dbus_pending_call_steal_reply(pending)) ) { + log_err("did not get reply"); + goto EXIT; + } + + if( dbus_set_error_from_message(&err, rsp) ) + { + log_err("error reply: %s: %s", err.name, err.message); + goto EXIT; + } + + if( !dbus_message_get_args(rsp, &err, + DBUS_TYPE_STRING, &dta, + DBUS_TYPE_INVALID) ) + { + if( strcmp(err.name, DBUS_ERROR_NAME_HAS_NO_OWNER) ) + log_err("parse error: %s: %s", err.name, err.message); + goto EXIT; + } + + device_state_changed(dta); + +EXIT: + + if( rsp ) dbus_message_unref(rsp); + + dbus_error_free(&err); + + dbus_pending_call_unref(device_state_query_pc), + device_state_query_pc = 0; +} + +static void device_state_query(void) { - DBusConnection *dbus_conn = NULL; - DBusMessage *msg = NULL, *reply = NULL; - DBusError error; - int ret = 0; - char* buffer = NULL; - - dbus_error_init(&error); - - if( (dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error)) == 0 ) - { - log_err("Could not connect to dbus systembus (is_in_user_state)\n"); - /* dbus system bus is broken or not there, so assume not in USER state */ - return(ret); - } - - if ((msg = dbus_message_new_method_call("com.nokia.dsme", "/request", "com.nokia.dsme.request", "get_state")) != NULL) - { - if ((reply = dbus_connection_send_with_reply_and_block(dbus_conn, msg, -1, NULL)) != NULL) + DBusMessage *req = NULL; + DBusPendingCall *pc = 0; + + device_state_cancel(); + + if( !dsme_con ) { + log_err("not connected to system bus; skip device state query"); + goto EXIT; + } + + req = dbus_message_new_method_call(DSME_DBUS_SERVICE, + DSME_DBUS_REQUEST_PATH, + DSME_DBUS_REQUEST_IFACE, + DSME_DBUS_GET_STATE_REQ); + if( !req ) { + log_err("failed to construct %s.%s request", + DSME_DBUS_REQUEST_IFACE, + DSME_DBUS_GET_STATE_REQ); + goto EXIT; + } + + if( !dbus_connection_send_with_reply(dsme_con, req, &pc, -1) ) + goto EXIT; + + if( !pc ) + goto EXIT; + + if( !dbus_pending_call_set_notify(pc, device_state_query_cb, 0, 0) ) + goto EXIT; + + device_state_query_pc = pc, pc = 0; + +EXIT: + + if( pc ) dbus_pending_call_unref(pc); + if( req ) dbus_message_unref(req); +} + +static void device_state_signal(DBusMessage *msg) +{ + DBusError err = DBUS_ERROR_INIT; + const char *dta = 0; + + if( !dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &dta, + DBUS_TYPE_INVALID) ) { - dbus_message_get_args(reply, &error, DBUS_TYPE_STRING, &buffer, DBUS_TYPE_INVALID); - dbus_message_unref(reply); - } - dbus_message_unref(msg); - } - dbus_connection_unref(dbus_conn); - - if(buffer) - { - log_debug("user state = %s\n", buffer); - if (strcmp(buffer, "USER")==0) ret = 1; - } - return(ret); + log_err("failed to parse signal: %s: %s", + err.name, err.message); + } + else + { + device_state_changed(dta); + } + dbus_error_free(&err); } +/* ========================================================================= * + * dsme name owner tracking + * ========================================================================= */ + +static void dsme_available_changed(const char *owner) +{ + gboolean is_available = (owner && *owner); + + if( dsme_is_available != is_available ) { + dsme_is_available = is_available; + log_debug("dsme is %s", dsme_is_available ? "running" : "stopped"); + + /* Forget cached device state */ + device_state_changed("UNKNOWN"); + + /* Query current state on dsme startup */ + if( dsme_is_available ) { + device_state_query(); + } + } +} + +static DBusPendingCall *dsme_available_pc = 0; + +static void dsme_available_cb(const char *owner) +{ + dsme_available_changed(owner); + + dbus_pending_call_unref(dsme_available_pc), + dsme_available_pc = 0; +} + +static void dsme_available_cancel(void) +{ + if( dsme_available_pc ) + { + dbus_pending_call_cancel(dsme_available_pc); + dbus_pending_call_unref(dsme_available_pc), + dsme_available_pc = 0; + } +} + +static void dsme_available_query(void) +{ + dsme_available_cancel(); + + usb_moded_get_name_owner_async(DSME_DBUS_SERVICE, + dsme_available_cb, + &dsme_available_pc); +} + +static void name_owner_signal(DBusMessage *msg) +{ + DBusError err = DBUS_ERROR_INIT; + const char *name = 0; + const char *prev = 0; + const char *curr = 0; + + if( !dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &prev, + DBUS_TYPE_STRING, &curr, + DBUS_TYPE_INVALID) ) + { + log_err("failed to parse signal: %s: %s", + err.name, err.message); + } + else if( !strcmp(name, DSME_DBUS_SERVICE) ) + { + dsme_available_changed(curr); + } + dbus_error_free(&err); +} + +/* ========================================================================= * + * dbus message filter + * ========================================================================= */ + +static DBusHandlerResult +dsme_dbus_filter_cb(DBusConnection *con, DBusMessage *msg, void *user_data) +{ + (void)con; + (void)user_data; + + if( dbus_message_is_signal(msg, + DSME_DBUS_SIGNAL_IFACE, + DSME_STATE_CHANGE_SIG) ) + { + device_state_signal(msg); + } + else if( dbus_message_is_signal(msg, + DBUS_INTERFACE_DBUS, + DBUS_NAME_OWNER_CHANGED_SIG) ) + { + name_owner_signal(msg); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +/* ========================================================================= * + * start/stop dsme tracking + * ========================================================================= */ + +gboolean +dsme_listener_start(void) +{ + gboolean ack = FALSE; + + /* Get connection ref */ + if( (dsme_con = usb_moded_dbus_get_connection()) == 0 ) + { + log_err("Could not connect to dbus for dsme\n"); + goto cleanup; + } + + /* Add filter callback */ + if( !dbus_connection_add_filter(dsme_con, + dsme_dbus_filter_cb , 0, 0) ) + { + log_err("adding system dbus filter for dsme failed"); + goto cleanup; + } + + /* Add match without blocking / error checking */ + dbus_bus_add_match(dsme_con, DSME_STATE_CHANGE_MATCH, 0); + dbus_bus_add_match(dsme_con, DSME_OWNER_CHANGE_MATCH, 0); + + /* Initiate async dsme name owner query */ + dsme_available_query(); + + ack = TRUE; + +cleanup: + + return ack; +} + +void +dsme_listener_stop(void) +{ + /* Cancel pending dbus queries */ + dsme_available_cancel(); + device_state_cancel(); + + if(dsme_con) + { + /* Remove filter callback */ + dbus_connection_remove_filter(dsme_con, + dsme_dbus_filter_cb, 0); + + if( dbus_connection_get_is_connected(dsme_con) ) { + /* Remove match without blocking / error checking */ + dbus_bus_remove_match(dsme_con, DSME_STATE_CHANGE_MATCH, 0); + dbus_bus_remove_match(dsme_con, DSME_OWNER_CHANGE_MATCH, 0); + } + + /* Let go of connection ref */ + dbus_connection_unref(dsme_con), + dsme_con = 0; + } +} diff --git a/src/usb_moded-dsme.h b/src/usb_moded-dsme.h index c64da19..33d87cf 100644 --- a/src/usb_moded-dsme.h +++ b/src/usb_moded-dsme.h @@ -1,9 +1,10 @@ /** @file usb_moded-dsme.h - Copyright (C) 2013 Jolla. All rights reserved. + Copyright (C) 2013-2016 Jolla. All rights reserved. @author: Philippe De Swert + @author: Simo Piiroinen This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License @@ -20,4 +21,6 @@ 02110-1301 USA */ -int is_in_user_state(void); +gboolean is_in_user_state(void); +gboolean dsme_listener_start(void); +void dsme_listener_stop(void); diff --git a/src/usb_moded.c b/src/usb_moded.c index ca414a0..467f4ed 100644 --- a/src/usb_moded.c +++ b/src/usb_moded.c @@ -38,6 +38,7 @@ #include "usb_moded.h" #include "usb_moded-modes.h" +#include "usb_moded-dsme.h" #include "usb_moded-dbus.h" #include "usb_moded-dbus-private.h" #include "usb_moded-hw-ab.h" @@ -1191,6 +1192,11 @@ int main(int argc, char* argv[]) goto EXIT; } + if( !dsme_listener_start() ) { + log_crit("dsme tracking could not be started"); + goto EXIT; + } + /* init daemon into a clean state first, then dbus and hw_abstraction last */ usb_moded_init(); @@ -1244,6 +1250,7 @@ int main(int argc, char* argv[]) result = EXIT_SUCCESS; g_main_loop_run(mainloop); EXIT: + dsme_listener_stop(); handle_exit(); allow_suspend();