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);