]> code.delx.au - pulseaudio/blobdiff - src/modules/dbus/iface-core.c
dbusiface-module: Implement the Module D-Bus interface.
[pulseaudio] / src / modules / dbus / iface-core.c
index cdfd2a3ca53775f1d2ab1d3b2acbfcd9692b7b29..9e8f775e6a9b2538529ac56c46861d4b1e3ae6e2 100644 (file)
 #include "iface-card.h"
 #include "iface-client.h"
 #include "iface-device.h"
+#include "iface-memstats.h"
 #include "iface-module.h"
 #include "iface-sample.h"
 #include "iface-stream.h"
 
 #include "iface-core.h"
 
-#define OBJECT_PATH "/org/pulseaudio/core1"
-#define INTERFACE_CORE "org.PulseAudio.Core1"
-
 #define INTERFACE_REVISION 0
 
-
-
 static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_version(DBusConnection *conn, DBusMessage *msg, void *userdata);
@@ -112,6 +108,11 @@ struct pa_dbusiface_core {
 
     pa_sink *fallback_sink;
     pa_source *fallback_source;
+
+    pa_hook_slot *extension_registered_slot;
+    pa_hook_slot *extension_unregistered_slot;
+
+    pa_dbusiface_memstats *memstats;
 };
 
 enum property_handler_index {
@@ -259,6 +260,8 @@ enum signal_index {
     SIGNAL_MODULE_REMOVED,
     SIGNAL_NEW_CLIENT,
     SIGNAL_CLIENT_REMOVED,
+    SIGNAL_NEW_EXTENSION,
+    SIGNAL_EXTENSION_REMOVED,
     SIGNAL_MAX
 };
 
@@ -280,6 +283,8 @@ static pa_dbus_arg_info new_module_args[] =              { { "module",
 static pa_dbus_arg_info module_removed_args[] =          { { "module",          "o", NULL } };
 static pa_dbus_arg_info new_client_args[] =              { { "client",          "o", NULL } };
 static pa_dbus_arg_info client_removed_args[] =          { { "client",          "o", NULL } };
+static pa_dbus_arg_info new_extension_args[] =           { { "extension",       "s", NULL } };
+static pa_dbus_arg_info extension_removed_args[] =       { { "extension",       "s", NULL } };
 
 static pa_dbus_signal_info signals[SIGNAL_MAX] = {
     [SIGNAL_NEW_CARD]                = { .name = "NewCard",               .arguments = new_card_args,                .n_arguments = 1 },
@@ -300,10 +305,12 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = {
     [SIGNAL_MODULE_REMOVED]          = { .name = "ModuleRemoved",         .arguments = module_removed_args,          .n_arguments = 1 },
     [SIGNAL_NEW_CLIENT]              = { .name = "NewClient",             .arguments = new_client_args,              .n_arguments = 1 },
     [SIGNAL_CLIENT_REMOVED]          = { .name = "ClientRemoved",         .arguments = client_removed_args,          .n_arguments = 1 },
+    [SIGNAL_NEW_EXTENSION]           = { .name = "NewExtension",          .arguments = new_extension_args,           .n_arguments = 1 },
+    [SIGNAL_EXTENSION_REMOVED]       = { .name = "ExtensionRemoved",      .arguments = extension_removed_args,       .n_arguments = 1 }
 };
 
 static pa_dbus_interface_info core_interface_info = {
-    .name = INTERFACE_CORE,
+    .name = PA_DBUS_CORE_INTERFACE,
     .method_handlers = method_handlers,
     .n_method_handlers = METHOD_HANDLER_MAX,
     .property_handlers = property_handlers,
@@ -443,7 +450,8 @@ static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg,
     }
 
     if (n_channels > PA_CHANNELS_MAX) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels: %u. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "Too many channels: %u. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX);
         return;
     }
 
@@ -535,7 +543,7 @@ static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *ms
 /* The caller frees the array, but not the strings. */
 static const char **get_cards(pa_dbusiface_core *c, unsigned *n) {
     const char **cards;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_card *card;
 
@@ -549,10 +557,8 @@ static const char **get_cards(pa_dbusiface_core *c, unsigned *n) {
 
     cards = pa_xnew(const char *, *n);
 
-    for (i = 0, card = pa_hashmap_iterate(c->cards, &state, NULL); card; ++i, card = pa_hashmap_iterate(c->cards, &state, NULL))
-        cards[i] = pa_dbusiface_card_get_path(card);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(card, c->cards, state)
+        cards[i++] = pa_dbusiface_card_get_path(card);
 
     return cards;
 }
@@ -576,7 +582,7 @@ static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userd
 /* The caller frees the array, but not the strings. */
 static const char **get_sinks(pa_dbusiface_core *c, unsigned *n) {
     const char **sinks;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_device *sink;
 
@@ -590,10 +596,8 @@ static const char **get_sinks(pa_dbusiface_core *c, unsigned *n) {
 
     sinks = pa_xnew(const char *, *n);
 
-    for (i = 0, sink = pa_hashmap_iterate(c->sinks_by_index, &state, NULL); sink; ++i, sink = pa_hashmap_iterate(c->sinks_by_index, &state, NULL))
-        sinks[i] = pa_dbusiface_device_get_path(sink);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(sink, c->sinks_by_index, state)
+        sinks[i++] = pa_dbusiface_device_get_path(sink);
 
     return sinks;
 }
@@ -624,7 +628,8 @@ static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi
     pa_assert(c);
 
     if (!c->fallback_sink) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sinks, and therefore no fallback sink either.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "There are no sinks, and therefore no fallback sink either.");
         return;
     }
 
@@ -644,7 +649,8 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi
     pa_assert(c);
 
     if (!c->fallback_sink) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sinks, and therefore no fallback sink either.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "There are no sinks, and therefore no fallback sink either.");
         return;
     }
 
@@ -652,7 +658,7 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi
         return;
 
     if (!(fallback_sink = pa_hashmap_get(c->sinks_by_path, object_path))) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sink.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", object_path);
         return;
     }
 
