From d72c59aec0c0d65625d1118988016b6a3da0a152 Mon Sep 17 00:00:00 2001 From: Simo Piiroinen Date: Mon, 7 Nov 2016 09:57:13 +0200 Subject: [PATCH] [devicelock] Make devicelock state tracking asynchronous. MER#1694 Usb-moded is making repeated blocking devicelock state queries even when the component providing the service is not running. Use cached devicelock state and keep it up to date fully asynchronously by tracking devicelock service availability and state changes it signals. Signed-off-by: Simo Piiroinen --- src/usb_moded-devicelock.c | 477 +++++++++++++++++++++++++++++-------- src/usb_moded-devicelock.h | 32 ++- src/usb_moded-lock.h | 9 +- 3 files changed, 401 insertions(+), 117 deletions(-) diff --git a/src/usb_moded-devicelock.c b/src/usb_moded-devicelock.c index 330ba5b..d9ad5bd 100644 --- a/src/usb_moded-devicelock.c +++ b/src/usb_moded-devicelock.c @@ -1,28 +1,34 @@ /** - + @file: usb_moded-devicelock.c - + Copyright (C) 2010 Nokia Corporation. All rights reserved. + Copyright (C) 2013-2016 Jolla Ltd. @author: Philippe De Swert + @author: Philippe De Swert + @author: Jonni Rainisto + @author: Vesa Halttunen + @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 02110-1301 USA */ + /* - * Interacts with the devicelock to know if we can expose the system contents or not -*/ + * Interacts with the devicelock to know if we can expose the system contents or not + */ /*============================================================================= */ @@ -36,122 +42,389 @@ #include "usb_moded-log.h" #include "usb_moded.h" #include "usb_moded-modes.h" +#include "usb_moded-dbus-private.h" + +/* ========================================================================= * + * devicelock state type + * ========================================================================= */ + +/** Devicelock states */ +typedef enum +{ + /** Devicelock is not active */ + DEVICE_LOCK_UNLOCKED = 0, + + /** Devicelock is active */ + DEVICE_LOCK_LOCKED = 1, + + /** Initial startup value; from usb-moded p.o.v. equals locked */ + DEVICE_LOCK_UNDEFINED = 2, +} devicelock_state_t; + +/** Return human readable representation of devicelock state enum + */ +static const char * +devicelock_state_repr(devicelock_state_t state) +{ + const char *repr = "DEVICE_LOCK_"; -static DBusHandlerResult devicelock_unlocked_cb(DBusConnection *conn, DBusMessage *msg, void *user_data); + switch( state ) + { + case DEVICE_LOCK_UNLOCKED: repr = "DEVICE_LOCK_UNLOCKED"; break; + case DEVICE_LOCK_LOCKED: repr = "DEVICE_LOCK_LOCKED"; break; + case DEVICE_LOCK_UNDEFINED: repr = "DEVICE_LOCK_UNDEFINED"; break; + default: break; + } + + return repr; +} + +/* ========================================================================= * + * module state data + * ========================================================================= */ + +/* SystemBus connection ref used for devicelock ipc */ +static DBusConnection *devicelock_con = NULL; + +/* Cached devicelock state */ +static devicelock_state_t device_lock_state = DEVICE_LOCK_UNDEFINED; + +/* Flag for: devicelock is available on system bus */ +static gboolean devicelock_is_available = FALSE; + +/* ========================================================================= * + * functionality provided to the other modules + * ========================================================================= */ -DBusConnection *dbus_conn_devicelock = NULL; - /** Checks if the device is locked. - * + * * @return 0 for unlocked, 1 for locked * */ int usb_moded_get_export_permission(void) { - DBusConnection *dbus_conn_devicelock = NULL; - DBusMessage *msg = NULL, *reply = NULL; - DBusError error; - int ret = 2; - - dbus_error_init(&error); - - if( (dbus_conn_devicelock = dbus_bus_get(DBUS_BUS_SYSTEM, &error)) == 0 ) - { - log_err("Could not connect to dbus for devicelock\n"); - } - - if ((msg = dbus_message_new_method_call(DEVICELOCK_SERVICE, DEVICELOCK_REQUEST_PATH, DEVICELOCK_REQUEST_IF, DEVICELOCK_STATE_REQ)) != NULL) - { - /* default dbus timeout is too long, timeout after 1,5 seconds */ - if ((reply = dbus_connection_send_with_reply_and_block(dbus_conn_devicelock, msg, 1500, NULL)) != NULL) - { - dbus_message_get_args(reply, NULL, DBUS_TYPE_INT32, &ret, DBUS_TYPE_INVALID); - dbus_message_unref(reply); - } - dbus_message_unref(msg); - } - dbus_connection_unref(dbus_conn_devicelock); - - log_debug("devicelock state = %d\n", ret); - return(ret); + gboolean unlocked = (device_lock_state == DEVICE_LOCK_UNLOCKED); + + // FIXME: this is reverse from what function name makes to expect + + return unlocked ? 0 : 1; } -int start_devicelock_listener(void) +/* ========================================================================= * + * devicelock state queries + * ========================================================================= */ + +static void devicelock_state_changed(devicelock_state_t state) { - DBusError err = DBUS_ERROR_INIT; + if( device_lock_state != state ) { + log_debug("devicelock state: %s -> %s", + devicelock_state_repr(device_lock_state), + devicelock_state_repr(state)); + device_lock_state = state; - if( (dbus_conn_devicelock = dbus_bus_get(DBUS_BUS_SYSTEM, &err)) == 0 ) - { - log_err("Could not connect to dbus for devicelock\n"); - goto cleanup; - } + if( device_lock_state == DEVICE_LOCK_UNLOCKED && + get_usb_connection_state() == 1 ) + { + const char *usb_mode = get_usb_mode(); + log_debug("usb_mode %s\n", usb_mode); - dbus_bus_add_match(dbus_conn_devicelock, MATCH_DEVICELOCK_SIGNALS, &err); - if( dbus_error_is_set(&err) ) - { - goto cleanup; - } - if( !dbus_connection_add_filter(dbus_conn_devicelock, devicelock_unlocked_cb , 0, 0) ) - { - log_err("adding system dbus filter for devicelock failed"); - goto cleanup; - } - //dbus_connection_setup_with_g_main(dbus_conn_devicelock, NULL); + /* if the mode is MODE_CHARGING_FALLBACK we know the user + * has not selected any mode, in case it things are still + * undefined it cannot hurt to try again to set a mode */ + if(!strcmp(usb_mode, MODE_UNDEFINED) || + !strcmp(usb_mode, MODE_CHARGING_FALLBACK)) + { + log_debug("set_usb"); + set_usb_connected_state(); + } + } + } +} -cleanup: - dbus_error_free(&err); - return(1); +static DBusPendingCall *devicelock_state_query_pc = 0; + +static void devicelock_state_cancel(void) +{ + if( devicelock_state_query_pc ) { + dbus_pending_call_cancel(devicelock_state_query_pc); + dbus_pending_call_unref(devicelock_state_query_pc), + devicelock_state_query_pc = 0; + } } -int stop_devicelock_listener(void) +static void devicelock_state_query_cb(DBusPendingCall *pending, void *aptr) { - if(dbus_conn_devicelock) - { - dbus_connection_remove_filter(dbus_conn_devicelock, devicelock_unlocked_cb, NULL); - dbus_bus_remove_match(dbus_conn_devicelock, MATCH_DEVICELOCK_SIGNALS, NULL); - dbus_connection_unref(dbus_conn_devicelock); - } + DBusMessage *rsp = 0; + dbus_int32_t dta = DEVICE_LOCK_UNDEFINED; + DBusError err = DBUS_ERROR_INIT; + + (void)aptr; + + if( !(rsp = dbus_pending_call_steal_reply(pending)) ) + { + log_err("%s.%s: no reply", + DEVICELOCK_INTERFACE, DEVICELOCK_GET_STATE_REQ); + goto EXIT; + } + + if( dbus_set_error_from_message(&err, rsp) ) + { + log_err("%s.%s: error reply: %s: %s", + DEVICELOCK_INTERFACE, DEVICELOCK_GET_STATE_REQ, + err.name, err.message); + goto EXIT; + } - return 0; + if( !dbus_message_get_args(rsp, &err, DBUS_TYPE_INT32, &dta, + DBUS_TYPE_INVALID) ) + { + log_err("%s.%s: parse error: %s: %s", + DEVICELOCK_INTERFACE, DEVICELOCK_GET_STATE_REQ, + err.name, err.message); + goto EXIT; + } + +EXIT: + devicelock_state_changed(dta); + + if( rsp ) dbus_message_unref(rsp); + + dbus_error_free(&err); + + dbus_pending_call_unref(devicelock_state_query_pc), + devicelock_state_query_pc = 0; } -static DBusHandlerResult devicelock_unlocked_cb(DBusConnection *conn, DBusMessage *msg, void *user_data) -{ - /* regardless what happens, we want that other programs get a chance to process this */ - DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - const char *interface = dbus_message_get_interface(msg); - const char *member = dbus_message_get_member(msg); - const char *object = dbus_message_get_path(msg); - int type = dbus_message_get_type(msg); - int ret=0; - - (void) user_data; - - // sanity checks - if( !interface || !member || !object ) goto cleanup; - if( type != DBUS_MESSAGE_TYPE_SIGNAL ) goto cleanup; - if( strcmp(interface, DEVICELOCK_REQUEST_IF) ) goto cleanup; - if( strcmp(object, DEVICELOCK_REQUEST_PATH) ) goto cleanup; - - // handle known signals - else if( !strcmp(member, "stateChanged") ) - { - dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &ret, DBUS_TYPE_INVALID); - log_debug("Devicelock state changed. New state = %d\n", ret); - if(ret == 0 && get_usb_connection_state() == 1 ) - { - log_debug("usb_mode %s\n", get_usb_mode()); - /* if the mode is MODE_CHARGING_FALLBACK we know the user has not selected any mode, in case it - things are still undefined it cannot hurt to try again to set a mode */ - if(!strcmp(get_usb_mode(), MODE_UNDEFINED) || !strcmp(get_usb_mode(), MODE_CHARGING_FALLBACK)) - { - log_debug("set_usb"); - set_usb_connected_state(); - } - } - } +static void devicelock_state_query(void) +{ + DBusMessage *req = NULL; + DBusPendingCall *pc = 0; + + devicelock_state_cancel(); + + log_debug("querying device lock state"); + + if( !devicelock_con ) { + log_err("not connected to system bus; skip device state query"); + goto EXIT; + } + + req = dbus_message_new_method_call(DEVICELOCK_SERVICE, + DEVICELOCK_OBJECT, + DEVICELOCK_INTERFACE, + DEVICELOCK_GET_STATE_REQ); + + if( !req ) + { + log_err("%s.%s: failed to construct request", + DEVICELOCK_INTERFACE, DEVICELOCK_GET_STATE_REQ); + goto EXIT; + } + + if( !dbus_connection_send_with_reply(devicelock_con, req, &pc, -1) ) + goto EXIT; + + if( !pc ) + goto EXIT; + + if( !dbus_pending_call_set_notify(pc, devicelock_state_query_cb, 0, 0) ) + goto EXIT; + + devicelock_state_query_pc = pc, pc = 0; + +EXIT: + + if( pc ) dbus_pending_call_unref(pc); + if( req ) dbus_message_unref(req); +} + +static void devicelock_state_signal(DBusMessage *msg) +{ + DBusError err = DBUS_ERROR_INIT; + dbus_int32_t dta = DEVICE_LOCK_LOCKED; + + if( !dbus_message_get_args(msg, &err, + DBUS_TYPE_INT32, &dta, + DBUS_TYPE_INVALID) ) + { + log_err("failed to parse %s.%s signal: %s: %s", + DEVICELOCK_INTERFACE, DEVICELOCK_STATE_CHANGED_SIG, + err.name, err.message); + } + + devicelock_state_changed(dta); + + dbus_error_free(&err); +} + +/* ========================================================================= * + * devicelock name owner tracking + * ========================================================================= */ + +static void devicelock_available_changed(const char *owner) +{ + gboolean is_available = (owner && *owner); + + if( devicelock_is_available != is_available ) { + devicelock_is_available = is_available; + log_debug("devicelock is %s", + devicelock_is_available ? "running" : "stopped"); + + /* Forget cached device state */ + devicelock_state_changed(DEVICE_LOCK_UNDEFINED); + + /* Query current state on devicelock startup */ + if( devicelock_is_available ) { + devicelock_state_query(); + } + } +} + +static DBusPendingCall *devicelock_available_pc = 0; + +static void devicelock_available_cb(const char *owner) +{ + devicelock_available_changed(owner); + + dbus_pending_call_unref(devicelock_available_pc), + devicelock_available_pc = 0; +} + +static void devicelock_available_cancel(void) +{ + if( devicelock_available_pc ) + { + dbus_pending_call_cancel(devicelock_available_pc); + dbus_pending_call_unref(devicelock_available_pc), + devicelock_available_pc = 0; + } +} + +static void devicelock_available_query(void) +{ + devicelock_available_cancel(); + + log_debug("querying %s name owner", DEVICELOCK_SERVICE); + + usb_moded_get_name_owner_async(DEVICELOCK_SERVICE, + devicelock_available_cb, + &devicelock_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, DEVICELOCK_SERVICE) ) + { + devicelock_available_changed(curr); + } + dbus_error_free(&err); +} + +/* ========================================================================= * + * dbus message filter + * ========================================================================= */ + +static DBusHandlerResult +devicelock_dbus_filter_cb(DBusConnection *con, DBusMessage *msg, void *aptr) +{ + (void)con; + (void)aptr; + + if( dbus_message_is_signal(msg, + DEVICELOCK_INTERFACE, + DEVICELOCK_STATE_CHANGED_SIG) ) + { + devicelock_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 devicelock state tracking + * ========================================================================= */ + +gboolean +start_devicelock_listener(void) +{ + gboolean ack = FALSE; + + log_debug("starting devicelock listener"); + + /* Get connection ref */ + if( (devicelock_con = usb_moded_dbus_get_connection()) == 0 ) + { + log_err("Could not connect to dbus for devicelock\n"); + goto cleanup; + } + + /* Add filter callback */ + if( !dbus_connection_add_filter(devicelock_con, + devicelock_dbus_filter_cb , 0, 0) ) + { + log_err("adding system dbus filter for devicelock failed"); + goto cleanup; + } + + /* Add match without blocking / error checking */ + dbus_bus_add_match(devicelock_con, DEVICELOCK_STATE_CHANGED_MATCH,0); + dbus_bus_add_match(devicelock_con, DEVICELOCK_NAME_OWNER_CHANGED_MATCH,0); + + /* Initiate async devicelock name owner query */ + devicelock_available_query(); + + ack = TRUE; cleanup: - return result; + + return ack; } +void +stop_devicelock_listener(void) +{ + log_debug("stopping devicelock listener"); + + /* Do note leave pending queries behind */ + devicelock_state_cancel(); + devicelock_available_cancel(); + + if(devicelock_con) + { + /* Remove filter callback */ + dbus_connection_remove_filter(devicelock_con, + devicelock_dbus_filter_cb, 0); + + if( dbus_connection_get_is_connected(devicelock_con) ) { + /* Remove match without blocking / error checking */ + dbus_bus_remove_match(devicelock_con, + DEVICELOCK_STATE_CHANGED_MATCH, 0); + dbus_bus_remove_match(devicelock_con, + DEVICELOCK_NAME_OWNER_CHANGED_MATCH, 0); + } + + /* Let go of connection ref */ + dbus_connection_unref(devicelock_con), + devicelock_con = 0; + } +} diff --git a/src/usb_moded-devicelock.h b/src/usb_moded-devicelock.h index dc4f61b..8143320 100644 --- a/src/usb_moded-devicelock.h +++ b/src/usb_moded-devicelock.h @@ -1,7 +1,10 @@ /* Copyright (C) 2010 Nokia Corporation. All rights reserved. + Copyright (C) 2013-2016 Jolla Ltd. Author: Philippe De Swert + Author: Vesa Halttunen + 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 @@ -23,15 +26,20 @@ /*============================================================================= */ -#define DEVICELOCK_SERVICE "org.nemomobile.lipstick" -#define DEVICELOCK_REQUEST_PATH "/devicelock" -#define DEVICELOCK_REQUEST_IF "org.nemomobile.lipstick.devicelock" -#define DEVICELOCK_STATE_REQ "state" - -#define DEVICELOCK_LOCKED "Locked" - -#define MATCH_DEVICELOCK_SIGNALS\ - "type='signal'"\ - ",interface='"DEVICELOCK_REQUEST_IF"'"\ - ",path='"DEVICELOCK_REQUEST_PATH"'" - +#define DEVICELOCK_SERVICE "org.nemomobile.lipstick" +#define DEVICELOCK_OBJECT "/devicelock" +#define DEVICELOCK_INTERFACE "org.nemomobile.lipstick.devicelock" +#define DEVICELOCK_GET_STATE_REQ "state" +#define DEVICELOCK_STATE_CHANGED_SIG "stateChanged" + +#define DEVICELOCK_STATE_CHANGED_MATCH\ + "type='signal'"\ + ",interface='"DEVICELOCK_INTERFACE"'"\ + ",path='"DEVICELOCK_OBJECT"'"\ + ",member='"DEVICELOCK_STATE_CHANGED_SIG"'" + +#define DEVICELOCK_NAME_OWNER_CHANGED_MATCH\ + "type='signal'"\ + ",interface='"DBUS_INTERFACE_DBUS"'"\ + ",member='"DBUS_NAME_OWNER_CHANGED_SIG"'"\ + ",arg0='"DEVICELOCK_SERVICE"'" diff --git a/src/usb_moded-lock.h b/src/usb_moded-lock.h index bde9018..dd91fcd 100644 --- a/src/usb_moded-lock.h +++ b/src/usb_moded-lock.h @@ -1,7 +1,10 @@ /* Copyright (C) 2012 Nokia Corporation. All rights reserved. + Copyright (C) 2013-2016 Jolla Ltd. Author: Philippe De Swert + 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 @@ -23,6 +26,6 @@ */ /*============================================================================= */ -int usb_moded_get_export_permission(void); -int start_devicelock_listener(void); -int stop_devicelock_listener(void); +int usb_moded_get_export_permission(void); +gboolean start_devicelock_listener(void); +void stop_devicelock_listener(void);