Skip to content

Commit

Permalink
[dsme] Make device state tracking asynchronous. MER#1694
Browse files Browse the repository at this point in the history
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 <simo.piiroinen@jollamobile.com>
  • Loading branch information
spiiroin committed Nov 7, 2016
1 parent d72c59a commit 4e7f970
Show file tree
Hide file tree
Showing 4 changed files with 333 additions and 25 deletions.
2 changes: 1 addition & 1 deletion src/usb_moded-dbus.c
Expand Up @@ -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;
}

Expand Down
342 changes: 320 additions & 22 deletions src/usb_moded-dsme.c
@@ -1,10 +1,11 @@
/**
@file usb_moded-dsme.c
Copyright (C) 2013 Jolla. All rights reserved.
Copyright (C) 2013-2016 Jolla. All rights reserved.
@author: Philippe De Swert <philippe.deswert@jollamobile.com>
@author: Jonni Rainisto <jonni.rainisto@jollamobile.com>
@author: Simo Piiroinen <simo.piiroinen@jollamobile.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the Lesser GNU General Public License
Expand All @@ -25,46 +26,343 @@
#include <dbus/dbus-glib-lowlevel.h>

#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)
{
DBusConnection *dbus_conn = NULL;
DBusMessage *msg = NULL, *reply = NULL;
DBusError error;
int ret = 0;
char* buffer = NULL;
return in_user_state;
}

dbus_error_init(&error);
/* ========================================================================= *
* device state queries
* ========================================================================= */

if( (dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error)) == 0 )
static void device_state_changed(const char *state)
{
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);
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");
}
}

if ((msg = dbus_message_new_method_call("com.nokia.dsme", "/request", "com.nokia.dsme.request", "get_state")) != NULL)
static DBusPendingCall *device_state_query_pc = 0;

static void device_state_cancel(void)
{
if ((reply = dbus_connection_send_with_reply_and_block(dbus_conn, msg, -1, NULL)) != NULL)
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)
{
dbus_message_get_args(reply, &error, DBUS_TYPE_STRING, &buffer, DBUS_TYPE_INVALID);
dbus_message_unref(reply);
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;
}
dbus_message_unref(msg);

if( dbus_set_error_from_message(&err, rsp) )
{
log_err("error reply: %s: %s", err.name, err.message);
goto EXIT;
}
dbus_connection_unref(dbus_conn);

if(buffer)
if( !dbus_message_get_args(rsp, &err,
DBUS_TYPE_STRING, &dta,
DBUS_TYPE_INVALID) )
{
log_debug("user state = %s\n", buffer);
if (strcmp(buffer, "USER")==0) ret = 1;
if( strcmp(err.name, DBUS_ERROR_NAME_HAS_NO_OWNER) )
log_err("parse error: %s: %s", err.name, err.message);
goto EXIT;
}
return(ret);

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

0 comments on commit 4e7f970

Please sign in to comment.