@@ -664,7 +670,7 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi
 /* The caller frees the array, but not the strings. */
 static const char **get_sources(pa_dbusiface_core *c, unsigned *n) {
     const char **sources;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_device *source;
 
@@ -678,10 +684,8 @@ static const char **get_sources(pa_dbusiface_core *c, unsigned *n) {
 
     sources = pa_xnew(const char *, *n);
 
-    for (i = 0, source = pa_hashmap_iterate(c->sources_by_index, &state, NULL); source; ++i, source = pa_hashmap_iterate(c->sources_by_index, &state, NULL))
-        sources[i] = pa_dbusiface_device_get_path(source);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(source, c->sources_by_index, state)
+        sources[i++] = pa_dbusiface_device_get_path(source);
 
     return sources;
 }
@@ -712,7 +716,8 @@ static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, v
     pa_assert(c);
 
     if (!c->fallback_source) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sources, and therefore no fallback source either.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "There are no sources, and therefore no fallback source either.");
         return;
     }
 
@@ -732,7 +737,8 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, v
     pa_assert(c);
 
     if (!c->fallback_source) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sources, and therefore no fallback source either.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "There are no sources, and therefore no fallback source either.");
         return;
     }
 
@@ -740,7 +746,7 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, v
         return;
 
     if (!(fallback_source = pa_hashmap_get(c->sources_by_path, object_path))) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such source.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", object_path);
         return;
     }
 
