summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Guiraud <christophe.guiraud@intel.com>2013-07-05 13:57:41 +0200
committerrmerlino <regis.merlino@intel.com>2013-12-12 14:51:06 +0100
commite0a29b4f4596f04831f0aa6b863b0c3d53c598ee (patch)
tree421fd6c1b3f94c8267c762d6bf563de7cfc8a210
parentdb713517e91add01c278ec89ba2d77cfb558b7de (diff)
downloaddleyna-server-e0a29b4f4596f04831f0aa6b863b0c3d53c598ee.tar.gz
[Device] Add sleeping device memory cache
when the last context of a sleeping device is notified as unavailable, instead of having the device and its context deleted, the device is kept alive, the lost server notification is not broadcasted, the device is moved from the "device_udn_map" to a "sleeping device udn" map and the context is stored in a new device structure context field "sleeping_device_context". All pending operations related to the device will also be cancelled. The GetServer() request returns now the devices available in the "sleeping device udn" mapĀ in addition to those in the "device udn map". when a get_prop request is done on a device with a sleeping_device_context available this one is used to perform the request. all Dbus requests other than "get_prop" and "wake" invoked on a device with a sleeping_device_context available fail with an error "DLEYNA_ERROR_OPERATION_FAILED". when a new device proxy is notified as available, if it belongs to the "sleeping device udn" map, then it is removed from the "sleeping device udn" map and detroyed, the lost server notification is then broadcasted. The new proxy device is then constructed as usual. Signed-off-by: Christophe Guiraud <christophe.guiraud@intel.com>
-rw-r--r--libdleyna/server/device.c63
-rw-r--r--libdleyna/server/device.h4
-rw-r--r--libdleyna/server/server.c14
-rw-r--r--libdleyna/server/server.h2
-rw-r--r--libdleyna/server/task.c24
-rw-r--r--libdleyna/server/upnp.c107
-rw-r--r--libdleyna/server/upnp.h2
7 files changed, 182 insertions, 34 deletions
diff --git a/libdleyna/server/device.c b/libdleyna/server/device.c
index cc842ee..79f2fc1 100644
--- a/libdleyna/server/device.c
+++ b/libdleyna/server/device.c
@@ -21,6 +21,7 @@
*/
#include <string.h>
+#include <stdint.h>
#include <libgupnp/gupnp-error.h>
#include <libgupnp-dlna/gupnp-dlna-profile.h>
#include <libgupnp-dlna/gupnp-dlna-profile-guesser.h>
@@ -228,10 +229,8 @@ static void prv_context_unsubscribe(dls_device_context_t *ctx)
}
}
-static void prv_context_delete(gpointer context)
+void dls_device_delete_context(dls_device_context_t *ctx)
{
- dls_device_context_t *ctx = context;
-
if (ctx) {
prv_context_unsubscribe(ctx);
@@ -254,6 +253,7 @@ static void prv_context_delete(gpointer context)
}
}
+
static GUPnPServiceInfo *prv_lookup_em_service(GUPnPDeviceInfo *device_info)
{
GList *child_devices;
@@ -346,6 +346,7 @@ void dls_device_delete(void *device)
dev->connection, dev->id);
g_ptr_array_unref(dev->contexts);
+ dls_device_delete_context(dev->sleeping_context);
g_free(dev->path);
g_variant_unref(dev->search_caps);
g_variant_unref(dev->sort_caps);
@@ -577,43 +578,49 @@ static dls_network_if_info_t *prv_get_network_if_info(xmlNode *device_if_node)
info = g_new0(dls_network_if_info_t, 1);
ipv4_addresses = xml_util_get_child_string_list_content_by_name(
- device_if_node,
- "NetworkInterface",
- "AssociatedIpAddresses",
- "Ipv4", NULL);
+ device_if_node,
+ "NetworkInterface",
+ "AssociatedIpAddresses",
+ "Ipv4",
+ NULL);
ipv6_addresses = xml_util_get_child_string_list_content_by_name(
- device_if_node,
- "NetworkInterface",
- "AssociatedIpAddresses",
- "Ipv6", NULL);
+ device_if_node,
+ "NetworkInterface",
+ "AssociatedIpAddresses",
+ "Ipv6",
+ NULL);
info->ip_addresses = g_list_concat(ipv4_addresses, ipv6_addresses);
info->device_uuid = xml_util_get_child_string_content_by_name(
- device_if_node,
- "DeviceUUID", NULL);
+ device_if_node,
+ "DeviceUUID",
+ NULL);
info->mac_address = xml_util_get_child_string_content_by_name(
- device_if_node,
- "NetworkInterface",
- "MacAddress", NULL);
+ device_if_node,
+ "NetworkInterface",
+ "MacAddress",
+ NULL);
info->network_if_mode = xml_util_get_child_string_content_by_name(
- device_if_node,
- "NetworkInterface",
- "NetworkInterfaceMode"
- , NULL);
+ device_if_node,
+ "NetworkInterface",
+ "NetworkInterfaceMode",
+ NULL);
info->wake_on_pattern = xml_util_get_child_string_content_by_name(
- device_if_node,
- "NetworkInterface",
- "WakeOnPattern", NULL);
+ device_if_node,
+ "NetworkInterface",
+ "WakeOnPattern",
+ NULL);
info->wake_transport = xml_util_get_child_string_content_by_name(
device_if_node,
"NetworkInterface",
- "WakeSupportedTransport", NULL);
+ "WakeSupportedTransport",
+ NULL);
if ((info->device_uuid == NULL || strlen(info->device_uuid) > 70) ||
(info->mac_address == NULL || strlen(info->mac_address) != 17) ||
@@ -1590,7 +1597,8 @@ dls_device_t *dls_device_new(
dev = g_new0(dls_device_t, 1);
dev->connection = connection;
- dev->contexts = g_ptr_array_new_with_free_func(prv_context_delete);
+ dev->contexts = g_ptr_array_new_with_free_func((GDestroyNotify)
+ dls_device_delete_context);
dev->path = new_path;
context = dls_device_append_new_context(dev, ip_address,
@@ -3045,7 +3053,10 @@ void dls_device_get_prop(dls_client_t *client,
DLEYNA_LOG_DEBUG("Enter");
- context = dls_device_get_context(task->target.device, client);
+ if (task->target.device->contexts->len != 0)
+ context = dls_device_get_context(task->target.device, client);
+ else
+ context = task->target.device->sleeping_context;
if (!strcmp(task_data->interface_name,
DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE)) {
diff --git a/libdleyna/server/device.h b/libdleyna/server/device.h
index 891351c..24b3cc4 100644
--- a/libdleyna/server/device.h
+++ b/libdleyna/server/device.h
@@ -72,6 +72,7 @@ struct dls_device_t_ {
guint id;
gchar *path;
GPtrArray *contexts;
+ dls_device_context_t *sleeping_context;
guint timeout_id;
GHashTable *uploads;
GHashTable *upload_jobs;
@@ -92,6 +93,7 @@ dls_device_context_t *dls_device_append_new_context(dls_device_t *device,
const gchar *ip_address,
GUPnPDeviceProxy *proxy,
GUPnPDeviceInfo *device_info);
+
void dls_device_delete(void *device);
void dls_device_unsubscribe(void *device);
@@ -119,6 +121,8 @@ dls_device_t *dls_device_from_path(const gchar *path, GHashTable *device_list);
dls_device_context_t *dls_device_get_context(const dls_device_t *device,
dls_client_t *client);
+void dls_device_delete_context(dls_device_context_t *context);
+
void dls_device_get_children(dls_client_t *client,
dls_task_t *task,
const gchar *upnp_filter, const gchar *sort_by);
diff --git a/libdleyna/server/server.c b/libdleyna/server/server.c
index 5367252..1673ccd 100644
--- a/libdleyna/server/server.c
+++ b/libdleyna/server/server.c
@@ -945,6 +945,12 @@ gboolean dls_server_get_object_info(const gchar *object_path,
dls_upnp_get_device_udn_map(g_context.upnp));
if (*device == NULL) {
+ *device = dls_device_from_path(*root_path,
+ dls_upnp_get_sleeping_device_udn_map(
+ g_context.upnp));
+ }
+
+ if (*device == NULL) {
DLEYNA_LOG_WARNING("Cannot locate device for %s", *root_path);
*error = g_error_new(DLEYNA_SERVER_ERROR,
@@ -965,6 +971,14 @@ on_error:
return FALSE;
}
+gboolean dls_server_is_device_sleeping(dls_device_t *dev)
+{
+ if (dev->sleeping_context != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
static const gchar *prv_get_device_id(const gchar *object, GError **error)
{
dls_device_t *device;
diff --git a/libdleyna/server/server.h b/libdleyna/server/server.h
index 01d9356..36261de 100644
--- a/libdleyna/server/server.h
+++ b/libdleyna/server/server.h
@@ -40,6 +40,8 @@ gboolean dls_server_get_object_info(const gchar *object_path,
dls_upnp_t *dls_server_get_upnp(void);
+gboolean dls_server_is_device_sleeping(dls_device_t *dev);
+
dleyna_task_processor_t *dls_server_get_task_processor(void);
const dleyna_connector_t *dls_server_get_connector(void);
diff --git a/libdleyna/server/task.c b/libdleyna/server/task.c
index 312efc0..ec63d8e 100644
--- a/libdleyna/server/task.c
+++ b/libdleyna/server/task.c
@@ -222,6 +222,24 @@ static gboolean prv_set_task_target_info(dls_task_t *task, const gchar *path,
&task->target.device, error);
}
+static gboolean prv_is_task_allowed(dls_task_t *task, GError **error)
+{
+ if (dls_server_is_device_sleeping(task->target.device)) {
+ if (task->type != DLS_TASK_WAKE &&
+ task->type != DLS_TASK_GET_PROP)
+ goto on_error;
+ }
+
+ return TRUE;
+
+on_error:
+ *error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Target device is sleeping");
+
+ return FALSE;
+}
+
static dls_task_t *prv_m2spec_task_new(dls_task_type_t type,
dleyna_connector_msg_id_t invocation,
const gchar *path,
@@ -238,14 +256,16 @@ static dls_task_t *prv_m2spec_task_new(dls_task_type_t type,
task = (dls_task_t *)g_new0(dls_async_task_t, 1);
}
- if (!prv_set_task_target_info(task, path, error)) {
+ task->type = type;
+
+ if (!prv_set_task_target_info(task, path, error) ||
+ !prv_is_task_allowed(task, error)) {
prv_delete(task);
task = NULL;
goto finished;
}
- task->type = type;
task->invocation = invocation;
task->result_format = result_format;
diff --git a/libdleyna/server/upnp.c b/libdleyna/server/upnp.c
index 7301e61..3074645 100644
--- a/libdleyna/server/upnp.c
+++ b/libdleyna/server/upnp.c
@@ -50,6 +50,7 @@ struct dls_upnp_t_ {
GUPnPContextManager *context_manager;
void *user_data;
GHashTable *device_udn_map;
+ GHashTable *sleeping_device_udn_map;
GHashTable *device_uc_map;
guint counter;
};
@@ -205,6 +206,9 @@ static void prv_device_available_cb(GUPnPControlPoint *cp,
GUPnPDeviceInfo *device_proxy = (GUPnPDeviceInfo *)proxy;
GUPnPDeviceInfo *device_info = NULL;
const gchar *device_type;
+ gboolean subscribe = FALSE;
+ gpointer key;
+ gpointer val;
udn = gupnp_device_info_get_udn(device_proxy);
@@ -231,6 +235,33 @@ static void prv_device_available_cb(GUPnPControlPoint *cp,
device = g_hash_table_lookup(upnp->device_udn_map, udn);
if (!device) {
+ device = g_hash_table_lookup(upnp->sleeping_device_udn_map,
+ udn);
+
+ if (device != NULL) {
+ if (g_hash_table_lookup_extended(
+ upnp->sleeping_device_udn_map,
+ udn,
+ &key,
+ &val)) {
+ g_hash_table_steal(
+ upnp->sleeping_device_udn_map,
+ udn);
+
+ g_free(key);
+ }
+
+ g_hash_table_insert(upnp->device_udn_map, g_strdup(udn),
+ device);
+
+ dls_device_delete_context(device->sleeping_context);
+ device->sleeping_context = NULL;
+ device->sleeping = FALSE;
+ subscribe = TRUE;
+ }
+ }
+
+ if (!device) {
priv_t = g_hash_table_lookup(upnp->device_uc_map, udn);
if (priv_t)
@@ -270,6 +301,8 @@ static void prv_device_available_cb(GUPnPControlPoint *cp,
ip_address,
proxy,
device_info);
+ if (subscribe)
+ dls_device_subscribe_to_service_changes(device);
}
DLEYNA_LOG_DEBUG_NL();
@@ -305,6 +338,9 @@ static void prv_device_unavailable_cb(GUPnPControlPoint *cp,
gboolean under_construction = FALSE;
prv_device_new_ct_t *priv_t;
const dleyna_task_queue_key_t *queue_id;
+ dls_device_context_t *lost_context;
+ gpointer key;
+ gpointer val;
DLEYNA_LOG_DEBUG("Enter");
@@ -349,13 +385,51 @@ static void prv_device_unavailable_cb(GUPnPControlPoint *cp,
construction_ctx = !strcmp(context->ip_address,
priv_t->ip_address);
- (void) g_ptr_array_remove_index(device->contexts, i);
+ g_ptr_array_set_free_func(device->contexts, NULL);
+
+ lost_context = g_ptr_array_remove_index(device->contexts, i);
+
+ g_ptr_array_set_free_func(device->contexts,
+ (GDestroyNotify)dls_device_delete_context);
if (device->contexts->len == 0) {
if (!under_construction) {
- DLEYNA_LOG_DEBUG("Last Context lost. Delete device");
- upnp->lost_server(device->path, upnp->user_data);
- g_hash_table_remove(upnp->device_udn_map, udn);
+ DLEYNA_LOG_DEBUG("Last Context lost.");
+
+ if (!device->sleeping) {
+ DLEYNA_LOG_DEBUG("Delete device.");
+
+ upnp->lost_server(device->path,
+ upnp->user_data);
+
+ g_hash_table_remove(upnp->device_udn_map, udn);
+ } else {
+ DLEYNA_LOG_DEBUG("Persist sleeping device.");
+
+ dleyna_task_processor_remove_queues_for_sink(
+ dls_server_get_task_processor(),
+ device->path);
+
+ g_hash_table_insert(
+ upnp->sleeping_device_udn_map,
+ g_strdup(udn),
+ device);
+
+ if (g_hash_table_lookup_extended(
+ upnp->device_udn_map,
+ udn,
+ &key,
+ &val)) {
+ g_hash_table_steal(upnp->device_udn_map,
+ udn);
+
+ g_free(key);
+ }
+
+ device->sleeping_context = lost_context;
+
+ lost_context = NULL;
+ }
} else {
DLEYNA_LOG_WARNING(
"Device under construction. Cancelling");
@@ -394,6 +468,9 @@ static void prv_device_unavailable_cb(GUPnPControlPoint *cp,
device);
}
+ if (lost_context != NULL)
+ dls_device_delete_context(lost_context);
+
on_error:
DLEYNA_LOG_DEBUG("Exit");
@@ -439,8 +516,13 @@ dls_upnp_t *dls_upnp_new(dleyna_connector_id_t connection,
upnp->lost_server = lost_server;
upnp->device_udn_map = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free,
- dls_device_delete);
+ g_free,
+ dls_device_delete);
+
+ upnp->sleeping_device_udn_map = g_hash_table_new_full(g_str_hash,
+ g_str_equal,
+ g_free,
+ dls_device_delete);
upnp->device_uc_map = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, NULL);
@@ -463,6 +545,7 @@ void dls_upnp_delete(dls_upnp_t *upnp)
g_hash_table_unref(upnp->property_map);
g_hash_table_unref(upnp->filter_map);
g_hash_table_unref(upnp->device_udn_map);
+ g_hash_table_unref(upnp->sleeping_device_udn_map);
g_hash_table_unref(upnp->device_uc_map);
g_free(upnp);
}
@@ -487,6 +570,13 @@ GVariant *dls_upnp_get_device_ids(dls_upnp_t *upnp)
g_variant_builder_add(&vb, "o", device->path);
}
+ g_hash_table_iter_init(&iter, upnp->sleeping_device_udn_map);
+ while (g_hash_table_iter_next(&iter, NULL, &value)) {
+ device = value;
+ DLEYNA_LOG_DEBUG("Have sleeping device %s", device->path);
+ g_variant_builder_add(&vb, "o", device->path);
+ }
+
retval = g_variant_ref_sink(g_variant_builder_end(&vb));
DLEYNA_LOG_DEBUG("Exit");
@@ -499,6 +589,11 @@ GHashTable *dls_upnp_get_device_udn_map(dls_upnp_t *upnp)
return upnp->device_udn_map;
}
+GHashTable *dls_upnp_get_sleeping_device_udn_map(dls_upnp_t *upnp)
+{
+ return upnp->sleeping_device_udn_map;
+}
+
void dls_upnp_get_children(dls_upnp_t *upnp, dls_client_t *client,
dls_task_t *task,
dls_upnp_task_complete_t cb)
diff --git a/libdleyna/server/upnp.h b/libdleyna/server/upnp.h
index b2d9ba1..fc1b3a3 100644
--- a/libdleyna/server/upnp.h
+++ b/libdleyna/server/upnp.h
@@ -43,6 +43,8 @@ GVariant *dls_upnp_get_device_ids(dls_upnp_t *upnp);
GHashTable *dls_upnp_get_device_udn_map(dls_upnp_t *upnp);
+GHashTable *dls_upnp_get_sleeping_device_udn_map(dls_upnp_t *upnp);
+
void dls_upnp_get_children(dls_upnp_t *upnp, dls_client_t *client,
dls_task_t *task,
dls_upnp_task_complete_t cb);