@@ -752,7 +758,7 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, v
 /* The caller frees the array, but not the strings. */
 static const char **get_playback_streams(pa_dbusiface_core *c, unsigned *n) {
     const char **streams;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_stream *stream;
 
@@ -766,10 +772,8 @@ static const char **get_playback_streams(pa_dbusiface_core *c, unsigned *n) {
 
     streams = pa_xnew(const char *, *n);
 
-    for (i = 0, stream = pa_hashmap_iterate(c->playback_streams, &state, NULL); stream; ++i, stream = pa_hashmap_iterate(c->playback_streams, &state, NULL))
-        streams[i] = pa_dbusiface_stream_get_path(stream);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(stream, c->playback_streams, state)
+        streams[i++] = pa_dbusiface_stream_get_path(stream);
 
     return streams;
 }
@@ -793,7 +797,7 @@ static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg,
 /* The caller frees the array, but not the strings. */
 static const char **get_record_streams(pa_dbusiface_core *c, unsigned *n) {
     const char **streams;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_stream *stream;
 
@@ -807,10 +811,8 @@ static const char **get_record_streams(pa_dbusiface_core *c, unsigned *n) {
 
     streams = pa_xnew(const char *, *n);
 
-    for (i = 0, stream = pa_hashmap_iterate(c->record_streams, &state, NULL); stream; ++i, stream = pa_hashmap_iterate(c->record_streams, &state, NULL))
-        streams[i] = pa_dbusiface_stream_get_path(stream);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(stream, c->record_streams, state)
+        streams[i++] = pa_dbusiface_stream_get_path(stream);
 
     return streams;
 }
@@ -834,7 +836,7 @@ static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, vo
 /* The caller frees the array, but not the strings. */
 static const char **get_samples(pa_dbusiface_core *c, unsigned *n) {
     const char **samples;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_sample *sample;
 
@@ -848,10 +850,8 @@ static const char **get_samples(pa_dbusiface_core *c, unsigned *n) {
 
     samples = pa_xnew(const char *, *n);
 
-    for (i = 0, sample = pa_hashmap_iterate(c->samples, &state, NULL); sample; ++i, sample = pa_hashmap_iterate(c->samples, &state, NULL))
-        samples[i] = pa_dbusiface_sample_get_path(sample);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(sample, c->samples, state)
+        samples[i++] = pa_dbusiface_sample_get_path(sample);
 
     return samples;
 }
@@ -875,7 +875,7 @@ static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *use
 /* The caller frees the array, but not the strings. */
 static const char **get_modules(pa_dbusiface_core *c, unsigned *n) {
     const char **modules;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_module *module;
 
@@ -889,10 +889,8 @@ static const char **get_modules(pa_dbusiface_core *c, unsigned *n) {
 
     modules = pa_xnew(const char *, *n);
 
-    for (i = 0, module = pa_hashmap_iterate(c->modules, &state, NULL); module; ++i, module = pa_hashmap_iterate(c->modules, &state, NULL))
-        modules[i] = pa_dbusiface_module_get_path(module);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(module, c->modules, state)
+        modules[i++] = pa_dbusiface_module_get_path(module);
 
     return modules;
 }
@@ -916,7 +914,7 @@ static void handle_get_modules(DBusConnection *conn, DBusMessage *msg, void *use
 /* The caller frees the array, but not the strings. */
 static const char **get_clients(pa_dbusiface_core *c, unsigned *n) {
     const char **clients;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_client *client;
 
@@ -930,10 +928,8 @@ static const char **get_clients(pa_dbusiface_core *c, unsigned *n) {
 
     clients = pa_xnew(const char *, *n);
 
-    for (i = 0, client = pa_hashmap_iterate(c->clients, &state, NULL); client; ++i, client = pa_hashmap_iterate(c->clients, &state, NULL))
-        clients[i] = pa_dbusiface_client_get_path(client);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(client, c->clients, state)
+        clients[i++] = pa_dbusiface_client_get_path(client);
 
     return clients;
 }
@@ -1046,9 +1042,14 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
     default_sample_rate = c->core->default_sample_spec.rate;
     cards = get_cards(c, &n_cards);
     sinks = get_sinks(c, &n_sinks);
-    fallback_sink = c->fallback_sink ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index))) : NULL;
+    fallback_sink = c->fallback_sink
+                    ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index)))
+                    : NULL;
     sources = get_sources(c, &n_sources);
-    fallback_source = c->fallback_source ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index))) : NULL;
+    fallback_source = c->fallback_source
+                      ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index,
+                                                                    PA_UINT32_TO_PTR(c->fallback_source->index)))
+                      : NULL;
     playback_streams = get_playback_streams(c, &n_playback_streams);
     record_streams = get_record_streams(c, &n_record_streams);
     samples = get_samples(c, &n_samples);
@@ -1163,7 +1164,7 @@ static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void
     }
 
     if (!(sink = pa_namereg_get(c->core, sink_name, PA_NAMEREG_SINK))) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sink.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", sink_name);
         return;
     }
 
@@ -1195,7 +1196,7 @@ static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, vo
     }
 
     if (!(source = pa_namereg_get(c->core, source_name, PA_NAMEREG_SOURCE))) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such source.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", source_name);
         return;
     }
 
@@ -1308,7 +1309,8 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u
     }
 
     if (n_channels > PA_CHANNELS_MAX) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels.");
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "Too many channels: %u. The maximum is %u.", n_channels, PA_CHANNELS_MAX);
         goto finish;
     }
 
@@ -1320,7 +1322,8 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u
     }
 
     if (n_volume_entries != 0 && n_volume_entries != n_channels) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "The channels and default_volume arguments have different number of elements.");
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "The channels and default_volume arguments have different number of elements.");
         goto finish;
     }
 
@@ -1346,7 +1349,8 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u
     ss.channels = n_channels;
 
     if (!pa_frame_aligned(data_length, &ss)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "The sample length in bytes doesn't align with the sample format and channels.");
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "The sample length in bytes doesn't align with the sample format and channels.");
         goto finish;
     }
 
@@ -1377,7 +1381,7 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u
         sample->volume_is_set = FALSE;
     }
 
-    dbus_sample = pa_dbusiface_sample_new(sample, OBJECT_PATH);
+    dbus_sample = pa_dbusiface_sample_new(c, sample);
     pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), dbus_sample);
 
     object_path = pa_dbusiface_sample_get_path(dbus_sample);
@@ -1440,15 +1444,19 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
     arg_type = dbus_message_iter_get_arg_type(&msg_iter);
     if (arg_type != DBUS_TYPE_ARRAY) {
         if (arg_type == DBUS_TYPE_INVALID)
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. A dictionary from strings to strings was expected.");
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                               "Too few arguments. A dictionary from strings to strings was expected.");
         else
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An dictionary from strings to strings was expected.", (char) arg_type);
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                               "Wrong argument type: '%c'. An dictionary from strings to strings was expected.",
+                               (char) arg_type);
         return;
     }
 
     arg_type = dbus_message_iter_get_element_type(&msg_iter);
     if (arg_type != DBUS_TYPE_DICT_ENTRY) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dict entry (string to string) was expected.", (char) arg_type);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "Wrong array element type: '%c'. A dict entry (string to string) was expected.", (char) arg_type);
         return;
     }
 
@@ -1464,7 +1472,8 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
 
         arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter);
         if (arg_type != DBUS_TYPE_STRING) {
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type);
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                               "Wrong dict key type: '%c'. A string was expected.", (char) arg_type);
             goto finish;
         }
 
@@ -1481,7 +1490,8 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
             if (arg_type == DBUS_TYPE_INVALID)
                 pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing.");
             else
-                pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. A string was expected.", (char) arg_type);
+                pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                                   "Wrong dict value type: '%c'. A string was expected.", (char) arg_type);
             goto finish;
         }
 
@@ -1499,7 +1509,7 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
         goto finish;
     }
 
-    dbus_module = pa_dbusiface_module_new(module, OBJECT_PATH);
+    dbus_module = pa_dbusiface_module_new(module);
     pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(module->index), dbus_module);
 
     object_path = pa_dbusiface_module_get_path(dbus_module);
@@ -1541,7 +1551,10 @@ static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, voi
 
     dbus_error_init(&error);
 
-    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, DBUS_TYPE_INVALID)) {
+    if (!dbus_message_get_args(msg, &error,
+                               DBUS_TYPE_STRING, &signal,
+                               DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects,
+                               DBUS_TYPE_INVALID)) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
         dbus_error_free(&error);
         goto finish;
@@ -1600,12 +1613,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
             if (c->fallback_sink != new_fallback_sink) {
                 c->fallback_sink = new_fallback_sink;
 
-                if (new_fallback_sink) {
-                    pa_assert_se((device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index))));
-
+                if (new_fallback_sink
+                    && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) {
                     object_path = pa_dbusiface_device_get_path(device);
 
-                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
+                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                                   PA_DBUS_CORE_INTERFACE,
+                                                                   signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
                     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
                     dbus_message_unref(signal);
@@ -1616,12 +1630,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
             if (c->fallback_source != new_fallback_source) {
                 c->fallback_source = new_fallback_source;
 
-                if (new_fallback_source) {
-                    pa_assert_se((device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index))));
-
+                if (new_fallback_source
+                    && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) {
                     object_path = pa_dbusiface_device_get_path(device);
 
-                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
+                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                                   PA_DBUS_CORE_INTERFACE,
+                                                                   signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
                     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
                     dbus_message_unref(signal);
@@ -1633,13 +1648,15 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
         case PA_SUBSCRIPTION_EVENT_CARD:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
                 if (!(card = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(idx)))) {
-                    card = pa_dbusiface_card_new(pa_idxset_get_by_index(core->cards, idx), OBJECT_PATH);
+                    card = pa_dbusiface_card_new(c, pa_idxset_get_by_index(core->cards, idx));
                     pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card);
                 }
 
                 object_path = pa_dbusiface_card_get_path(card);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_CARD].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_CARD].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1647,7 +1664,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_card_get_path(card);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_CARD_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_CARD_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_card_free(card);
@@ -1656,23 +1675,48 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
         case PA_SUBSCRIPTION_EVENT_SINK:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+                pa_sink *sink = pa_idxset_get_by_index(core->sinks, idx);
+
                 if (!(device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) {
-                    device = pa_dbusiface_device_new_sink(pa_idxset_get_by_index(core->sinks, idx), OBJECT_PATH);
+                    device = pa_dbusiface_device_new_sink(c, sink);
                     pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device);
                     pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device);
                 }
 
                 object_path = pa_dbusiface_device_get_path(device);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SINK].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_SINK].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
+                pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+                dbus_message_unref(signal);
+                signal = NULL;
+
+                if (c->fallback_sink && pa_streq(c->fallback_sink->name, sink->name)) {
+                    /* We have got default sink change event, but at that point
+                     * the D-Bus sink object wasn't created yet. Now that the
+                     * object is created, let's send the fallback sink change
+                     * signal. */
+                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                                   PA_DBUS_CORE_INTERFACE,
+                                                                   signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
+                    pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+                    pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+                    dbus_message_unref(signal);
+                    signal = NULL;
+                }
+
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                 pa_assert_se((device = pa_hashmap_remove(c->sinks_by_index, PA_UINT32_TO_PTR(idx))));
                 object_path = pa_dbusiface_device_get_path(device);
                 pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path));
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SINK_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_SINK_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_device_free(device);
@@ -1681,23 +1725,48 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
         case PA_SUBSCRIPTION_EVENT_SOURCE:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+                pa_source *source = pa_idxset_get_by_index(core->sources, idx);
+
                 if (!(device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) {
-                    device = pa_dbusiface_device_new_source(pa_idxset_get_by_index(core->sources, idx), OBJECT_PATH);
+                    device = pa_dbusiface_device_new_source(c, source);
                     pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device);
                     pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device);
                 }
 
                 object_path = pa_dbusiface_device_get_path(device);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SOURCE].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_SOURCE].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
+                pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+                dbus_message_unref(signal);
+                signal = NULL;
+
+                if (c->fallback_source && pa_streq(c->fallback_source->name, source->name)) {
+                    /* We have got default source change event, but at that
+                     * point the D-Bus source object wasn't created yet. Now
+                     * that the object is created, let's send the fallback
+                     * source change signal. */
+                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                                   PA_DBUS_CORE_INTERFACE,
+                                                                   signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
+                    pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+                    pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+                    dbus_message_unref(signal);
+                    signal = NULL;
+                }
+
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                 pa_assert_se((device = pa_hashmap_remove(c->sources_by_index, PA_UINT32_TO_PTR(idx))));
                 object_path = pa_dbusiface_device_get_path(device);
                 pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path));
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SOURCE_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_SOURCE_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_device_free(device);
@@ -1707,13 +1776,15 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
         case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
                 if (!(stream = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(idx)))) {
-                    stream = pa_dbusiface_stream_new_playback(pa_idxset_get_by_index(core->sink_inputs, idx), OBJECT_PATH);
+                    stream = pa_dbusiface_stream_new_playback(c, pa_idxset_get_by_index(core->sink_inputs, idx));
                     pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream);
                 }
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_PLAYBACK_STREAM].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1721,7 +1792,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_stream_free(stream);
@@ -1731,13 +1804,15 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
         case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
                 if (!(stream = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(idx)))) {
-                    stream = pa_dbusiface_stream_new_record(pa_idxset_get_by_index(core->source_outputs, idx), OBJECT_PATH);
+                    stream = pa_dbusiface_stream_new_record(c, pa_idxset_get_by_index(core->source_outputs, idx));
                     pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream);
                 }
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_RECORD_STREAM].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_RECORD_STREAM].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1745,7 +1820,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_RECORD_STREAM_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_RECORD_STREAM_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_stream_free(stream);
@@ -1755,13 +1832,15 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
         case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
                 if (!(sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) {
-                    sample = pa_dbusiface_sample_new(pa_idxset_get_by_index(core->scache, idx), OBJECT_PATH);
+                    sample = pa_dbusiface_sample_new(c, pa_idxset_get_by_index(core->scache, idx));
                     pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample);
                 }
 
                 object_path = pa_dbusiface_sample_get_path(sample);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SAMPLE].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_SAMPLE].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1769,7 +1848,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_sample_get_path(sample);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SAMPLE_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_SAMPLE_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_sample_free(sample);
@@ -1779,13 +1860,15 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
         case PA_SUBSCRIPTION_EVENT_MODULE:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
                 if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) {
-                    module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx), OBJECT_PATH);
+                    module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx));
                     pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module);
                 }
 
                 object_path = pa_dbusiface_module_get_path(module);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_MODULE].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_MODULE].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1793,7 +1876,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_module_get_path(module);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_MODULE_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_MODULE_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_module_free(module);
@@ -1803,13 +1888,15 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
         case PA_SUBSCRIPTION_EVENT_CLIENT:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
                 if (!(client = pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(idx)))) {
-                    client = pa_dbusiface_client_new(pa_idxset_get_by_index(core->clients, idx), OBJECT_PATH);
+                    client = pa_dbusiface_client_new(c, pa_idxset_get_by_index(core->clients, idx));
                     pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client);
                 }
 
                 object_path = pa_dbusiface_client_get_path(client);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_CLIENT].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_CLIENT].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1817,7 +1904,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_client_get_path(client);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_CLIENT_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_CLIENT_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_client_free(client);
@@ -1831,6 +1920,44 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
     }
 }
 
+static pa_hook_result_t extension_registered_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    const char *ext_name = call_data;
+    DBusMessage *signal = NULL;
+
+    pa_assert(c);
+    pa_assert(ext_name);
+
+    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                   PA_DBUS_CORE_INTERFACE,
+                                                   signals[SIGNAL_NEW_EXTENSION].name)));
+    pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+    dbus_message_unref(signal);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t extension_unregistered_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    const char *ext_name = call_data;
+    DBusMessage *signal = NULL;
+
+    pa_assert(c);
+    pa_assert(ext_name);
+
+    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                   PA_DBUS_CORE_INTERFACE,
+                                                   signals[SIGNAL_EXTENSION_REMOVED].name)));
+    pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+    dbus_message_unref(signal);
+
+    return PA_HOOK_OK;
+}
+
 pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
     pa_dbusiface_core *c;
     pa_card *card;
@@ -1847,7 +1974,7 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
     pa_assert(core);
 
     c = pa_xnew(pa_dbusiface_core, 1);
-    c->core = core;
+    c->core = pa_core_ref(core);
     c->subscription = pa_subscription_new(core, PA_SUBSCRIPTION_MASK_ALL, subscription_cb, c);
     c->dbus_protocol = pa_dbus_protocol_get(core);
     c->cards = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
@@ -1862,38 +1989,49 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
     c->clients = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
     c->fallback_sink = pa_namereg_get_default_sink(core);
     c->fallback_source = pa_namereg_get_default_source(core);
-
-    for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx))
-        pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(card, OBJECT_PATH));
-
-    for (sink = pa_idxset_first(core->sinks, &idx); sink; sink = pa_idxset_next(core->sinks, &idx)) {
-        device = pa_dbusiface_device_new_sink(sink, OBJECT_PATH);
+    c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol,
+                                                                 PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED,
+                                                                 PA_HOOK_NORMAL,
+                                                                 extension_registered_cb,
+                                                                 c);
+    c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol,
+                                                                   PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED,
+                                                                   PA_HOOK_NORMAL,
+                                                                   extension_unregistered_cb,
+                                                                   c);
+    c->memstats = pa_dbusiface_memstats_new(c, core);
+
+    PA_IDXSET_FOREACH(card, core->cards, idx)
+        pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(c, card));
+
+    PA_IDXSET_FOREACH(sink, core->sinks, idx) {
+        device = pa_dbusiface_device_new_sink(c, sink);
         pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device);
         pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device);
     }
 
-    for (source = pa_idxset_first(core->sources, &idx); source; source = pa_idxset_next(core->sources, &idx)) {
-        device = pa_dbusiface_device_new_source(source, OBJECT_PATH);
+    PA_IDXSET_FOREACH(source, core->sources, idx) {
+        device = pa_dbusiface_device_new_source(c, source);
         pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device);
         pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device);
     }
 
-    for (sink_input = pa_idxset_first(core->sink_inputs, &idx); sink_input; sink_input = pa_idxset_next(core->sink_inputs, &idx))
-        pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(sink_input, OBJECT_PATH));
+    PA_IDXSET_FOREACH(sink_input, core->sink_inputs, idx)
+        pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(c, sink_input));
 
-    for (source_output = pa_idxset_first(core->source_outputs, &idx); source_output; source_output = pa_idxset_next(core->source_outputs, &idx))
-        pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(source_output, OBJECT_PATH));
+    PA_IDXSET_FOREACH(source_output, core->source_outputs, idx)
+        pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(c, source_output));
 
-    for (sample = pa_idxset_first(core->scache, &idx); sample; sample = pa_idxset_next(core->scache, &idx))
-        pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(sample, OBJECT_PATH));
+    PA_IDXSET_FOREACH(sample, core->scache, idx)
+        pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(c, sample));
 
-    for (module = pa_idxset_first(core->modules, &idx); module; module = pa_idxset_next(core->modules, &idx))
-        pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(module, OBJECT_PATH));
+    PA_IDXSET_FOREACH(module, core->modules, idx)
+        pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(module));
 
-    for (client = pa_idxset_first(core->clients, &idx); client; client = pa_idxset_next(core->clients, &idx))
-        pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(client, OBJECT_PATH));
+    PA_IDXSET_FOREACH(client, core->clients, idx)
+        pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(c, client));
 
-    pa_dbus_protocol_add_interface(c->dbus_protocol, OBJECT_PATH, &core_interface_info, c);
+    pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, &core_interface_info, c) >= 0);
 
     return c;
 }
@@ -1949,7 +2087,7 @@ static void free_client_cb(void *p, void *userdata) {
 void pa_dbusiface_core_free(pa_dbusiface_core *c) {
     pa_assert(c);
 
-    pa_dbus_protocol_remove_interface(c->dbus_protocol, OBJECT_PATH, INTERFACE_CORE);
+    pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, core_interface_info.name) >= 0);
 
     pa_subscription_free(c->subscription);
     pa_hashmap_free(c->cards, free_card_cb, NULL);
@@ -1962,8 +2100,89 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) {
     pa_hashmap_free(c->samples, free_sample_cb, NULL);
     pa_hashmap_free(c->modules, free_module_cb, NULL);
     pa_hashmap_free(c->clients, free_client_cb, NULL);
+    pa_hook_slot_free(c->extension_registered_slot);
+    pa_hook_slot_free(c->extension_unregistered_slot);
+    pa_dbusiface_memstats_free(c->memstats);
 
     pa_dbus_protocol_unref(c->dbus_protocol);
+    pa_core_unref(c->core);
 
     pa_xfree(c);
 }
+
+const char *pa_dbusiface_core_get_card_path(pa_dbusiface_core *c, const pa_card *card) {
+    pa_assert(c);
+    pa_assert(card);
+
+    return pa_dbusiface_card_get_path(pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(card->index)));
+}
+
+const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink) {
+    pa_assert(c);
+    pa_assert(sink);
+
+    return pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(sink->index)));
+}
+
+const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source) {
+    pa_assert(c);
+    pa_assert(source);
+
+    return pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(source->index)));
+}
+
+const char *pa_dbusiface_core_get_playback_stream_path(pa_dbusiface_core *c, const pa_sink_input *sink_input) {
+    pa_assert(c);
+    pa_assert(sink_input);
+
+    return pa_dbusiface_stream_get_path(pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(sink_input->index)));
+}
+
+const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const pa_source_output *source_output) {
+    pa_assert(c);
+    pa_assert(source_output);
+
+    return pa_dbusiface_stream_get_path(pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(source_output->index)));
+}
+
+const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module) {
+    pa_assert(c);
+    pa_assert(module);
+
+    return pa_dbusiface_module_get_path(pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(module->index)));
+}
+
+const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_client *client) {
+    pa_assert(c);
+    pa_assert(client);
+
+    return pa_dbusiface_client_get_path(pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(client->index)));
+}
+
+pa_sink *pa_dbusiface_core_get_sink(pa_dbusiface_core *c, const char *object_path) {
+    pa_dbusiface_device *device = NULL;
+
+    pa_assert(c);
+    pa_assert(object_path);
+
+    device = pa_hashmap_get(c->sinks_by_path, object_path);
+
+    if (device)
+        return pa_dbusiface_device_get_sink(device);
+    else
+        return NULL;
+}
+
+pa_source *pa_dbusiface_core_get_source(pa_dbusiface_core *c, const char *object_path) {
+    pa_dbusiface_device *device = NULL;
+
+    pa_assert(c);
+    pa_assert(object_path);
+
+    device = pa_hashmap_get(c->sources_by_path, object_path);
+
+    if (device)
+        return pa_dbusiface_device_get_source(device);
+    else
+        return NULL;
+}