tizen: Import kdbus patches glib_2.78.4_tizen
authorKarol Lewandowski <k.lewandowsk@samsung.com>
Wed, 7 Feb 2024 15:37:21 +0000 (16:37 +0100)
committerKarol Lewandowski <k.lewandowsk@samsung.com>
Wed, 14 Feb 2024 10:08:57 +0000 (11:08 +0100)
This commit imports kdbus support patches to main tree.

The kdbus is applied and built only at last stage of the build,
leaving regular build without any kdbus and glib-core (gvariant)
patches.

packaging/glib2.spec
packaging/kdbus1.patch [new file with mode: 0644]
packaging/kdbus2.patch [new file with mode: 0644]

index c3b9299..52e577f 100644 (file)
@@ -1,11 +1,12 @@
 # Do not create provides from extension .so files because the main package
 # should anchor any reverse-dependencies
-%global __provides_exclude_from ^(.*\\.extension-symbolic)$
+%global __provides_exclude_from ^(.*\\.extension-kdbus|.*\\.extension-symbolic)$
 
 %define baseline 2.78.4
 %define with_systemtap 0
 %define keepstatic 1
 %define build_dir _build
+%define build_dir_kdbus _build_kdbus
 %define build_dir_symbolic _build_symbolic
 
 %define relative_dbus_tests_base_dir %{_libdir}/dbus-tests
@@ -30,6 +31,8 @@ Source6:        macros.glib2
 Source7:        gtk-doc.m4
 Source8:        LICENSE.MIT
 Source99:       baselibs.conf
+Source200:      kdbus1.patch
+Source201:      kdbus2.patch
 Source1001:    glib2.manifest
 BuildRequires:  automake
 BuildRequires:  fdupes
@@ -51,6 +54,8 @@ BuildRequires:  pkgconfig(libffi)
 BuildRequires:  pkgconfig(libpcre2-8) >= 10.32
 BuildRequires:  pkgconfig(mount) >= 2.28
 BuildRequires:  pkgconfig(zlib)
+# Enable support for libdbuspolicy (only for kdbus transport)
+BuildRequires:  pkgconfig(libdbuspolicy1)
 
 %description
 GLib is a general-purpose utility library, which provides many useful
@@ -164,9 +169,25 @@ a main loop abstraction, and so on.
 
 GIO provides a modern, easy-to-use VFS API.
 
+%package -n libgio-extension-kdbus
+Summary:       Extension for libgio to support KDBUS in Tizen
+Requires:      libgio = %{version}-%{release}
+
+%description -n libgio-extension-kdbus
+This modifies libgio to support KDBUS in Tizen.
+
+%package -n libglib-extension-kdbus
+Summary:       Extension for libgio to support KDBUS in Tizen
+Requires:      libgio-extension-kdbus = %{version}-%{release}
+Conflicts:     libglib-extension-symbolic
+
+%description -n libglib-extension-kdbus
+This modifies libgio to support KDBUS in Tizen.
+
 %package -n libglib-extension-symbolic
 Summary:       Extension for libglib to allow the override of internal defined global function.
 Requires:      libglib = %{version}-%{release}
+Conflicts:     libglib-extension-kdbus
 
 %description -n libglib-extension-symbolic
 This modifies libglib to allow the override of internal defined global function.
@@ -235,6 +256,18 @@ fi
        -Db_pie=true
 %meson_build
 
+# Configure kdbus extension build
+%{__patch} -p1 < %{SOURCE200}
+%{__patch} -p1 < %{SOURCE201}
+%define _vpath_builddir %{build_dir_kdbus}
+%meson \
+       --default-library=both \
+       -Dkdbus=true \
+       -Dlibdbuspolicy=true \
+       -Dselinux=disabled \
+       -Db_pie=true
+%meson_build
+
 # compile test-runner for 'dbus-integration-test' framework
 %__cc %{_builddir}/%{name}-%{version}/test-runner.c -DTESTS_DIR='"'%{relative_dbus_tests_base_dir}/test-suites/glib-tests'"' -fPIC -pie -o %{_builddir}/%{name}-%{version}/glib-tests
 
@@ -244,7 +277,12 @@ fi
 %meson_install
 for FILE in %{buildroot}%{_libdir}/libglib*; do mv "$FILE" "$FILE.extension-symbolic"; done
 
-# normal install
+# kdbus extension install and gather required files
+%define _vpath_builddir %{build_dir_kdbus}
+%meson_install
+for FILE in %{buildroot}%{_libdir}/libgio*[0-9a] %{buildroot}%{_libdir}/libglib*[0-9a]; do mv "$FILE" "$FILE.extension-kdbus"; done
+
+# normal install overwriting kdbus extension install
 %define _vpath_builddir %{build_dir}
 %meson_install
 %find_lang glib20 %{?no_lang_C}
@@ -324,6 +362,7 @@ chmod 755 %{buildroot}%{_bindir}/*
 %manifest %{name}.manifest
 %defattr(-, root, root)
 %license COPYING
+%exclude %{_libdir}/libglib*.so.*.extension-kdbus
 %exclude %{_libdir}/libglib*.so.*.extension-symbolic
 %{_libdir}/libglib*.so.*
 
@@ -349,6 +388,7 @@ chmod 755 %{buildroot}%{_bindir}/*
 %manifest %{name}.manifest
 %defattr(-, root, root)
 %license COPYING
+%exclude %{_libdir}/libgio*.so.*.extension-kdbus
 %{_libdir}/libgio*.so.*
 %dir %{_libdir}/gio
 %dir %{_libdir}/gio/modules
@@ -359,6 +399,26 @@ chmod 755 %{buildroot}%{_bindir}/*
 
 %lang_package -f glib20
 
+%post -n libgio-extension-kdbus
+pushd %{_libdir}
+for FILE in libgio*.so.*.extension-kdbus; do mv "$FILE" "${FILE%.extension-kdbus}"; done
+popd
+
+%files -n libgio-extension-kdbus
+%manifest %{name}.manifest
+%license COPYING
+%{_libdir}/libgio*.so.*.extension-kdbus
+
+%post -n libglib-extension-kdbus
+pushd %{_libdir}
+for FILE in libglib*.so.*.extension-kdbus; do mv "$FILE" "${FILE%.extension-kdbus}"; done
+popd
+
+%files -n libglib-extension-kdbus
+%manifest %{name}.manifest
+%license COPYING
+%{_libdir}/libglib*.so.*.extension-kdbus
+
 %post -n libglib-extension-symbolic
 pushd %{_libdir}
 for FILE in libglib*.so.*.extension-symbolic; do mv "$FILE" "${FILE%.extension-symbolic}"; done
@@ -418,6 +478,7 @@ popd
 %defattr(-,root,root)
 %license COPYING
 %{_libdir}/lib*.a
+%{_libdir}/lib*.a.extension-kdbus
 %{_libdir}/lib*.a.extension-symbolic
 
 %files tests
diff --git a/packaging/kdbus1.patch b/packaging/kdbus1.patch
new file mode 100644 (file)
index 0000000..3a8ab17
--- /dev/null
@@ -0,0 +1,4424 @@
+From 43d5579bb0a0394f16c655616ab32adcadc38e4a Mon Sep 17 00:00:00 2001
+From: Karol Lewandowski <k.lewandowsk@samsung.com>
+Date: Tue, 6 Feb 2024 16:00:05 +0100
+Subject: [PATCH] tizen: Import glib & gio adjustments needed for kdbus
+ upstream
+
+This commit imports multiple squashed glib core code needed to support kdbus.
+Originally, the code has been authored by Ryan Lortie and Daving King and
+can be found at:
+
+    https://git.gnome.org/browse/glib/log/?h=wip/kdbus-junk
+
+Original commit logs can be found below (for identification purspose).
+
+.
+
+commit 52ca5a3d8fc7c6a0407f24158c7c5de57aac564b
+Author: Ryan Lortie <desrt@desrt.ca>
+Date:   Fri Aug 21 16:26:03 2015 +0200
+
+    gvariant: substantial rework for kdbus purposes
+    
+    This patch is 'squashed' version of Ryan Lortie's patches from
+    wip/kdbus-junk branch [1].
+    
+    Main changes:
+    - add GVariantVectors utility struct,
+    - support serialising/deserialising to/from GVariantVectors,
+    - support for single precision floats,
+    - GVariantVectors serialisation tests,
+    
+    [1] https://git.gnome.org/browse/glib/log/?h=wip/kdbus-junk
+    
+    https://bugzilla.gnome.org/show_bug.cgi?id=721861
+    
+    Change-Id: If70bd22337b5a9cddac545dce45474eba542d0de
+
+commit 5fa9680246ae47f5a2cdb62a672fcc3ddc0a629e
+Author: Ryan Lortie <desrt@desrt.ca>
+Date:   Fri Aug 21 16:15:25 2015 +0200
+
+    glib-unix: add new internal header glib-linux.h
+    
+    This defines some constants and syscall wrappers for features which may
+    not yet have appeared in the libc.
+    
+    https://bugzilla.gnome.org/show_bug.cgi?id=721861
+    
+    Change-Id: I7e83dadf8be9121f91fa0743bb8c1c612f923e13
+
+commit 6bc456e19258c081a65bdf7d6de27a967300c00c
+Author: David King <dking@redhat.com>
+Date:   Fri Aug 21 16:10:59 2015 +0200
+
+    glib-unix: add function to ensure an fd is sealed
+    
+    Add a function that checks if a fd is sealed and, if it's not, seals it.
+    
+    On Linux this is more or less an operation on memfd.  On other systems,
+    it currently always returns FALSE.
+    
+    https://bugzilla.gnome.org/show_bug.cgi?id=721861
+    
+    Change-Id: I021e40249c2cdd0f1137f6f9f3813f5d4e5083c5
+
+commit e3bcd736dbf8ac67440d839f767d4720450fa686
+Author: Ryan Lortie <desrt@desrt.ca>
+Date:   Fri Aug 21 16:06:15 2015 +0200
+
+    gbytes: substantial rework for kdbus purposes
+    
+    This patch is 'squashed' version of Ryan Lortie's patches from
+    wip/kdbus-junk branch [1].
+    
+    Main changes:
+    
+    - introduce a new type of GBytes: 'inline' -  this allows us to
+      make a single allocation instead of two in the g_bytes_new() case,
+    
+    - new g_bytes_take_zero_copy_fd() function - function takes a memfd,
+      seals it, and creates a GBytes based on it,
+    
+    - add g_bytes_get_zero_copy_fd() function - add a way to get the
+      zero-copy fd back out of a GBytes that was created from one.
+    
+    [1] https://git.gnome.org/browse/glib/log/?h=wip/kdbus-junk
+    
+    https://bugzilla.gnome.org/show_bug.cgi?id=721861
+    
+---
+ docs/reference/glib/glib-sections.txt.in |   6 +
+ docs/reference/glib/gvariant-text.xml    |   7 +-
+ glib/gbytes.c                            | 380 +++++++++---
+ glib/gbytes.h                            |  12 +
+ glib/glib-linux.h                        | 129 ++++
+ glib/glib-private.c                      |   4 +
+ glib/glib-private.h                      |  17 +
+ glib/glib-unix.c                         |  90 +++
+ glib/glib-unix.h                         |   3 +
+ glib/gmessages.h                         |  84 ++-
+ glib/gtestutils.c                        |  19 +
+ glib/gtestutils.h                        |   2 +
+ glib/gvariant-core.c                     | 722 ++++++++++++++---------
+ glib/gvariant-core.h                     |  12 +-
+ glib/gvariant-parser.c                   |  38 +-
+ glib/gvariant-serialiser.c               | 555 +++++++++++++++--
+ glib/gvariant-serialiser.h               |  28 +-
+ glib/gvariant-vectors.c                  | 248 ++++++++
+ glib/gvariant-vectors.h                  |  63 ++
+ glib/gvariant.c                          | 233 ++++++--
+ glib/gvariant.h                          |   5 +
+ glib/gvarianttype.c                      |  13 +-
+ glib/gvarianttype.h                      |   9 +
+ glib/gvarianttypeinfo.c                  |   4 +-
+ glib/meson.build                         |   1 +
+ glib/tests/bytes.c                       |  52 +-
+ glib/tests/gvariant.c                    | 168 +++++-
+ 27 files changed, 2404 insertions(+), 500 deletions(-)
+ create mode 100644 glib/glib-linux.h
+ create mode 100644 glib/gvariant-vectors.c
+ create mode 100644 glib/gvariant-vectors.h
+
+diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in
+index 5258544..8b5efd4 100644
+--- a/docs/reference/glib/glib-sections.txt.in
++++ b/docs/reference/glib/glib-sections.txt.in
+@@ -1129,6 +1129,8 @@ g_set_printerr_handler
+ <SUBSECTION>
+ g_return_if_fail
+ g_return_val_if_fail
++g_return_if_fail_se
++g_return_val_if_fail_se
+ g_return_if_reached
+ g_return_val_if_reached
+ g_warn_if_fail
+@@ -2304,6 +2306,7 @@ GUnixFDSourceFunc
+ g_unix_fd_add
+ g_unix_fd_add_full
+ g_unix_fd_source_new
++g_unix_fd_ensure_zero_copy_safe
+ <SUBSECTION>
+ g_unix_get_passwd_entry
+@@ -2784,12 +2787,14 @@ g_byte_array_free_to_bytes
+ GBytes
+ g_bytes_new
+ g_bytes_new_take
++g_bytes_new_take_zero_copy_fd
+ g_bytes_new_static
+ g_bytes_new_with_free_func
+ g_bytes_new_from_bytes
+ g_bytes_get_data
+ g_bytes_get_region
+ g_bytes_get_size
++g_bytes_get_zero_copy_fd
+ g_bytes_hash
+ g_bytes_equal
+ g_bytes_compare
+@@ -3368,6 +3373,7 @@ g_test_rand_double
+ g_test_rand_double_range
+ g_assert
++g_assert_se
+ g_assert_not_reached
+ g_assert_cmpstr
+diff --git a/docs/reference/glib/gvariant-text.xml b/docs/reference/glib/gvariant-text.xml
+index 55d476e..51e9567 100644
+--- a/docs/reference/glib/gvariant-text.xml
++++ b/docs/reference/glib/gvariant-text.xml
+@@ -224,6 +224,7 @@
+         <literal>handle</literal>,
+         <literal>int64</literal>,
+         <literal>uint64</literal>,
++        <literal>float</literal>,
+         <literal>double</literal>,
+         <literal>string</literal>,
+         <literal>objectpath</literal>,
+@@ -534,9 +535,9 @@
+     Type keywords can be seen as more verbose (and more legible) versions of a common subset of the type codes.
+     The type keywords <literal>boolean</literal>, <literal>byte</literal>, <literal>int16</literal>,
+     <literal>uint16</literal>, <literal>int32</literal>, <literal>uint32</literal>, <literal>handle</literal>,
+-    <literal>int64</literal>, <literal>uint64</literal>, <literal>double</literal>, <literal>string</literal>,
+-    <literal>objectpath</literal> and literal <literal>signature</literal> are each exactly equivalent to their
+-    corresponding type code.
++    <literal>int64</literal>, <literal>uint64</literal>, <literal>float</literal>, <literal>double</literal>,
++    <literal>string</literal>, <literal>objectpath</literal> and literal <literal>signature</literal> are each
++    exactly equivalent to their corresponding type code.
+    </para>
+    <para>
+     Type codes are an <literal>@</literal> ("at" sign) followed by a definite GVariant type string.  Some
+diff --git a/glib/gbytes.c b/glib/gbytes.c
+index 380e39f..72fa764 100644
+--- a/glib/gbytes.c
++++ b/glib/gbytes.c
+@@ -35,6 +35,14 @@
+ #include <glib/grefcount.h>
+ #include <string.h>
++#include <errno.h>
++#include <sys/stat.h>
++#include <sys/types.h>
++
++#ifdef G_OS_UNIX
++#include "glib-unix.h"
++#include <sys/mman.h>
++#endif
+ /**
+  * GBytes:
+@@ -70,13 +78,75 @@
+ /* Keep in sync with glib/tests/bytes.c */
+ struct _GBytes
+ {
+-  gconstpointer data;  /* may be NULL iff (size == 0) */
+-  gsize size;  /* may be 0 */
++  gsize size;
+   gatomicrefcount ref_count;
+-  GDestroyNotify free_func;
+-  gpointer user_data;
++  gint  type_or_fd;
+ };
++typedef struct
++{
++  GBytes bytes;
++#if GLIB_SIZEOF_SIZE_T == 4
++  guint pad;
++#endif
++
++  guchar data[1];
++} GBytesInline;
++
++/* important: the ->data field of GBytesInline should always be 'nicely
++ * aligned'.
++ */
++G_STATIC_ASSERT (G_STRUCT_OFFSET (GBytesInline, data) % (2 * sizeof (gpointer)) == 0);
++G_STATIC_ASSERT (G_STRUCT_OFFSET (GBytesInline, data) % 8 == 0);
++
++
++typedef struct
++{
++  GBytes   bytes;
++
++  gpointer data;
++} GBytesData;
++
++typedef struct
++{
++  GBytesData     data_bytes;
++
++  GDestroyNotify notify;
++  gpointer       user_data;
++} GBytesNotify;
++
++#define G_BYTES_TYPE_INLINE        (-1)
++#define G_BYTES_TYPE_STATIC        (-2)
++#define G_BYTES_TYPE_FREE          (-3)
++#define G_BYTES_TYPE_NOTIFY        (-4)
++
++/* All bytes are either inline or subtypes of GBytesData */
++#define G_BYTES_IS_INLINE(bytes)   ((bytes)->type_or_fd == G_BYTES_TYPE_INLINE)
++#define G_BYTES_IS_DATA(bytes)     (!G_BYTES_IS_INLINE(bytes))
++
++/* More specific subtypes of GBytesData */
++#define G_BYTES_IS_STATIC(bytes)   ((bytes)->type_or_fd == G_BYTES_TYPE_STATIC)
++#define G_BYTES_IS_FREE(bytes)     ((bytes)->type_or_fd == G_BYTES_TYPE_FREE)
++#define G_BYTES_IS_NOTIFY(bytes)   ((bytes)->type_or_fd == G_BYTES_TYPE_NOTIFY)
++
++/* we have a memfd if type_or_fd >= 0 */
++#define G_BYTES_IS_MEMFD(bytes)    ((bytes)->type_or_fd >= 0)
++
++static gpointer
++g_bytes_allocate (guint struct_size,
++                  guint type_or_fd,
++                  gsize data_size)
++{
++  GBytes *bytes;
++
++  bytes = g_slice_alloc (struct_size);
++  bytes->size = data_size;
++  g_atomic_ref_count_init (&bytes->ref_count);
++  bytes->type_or_fd = type_or_fd;
++
++  return bytes;
++}
++
+ /**
+  * g_bytes_new:
+  * @data: (transfer none) (array length=size) (element-type guint8) (nullable):
+@@ -95,11 +165,75 @@ GBytes *
+ g_bytes_new (gconstpointer data,
+              gsize         size)
+ {
++  GBytesInline *bytes;
++
+   g_return_val_if_fail (data != NULL || size == 0, NULL);
+-  return g_bytes_new_take (g_memdup2 (data, size), size);
++  bytes = g_bytes_allocate (G_STRUCT_OFFSET (GBytesInline, data[size]), G_BYTES_TYPE_INLINE, size);
++  memcpy (bytes->data, data, size);
++
++  return (GBytes *) bytes;
++}
++
++/**
++ * g_bytes_new_take_zero_copy_fd:
++ * @fd: a file descriptor capable of being zero-copy-safe
++ *
++ * Creates a new #GBytes from @fd.
++ *
++ * @fd must be capable of being made zero-copy-safe.  In concrete terms,
++ * this means that a call to g_unix_fd_ensure_zero_copy_safe() on @fd
++ * will succeed.  This call will be made before returning.
++ *
++ * This call consumes @fd, transferring ownership to the returned
++ * #GBytes.
++ *
++ * Returns: (transfer full): a new #GBytes
++ *
++ */
++#ifdef G_OS_UNIX
++GBytes *
++g_bytes_new_take_zero_copy_fd (gint fd)
++{
++  GBytes *bytes;
++  struct stat buf;
++
++  /* We already checked this is a memfd... */
++  g_assert_se (fstat (fd, &buf) == 0);
++
++  bytes = g_bytes_new_take_zero_copy_fd_size(fd, buf.st_size);
++
++  if (buf.st_size == 0)
++    {
++      g_assert_se (close (fd) == 0);
++    }
++
++  return bytes;
+ }
++GBytes *
++g_bytes_new_take_zero_copy_fd_size (gint fd, gsize size)
++{
++  GBytesData *bytes;
++
++  g_return_val_if_fail_se (g_unix_fd_ensure_zero_copy_safe (fd), NULL);
++
++  /* We already checked this is a memfd... */
++  if (size == 0)
++    {
++      return g_bytes_new (NULL, 0);
++    }
++
++  bytes = g_bytes_allocate (sizeof (GBytesData), fd, size);
++  bytes->data = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
++  if (bytes->data == MAP_FAILED)
++    /* this is similar to malloc() failing, so do the same... */
++    g_error ("mmap() on memfd failed: %s\n", g_strerror (errno));
++
++  return (GBytes *) bytes;
++}
++#endif /* G_OS_UNIX */
++
+ /**
+  * g_bytes_new_take:
+  * @data: (transfer full) (array length=size) (element-type guint8) (nullable):
+@@ -125,9 +259,13 @@ GBytes *
+ g_bytes_new_take (gpointer data,
+                   gsize    size)
+ {
+-  return g_bytes_new_with_free_func (data, size, g_free, data);
+-}
++  GBytesData *bytes;
++
++  bytes = g_bytes_allocate (sizeof (GBytesData), G_BYTES_TYPE_FREE, size);
++  bytes->data = data;
++  return (GBytes *) bytes;
++}
+ /**
+  * g_bytes_new_static: (skip)
+@@ -148,7 +286,14 @@ GBytes *
+ g_bytes_new_static (gconstpointer data,
+                     gsize         size)
+ {
+-  return g_bytes_new_with_free_func (data, size, NULL, NULL);
++  GBytesData *bytes;
++
++  g_return_val_if_fail (data != NULL || size == 0, NULL);
++
++  bytes = g_bytes_allocate (sizeof (GBytesData), G_BYTES_TYPE_STATIC, size);
++  bytes->data = (gpointer) data;
++
++  return (GBytes *) bytes;
+ }
+ /**
+@@ -179,18 +324,17 @@ g_bytes_new_with_free_func (gconstpointer  data,
+                             GDestroyNotify free_func,
+                             gpointer       user_data)
+ {
+-  GBytes *bytes;
++  GBytesNotify *bytes;
+-  g_return_val_if_fail (data != NULL || size == 0, NULL);
++  if (!free_func)
++    return g_bytes_new_static (data, size);
+-  bytes = g_slice_new (GBytes);
+-  bytes->data = data;
+-  bytes->size = size;
+-  bytes->free_func = free_func;
++  bytes = g_bytes_allocate (sizeof (GBytesNotify), G_BYTES_TYPE_NOTIFY, size);
++  bytes->data_bytes.data = (gpointer) data;
++  bytes->notify = free_func;
+   bytes->user_data = user_data;
+-  g_atomic_ref_count_init (&bytes->ref_count);
+-  return (GBytes *)bytes;
++  return (GBytes *) bytes;
+ }
+ /**
+@@ -221,6 +365,7 @@ g_bytes_new_from_bytes (GBytes  *bytes,
+                         gsize    length)
+ {
+   gchar *base;
++  gchar *data;
+   /* Note that length may be 0. */
+   g_return_val_if_fail (bytes != NULL, NULL);
+@@ -231,18 +376,19 @@ g_bytes_new_from_bytes (GBytes  *bytes,
+   if (offset == 0 && length == bytes->size)
+     return g_bytes_ref (bytes);
+-  base = (gchar *)bytes->data + offset;
++  base = (gchar *)g_bytes_get_data (bytes, NULL) + offset;
+   /* Avoid referencing intermediate GBytes. In practice, this should
+    * only loop once.
+    */
+-  while (bytes->free_func == (gpointer)g_bytes_unref)
+-    bytes = bytes->user_data;
++  while (((GBytesNotify*)bytes)->notify == (gpointer)g_bytes_unref)
++    bytes = ((GBytesNotify*)bytes)->user_data;
++  data = (gchar *)g_bytes_get_data (bytes, NULL);
+   g_return_val_if_fail (bytes != NULL, NULL);
+-  g_return_val_if_fail (base >= (gchar *)bytes->data, NULL);
+-  g_return_val_if_fail (base <= (gchar *)bytes->data + bytes->size, NULL);
+-  g_return_val_if_fail (base + length <= (gchar *)bytes->data + bytes->size, NULL);
++  g_return_val_if_fail (base >= data, NULL);
++  g_return_val_if_fail (base <= data + bytes->size, NULL);
++  g_return_val_if_fail (base + length <= data + bytes->size, NULL);
+   return g_bytes_new_with_free_func (base, length,
+                                      (GDestroyNotify)g_bytes_unref, g_bytes_ref (bytes));
+@@ -268,12 +414,27 @@ g_bytes_new_from_bytes (GBytes  *bytes,
+  */
+ gconstpointer
+ g_bytes_get_data (GBytes *bytes,
+-                  gsize *size)
++                  gsize  *size)
+ {
+   g_return_val_if_fail (bytes != NULL, NULL);
++
+   if (size)
+     *size = bytes->size;
+-  return bytes->data;
++
++  if (G_BYTES_IS_DATA (bytes))
++    {
++      GBytesData *data_bytes = (GBytesData *) bytes;
++
++      return data_bytes->data;
++    }
++  else if (G_BYTES_IS_INLINE (bytes))
++    {
++      GBytesInline *inline_bytes = (GBytesInline *) bytes;
++
++      return inline_bytes->data;
++    }
++  else
++    g_assert_not_reached ();
+ }
+ /**
+@@ -295,6 +456,35 @@ g_bytes_get_size (GBytes *bytes)
+   return bytes->size;
+ }
++/**
++ * g_bytes_get_zero_copy_fd:
++ * @bytes: a #GBytes
++ *
++ * Gets the zero-copy fd from a #GBytes, if it has one.
++ *
++ * Returns -1 if @bytes was not created from a zero-copy fd.
++ *
++ * A #GBytes created with a zero-copy fd may have been internally
++ * converted into another type of #GBytes for any reason at all.  This
++ * function may therefore return -1 at any time, even for a #GBytes that
++ * was created with g_bytes_new_take_zero_copy_fd().
++ *
++ * The returned file descriptor belongs to @bytes.  Do not close it.
++ *
++ * Returns: a file descriptor, or -1
++ *
++ * Since: 2.44
++ */
++gint
++g_bytes_get_zero_copy_fd (GBytes *bytes)
++{
++  g_return_val_if_fail (bytes != NULL, -1);
++
++  if (G_BYTES_IS_MEMFD (bytes))
++    return bytes->type_or_fd;
++  else
++    return -1;
++}
+ /**
+  * g_bytes_ref:
+@@ -333,9 +523,52 @@ g_bytes_unref (GBytes *bytes)
+   if (g_atomic_ref_count_dec (&bytes->ref_count))
+     {
+-      if (bytes->free_func != NULL)
+-        bytes->free_func (bytes->user_data);
+-      g_slice_free (GBytes, bytes);
++      switch (bytes->type_or_fd)
++        {
++        case G_BYTES_TYPE_STATIC:
++          /* data does not need to be freed */
++          g_slice_free (GBytesData, (GBytesData *) bytes);
++          break;
++
++        case G_BYTES_TYPE_INLINE:
++          /* data will be freed along with struct */
++          g_slice_free1 (G_STRUCT_OFFSET (GBytesInline, data[bytes->size]), bytes);
++          break;
++
++        case G_BYTES_TYPE_FREE:
++          {
++            GBytesData *data_bytes = (GBytesData *) bytes;
++
++            g_free (data_bytes->data);
++
++            g_slice_free (GBytesData, data_bytes);
++            break;
++          }
++
++        case G_BYTES_TYPE_NOTIFY:
++          {
++            GBytesNotify *notify_bytes = (GBytesNotify *) bytes;
++
++            /* We don't create GBytesNotify if callback was NULL */
++            (* notify_bytes->notify) (notify_bytes->user_data);
++
++            g_slice_free (GBytesNotify, notify_bytes);
++            break;
++          }
++
++        default:
++          {
++            GBytesData *data_bytes = (GBytesData *) bytes;
++
++            g_assert (bytes->type_or_fd >= 0);
++
++            g_assert_se (munmap (data_bytes->data, bytes->size) == 0);
++            g_assert_se (close (bytes->type_or_fd) == 0);
++
++            g_slice_free (GBytesData, data_bytes);
++            break;
++          }
++        }
+     }
+ }
+@@ -358,14 +591,22 @@ gboolean
+ g_bytes_equal (gconstpointer bytes1,
+                gconstpointer bytes2)
+ {
+-  const GBytes *b1 = bytes1;
+-  const GBytes *b2 = bytes2;
++  gconstpointer d1, d2;
++  gsize s1, s2;
+   g_return_val_if_fail (bytes1 != NULL, FALSE);
+   g_return_val_if_fail (bytes2 != NULL, FALSE);
+-  return b1->size == b2->size &&
+-         (b1->size == 0 || memcmp (b1->data, b2->data, b1->size) == 0);
++  d1 = g_bytes_get_data ((GBytes *) bytes1, &s1);
++  d2 = g_bytes_get_data ((GBytes *) bytes2, &s2);
++
++  if (s1 != s2)
++    return FALSE;
++
++  if (d1 == d2)
++    return TRUE;
++
++  return memcmp (d1, d2, s1) == 0;
+ }
+ /**
+@@ -384,14 +625,18 @@ g_bytes_equal (gconstpointer bytes1,
+ guint
+ g_bytes_hash (gconstpointer bytes)
+ {
+-  const GBytes *a = bytes;
+-  const signed char *p, *e;
++  const guchar *data;
++  const guchar *end;
++  gsize size;
+   guint32 h = 5381;
+   g_return_val_if_fail (bytes != NULL, 0);
+-  for (p = (signed char *)a->data, e = (signed char *)a->data + a->size; p != e; p++)
+-    h = (h << 5) + h + *p;
++  data = g_bytes_get_data ((GBytes *) bytes, &size);
++  end = data + size;
++
++  while (data != end)
++    h = (h << 5) + h + *(data++);
+   return h;
+ }
+@@ -422,43 +667,23 @@ gint
+ g_bytes_compare (gconstpointer bytes1,
+                  gconstpointer bytes2)
+ {
+-  const GBytes *b1 = bytes1;
+-  const GBytes *b2 = bytes2;
++  gconstpointer d1, d2;
++  gsize s1, s2;
+   gint ret;
+   g_return_val_if_fail (bytes1 != NULL, 0);
+   g_return_val_if_fail (bytes2 != NULL, 0);
+-  ret = memcmp (b1->data, b2->data, MIN (b1->size, b2->size));
+-  if (ret == 0 && b1->size != b2->size)
+-      ret = b1->size < b2->size ? -1 : 1;
+-  return ret;
+-}
+-
+-static gpointer
+-try_steal_and_unref (GBytes         *bytes,
+-                     GDestroyNotify  free_func,
+-                     gsize          *size)
+-{
+-  gpointer result;
+-
+-  if (bytes->free_func != free_func || bytes->data == NULL ||
+-      bytes->user_data != bytes->data)
+-    return NULL;
++  d1 = g_bytes_get_data ((GBytes *) bytes1, &s1);
++  d2 = g_bytes_get_data ((GBytes *) bytes2, &s2);
+-  /* Are we the only reference? */
+-  if (g_atomic_ref_count_compare (&bytes->ref_count, 1))
+-    {
+-      *size = bytes->size;
+-      result = (gpointer)bytes->data;
+-      g_slice_free (GBytes, bytes);
+-      return result;
+-    }
++  ret = memcmp (d1, d2, MIN (s1, s2));
++  if (ret == 0 && s1 != s2)
++    ret = s1 < s2 ? -1 : 1;
+-  return NULL;
++  return ret;
+ }
+-
+ /**
+  * g_bytes_unref_to_data:
+  * @bytes: (transfer full): a #GBytes
+@@ -487,20 +712,26 @@ g_bytes_unref_to_data (GBytes *bytes,
+   g_return_val_if_fail (bytes != NULL, NULL);
+   g_return_val_if_fail (size != NULL, NULL);
++
+   /*
+    * Optimal path: if this is was the last reference, then we can return
+    * the data from this GBytes without copying.
+    */
+-
+-  result = try_steal_and_unref (bytes, g_free, size);
+-  if (result == NULL)
++  if (G_BYTES_IS_FREE(bytes) && g_atomic_int_get (&bytes->ref_count) == 1)
+     {
+-      /*
+-       * Copy: Non g_malloc (or compatible) allocator, or static memory,
+-       * so we have to copy, and then unref.
+-       */
+-      result = g_memdup2 (bytes->data, bytes->size);
++      GBytesData *data_bytes = (GBytesData *) bytes;
++
++      result = data_bytes->data;
+       *size = bytes->size;
++
++      g_slice_free (GBytesData, data_bytes);
++    }
++  else
++    {
++      gconstpointer data;
++
++      data = g_bytes_get_data (bytes, size);
++      result = g_memdup (data, *size);
+       g_bytes_unref (bytes);
+     }
+@@ -608,5 +839,8 @@ g_bytes_get_region (GBytes *bytes,
+    *   0 <= offset <= end_offset <= bytes->size
+    */
+-  return ((guchar *) bytes->data) + offset;
+-}
+\ No newline at end of file
++  /* NB: The following line has been adapted to the kdbus rework by Mateusz
++   * and not by a competent programmer. Tread with caution!
++   */
++  return ((guchar *) g_bytes_get_data (bytes, NULL)) + offset;
++}
+diff --git a/glib/gbytes.h b/glib/gbytes.h
+index d934989..9cec5de 100644
+--- a/glib/gbytes.h
++++ b/glib/gbytes.h
+@@ -41,6 +41,15 @@ GLIB_AVAILABLE_IN_ALL
+ GBytes *        g_bytes_new_take                (gpointer        data,
+                                                  gsize           size);
++#ifdef G_OS_UNIX
++GLIB_AVAILABLE_IN_2_44
++GBytes *        g_bytes_new_take_zero_copy_fd   (gint            fd);
++
++GLIB_AVAILABLE_IN_2_44
++GBytes *        g_bytes_new_take_zero_copy_fd_size (gint fd,
++                                                    gsize           size);
++#endif
++
+ GLIB_AVAILABLE_IN_ALL
+ GBytes *        g_bytes_new_static              (gconstpointer   data,
+                                                  gsize           size);
+@@ -63,6 +72,9 @@ gconstpointer   g_bytes_get_data                (GBytes         *bytes,
+ GLIB_AVAILABLE_IN_ALL
+ gsize           g_bytes_get_size                (GBytes         *bytes);
++GLIB_AVAILABLE_IN_2_44
++gint            g_bytes_get_zero_copy_fd        (GBytes         *bytes);
++
+ GLIB_AVAILABLE_IN_ALL
+ GBytes *        g_bytes_ref                     (GBytes         *bytes);
+diff --git a/glib/glib-linux.h b/glib/glib-linux.h
+new file mode 100644
+index 0000000..8f8882c
+--- /dev/null
++++ b/glib/glib-linux.h
+@@ -0,0 +1,129 @@
++/*
++ * Copyright © 2014 Canonical Limited
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the licence, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
++ *
++ * Author: Ryan Lortie <desrt@desrt.ca>
++ */
++
++#ifndef __GLIB_LINUX_H__
++#define __GLIB_LINUX_H__
++
++#include <errno.h>
++
++/* If we know that we are on Linux, add some features, even if they are
++ * not (yet) advertised in the glibc or kernel headers.
++ *
++ * This allows us to use functionality regardless of if it was available
++ * when GLib was compiled or not.
++ *
++ * We take care not to define these things on non-Linux systems where
++ * certain numeric values could mean something different.
++ *
++ * This file is populated on an as-needed basis.
++ *
++ * As things in this file filter into glibc and the distributions we can
++ * remove them from this file and add unconditional dependencies.  Never
++ * add a configure.ac check in order to remove something from this file.
++ *
++ * import: include this header LAST
++ */
++
++#ifdef __linux__
++
++#define GLIB_LINUX
++
++#include <sys/syscall.h>
++
++static inline int
++glib_linux_enosys (void)
++{
++  errno = ENOSYS;
++  return -1;
++}
++
++/* futex */
++#include <linux/futex.h>
++
++static inline int
++glib_linux_futex (int                   *uaddr,
++                  int                    op,
++                  int                    val,
++                  const struct timespec *timeout,
++                  int                   *uaddr2,
++                  int                    val3)
++{
++  return syscall (__NR_futex, uaddr, op, val, timeout, uaddr2, val3);
++}
++
++/* memfd */
++#ifndef MFD_CLOEXEC
++#define MFD_CLOEXEC             0x0001U
++#endif
++
++#ifndef MFD_ALLOW_SEALING
++#define MFD_ALLOW_SEALING       0x0002U
++#endif
++
++#ifndef __NR_memfd_create
++#  if defined __x86_64__
++#    define __NR_memfd_create 319
++#  elif defined i386
++#    define __NR_memfd_create 356
++#  elif defined __arm__
++#    define __NR_memfd_create 385
++#  elif defined __aarch64__
++#    define __NR_memfd_create 279
++#  elif defined _MIPS_SIM
++#    if _MIPS_SIM == _MIPS_SIM_ABI32
++#      define __NR_memfd_create 4354
++#    endif
++#    if _MIPS_SIM == _MIPS_SIM_NABI32
++#      define __NR_memfd_create 6318
++#    endif
++#    if _MIPS_SIM == _MIPS_SIM_ABI64
++#      define __NR_memfd_create 5314
++#    endif
++#  endif
++#endif
++
++static inline int
++glib_linux_memfd_create (const char   *name,
++                         unsigned int  flags)
++{
++#ifdef __NR_memfd_create
++  return syscall (__NR_memfd_create, name, flags);
++#else
++  return glib_linux_enosys ();
++#endif
++}
++
++/* Linux-specific fcntl() operations */
++#ifndef F_LINUX_SPECIFIC_BASE
++#define F_LINUX_SPECIFIC_BASE 1024
++#endif
++
++#ifndef F_ADD_SEALS
++#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
++#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
++
++#define F_SEAL_SEAL     0x0001  /* prevent further seals from being set */
++#define F_SEAL_SHRINK   0x0002  /* prevent file from shrinking */
++#define F_SEAL_GROW     0x0004  /* prevent file from growing */
++#define F_SEAL_WRITE    0x0008  /* prevent writes */
++#endif
++
++#endif /* __linux __ */
++
++#endif /* __GLIB_LINUX_H__ */
+diff --git a/glib/glib-private.c b/glib/glib-private.c
+index 9f4887a..cceaf73 100644
+--- a/glib/glib-private.c
++++ b/glib/glib-private.c
+@@ -53,6 +53,10 @@ glib__private__ (void)
+     g_dir_open_with_errno,
+     g_dir_new_from_dirp,
++    g_variant_to_vectors,
++    g_variant_from_vectors,
++    g_variant_vectors_deinit,
++
+     glib_init,
+ #ifdef G_OS_WIN32
+diff --git a/glib/glib-private.h b/glib/glib-private.h
+index f49e38f..eb1ee50 100644
+--- a/glib/glib-private.h
++++ b/glib/glib-private.h
+@@ -22,6 +22,7 @@
+ #include <glib.h>
+ #include "gwakeup.h"
++#include "gvariant-vectors.h"
+ #include "gstdioprivate.h"
+ /* gcc defines __SANITIZE_ADDRESS__, clang sets the address_sanitizer
+@@ -120,6 +121,13 @@ g_end_ignore_leaks (void)
+ GMainContext *          g_get_worker_context            (void);
+ gboolean                g_check_setuid                  (void);
+ GMainContext *          g_main_context_new_with_next_id (guint next_id);
++void                    g_variant_to_vectors            (GVariant *value,
++                                                         GVariantVectors *vectors);
++GVariant *              g_variant_from_vectors          (const GVariantType *type,
++                                                         GVariantVector     *vectors,
++                                                         gsize               n_vectors,
++                                                         gsize               size,
++                                                         gboolean            trusted);
+ #if (defined (HAVE__SET_THREAD_LOCAL_INVALID_PARAMETER_HANDLER) || \
+      defined (HAVE__SET_INVALID_PARAMETER_HANDLER)) && \
+@@ -182,6 +190,15 @@ typedef struct {
+                                                          guint        flags);
+   GDir *                (* g_dir_new_from_dirp)         (gpointer dirp);
++  void                  (* g_variant_to_vectors)        (GVariant    *value,
++                                                         GVariantVectors *vectors);
++  GVariant *            (* g_variant_from_vectors)      (const GVariantType *type,
++                                                         GVariantVector  *vectors,
++                                                         gsize            n_vectors,
++                                                         gsize size,
++                                                         gboolean trusted);
++  void                  (* g_variant_vectors_deinit)    (GVariantVectors *vectors);
++
+   /* See glib-init.c */
+   void                  (* glib_init)                   (void);
+diff --git a/glib/glib-unix.c b/glib/glib-unix.c
+index df5726c..505923c 100644
+--- a/glib/glib-unix.c
++++ b/glib/glib-unix.c
+@@ -43,6 +43,37 @@ G_STATIC_ASSERT (G_ALIGNOF (GPid) == G_ALIGNOF (pid_t));
+  * might not be true everywhere. */
+ G_STATIC_ASSERT (O_NONBLOCK != FD_CLOEXEC);
++#include <fcntl.h>
++#include <sys/syscall.h>
++#include <unistd.h>
++
++#ifdef __linux__
++
++/* We want to support these features of Linux even when building GLib
++ * against older versions of system headers.  This will allow us to
++ * 'automatically' start supporting a particular feature when GLib is
++ * used with a newer kernel, without recompile.
++ *
++ * This means that we're not changing functionality of GLib simply based
++ * on the set of headers we happen to compile against...
++ */
++
++#ifndef F_LINUX_SPECIFIC_BASE
++#define F_LINUX_SPECIFIC_BASE 1024
++#endif
++
++#ifndef F_ADD_SEALS
++#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
++#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
++
++#define F_SEAL_SEAL     0x0001  /* prevent further seals from being set */
++#define F_SEAL_SHRINK   0x0002  /* prevent file from shrinking */
++#define F_SEAL_GROW     0x0004  /* prevent file from growing */
++#define F_SEAL_WRITE    0x0008  /* prevent writes */
++#endif
++
++#endif
++
+ /**
+  * SECTION:gunix
+  * @title: UNIX-specific utilities and integration
+@@ -404,6 +435,65 @@ g_unix_fd_add (gint              fd,
+   return g_unix_fd_add_full (G_PRIORITY_DEFAULT, fd, condition, function, user_data, NULL);
+ }
++/**
++ * g_unix_fd_ensure_zero_copy_safe:
++ * @fd: a file descriptor
++ *
++ * Checks whether @fd can be use in zero-copy mode. On Linux, this checks that
++ * the descriptor is a memfd, created with memfd_create(), and tries to apply
++ * seals to it so that it can be safely consumed by another process. On other
++ * Unix systems, this function will fail.
++ *
++ * Returns: whether the fd is safe to use in zero-copy mode. Sealing of memfds
++ *          is required for the @fd to be considered safe. If sealing fails, or
++ *          memfds are not available, or the @fd is not a memfd, returns %FALSE
++ *
++ * Since: 2.44
++ **/
++gboolean
++g_unix_fd_ensure_zero_copy_safe (gint fd)
++{
++#ifdef F_GET_SEALS
++  gint seals;
++  const gint IMMUTABLE_SEALS = F_SEAL_WRITE |
++                               F_SEAL_SHRINK |
++                               F_SEAL_GROW |
++                               F_SEAL_SEAL;
++
++  g_return_val_if_fail (fd >= 0, FALSE);
++
++  /* Seal the fd if possible (only on Linux 3.17+, and if the fd was created
++   * with memfd_create()). */
++  seals = fcntl (fd, F_GET_SEALS);
++  if (seals == -1)
++    {
++      g_debug ("Retrieving fd seals failed: %s", g_strerror (errno));
++      return FALSE;
++    }
++
++  /* Seal the fd, if it is not already. */
++  if ((seals & (IMMUTABLE_SEALS)) >= IMMUTABLE_SEALS)
++    {
++      g_debug ("%s", "fd already sealed");
++    }
++  else
++    {
++      gint error;
++
++      error = fcntl (fd, F_ADD_SEALS, IMMUTABLE_SEALS);
++      if (error == -1)
++        {
++          g_debug ("fd sealing failed: %s", g_strerror (errno));
++          return FALSE;
++        }
++    }
++
++  return TRUE;
++#else
++  return FALSE;
++#endif
++}
++
+ /**
+  * g_unix_get_passwd_entry:
+  * @user_name: the username to get the passwd file entry for
+diff --git a/glib/glib-unix.h b/glib/glib-unix.h
+index 7cf4f0d..205c9af 100644
+--- a/glib/glib-unix.h
++++ b/glib/glib-unix.h
+@@ -116,6 +116,9 @@ guint    g_unix_fd_add             (gint              fd,
+                                     GUnixFDSourceFunc function,
+                                     gpointer          user_data);
++GLIB_AVAILABLE_IN_2_44
++gboolean g_unix_fd_ensure_zero_copy_safe (gint fd);
++
+ GLIB_AVAILABLE_IN_2_64
+ struct passwd *g_unix_get_passwd_entry (const gchar  *user_name,
+                                         GError      **error);
+diff --git a/glib/gmessages.h b/glib/gmessages.h
+index eab6d06..825a587 100644
+--- a/glib/gmessages.h
++++ b/glib/gmessages.h
+@@ -606,13 +606,52 @@ GPrintFunc      g_set_printerr_handler  (GPrintFunc      func);
+  * the result is usually that a critical message is logged and @val is
+  * returned from the current function.
+  *
+- * If `G_DISABLE_CHECKS` is defined then the check is not performed.  You
+- * should therefore not depend on any side effects of @expr.
++ * If G_DISABLE_CHECKS is defined then the check is not performed.  You
++ * should therefore not depend on any side effects of @expr.  See
++ * g_return_if_fail_se() for a version that guarantees side effects.
+  *
+  * See g_return_if_fail() for guidance on how to debug failure of this check.
+  */
+ #define g_return_val_if_fail(expr,val) G_STMT_START{ (void)0; }G_STMT_END
++/**
++ * g_return_if_fail_se:
++ * @expr: the expression to check
++ *
++ * Verifies that the expression @expr, usually representing a
++ * precondition, evaluates to %TRUE.
++ *
++ * This is the same as g_return_if_fail() except that @expr is
++ * guaranteed to be evaluated, so it may contain side effects.
++ *
++ * Note: it is still undefined if this function will actually return or
++ * not, or if any side effects of @val will be evaluated.  There is only
++ * a guarantee with respect to side effects of @expr.
++ *
++ * Since: 2.44
++ */
++#define g_return_if_fail_se(expr)         ((void) (expr))
++
++/**
++ * g_return_val_if_fail_se:
++ * @expr: the expression to check
++ * @val: the value to return from the current function
++ *       if the expression is not true
++ *
++ * Verifies that the expression @expr, usually representing a
++ * precondition, evaluates to %TRUE.
++ *
++ * This is the same as g_return_val_if_fail() except that @expr is
++ * guaranteed to be evaluated, so it may contain side effects.
++ *
++ * Note: it is still undefined if this function will actually return or
++ * not, or if any side effects of @val will be evaluated.  There is only
++ * a guarantee with respect to side effects of @expr.
++ *
++ * Since: 2.44
++ */
++#define g_return_val_if_fail_se(expr,val) ((void) (expr))
++
+ /**
+  * g_return_if_reached:
+  *
+@@ -661,27 +700,26 @@ GPrintFunc      g_set_printerr_handler  (GPrintFunc      func);
+       } \
+   } G_STMT_END
+-#define g_return_if_reached() \
+-  G_STMT_START { \
+-    g_log (G_LOG_DOMAIN, \
+-           G_LOG_LEVEL_CRITICAL, \
+-           "file %s: line %d (%s): should not be reached", \
+-           __FILE__, \
+-           __LINE__, \
+-           G_STRFUNC); \
+-    return; \
+-  } G_STMT_END
+-
+-#define g_return_val_if_reached(val) \
+-  G_STMT_START { \
+-    g_log (G_LOG_DOMAIN, \
+-           G_LOG_LEVEL_CRITICAL, \
+-           "file %s: line %d (%s): should not be reached", \
+-           __FILE__, \
+-           __LINE__, \
+-           G_STRFUNC); \
+-    return (val); \
+-  } G_STMT_END
++#define g_return_if_fail_se(expr)         g_return_if_fail((expr))
++#define g_return_val_if_fail_se(expr,val) g_return_val_if_fail((expr), (val))
++
++#define g_return_if_reached()         G_STMT_START{                   \
++     g_log (G_LOG_DOMAIN,                                             \
++          G_LOG_LEVEL_CRITICAL,                                       \
++          "file %s: line %d (%s): should not be reached",             \
++          __FILE__,                                                   \
++          __LINE__,                                                   \
++          G_STRFUNC);                                                 \
++     return;                          }G_STMT_END
++
++#define g_return_val_if_reached(val)  G_STMT_START{                   \
++     g_log (G_LOG_DOMAIN,                                             \
++          G_LOG_LEVEL_CRITICAL,                                       \
++          "file %s: line %d (%s): should not be reached",             \
++          __FILE__,                                                   \
++          __LINE__,                                                   \
++          G_STRFUNC);                                                 \
++     return (val);                    }G_STMT_END
+ #endif /* !G_DISABLE_CHECKS */
+diff --git a/glib/gtestutils.c b/glib/gtestutils.c
+index 483275a..becb4a8 100644
+--- a/glib/gtestutils.c
++++ b/glib/gtestutils.c
+@@ -482,6 +482,25 @@
+  * in unit tests, otherwise the unit tests will be ineffective if compiled with
+  * `G_DISABLE_ASSERT`. Use g_assert_true() and related macros in unit tests
+  * instead.
++ *
++ * For a version which is guaranteed to evaluate side effects in @expr,
++ * see g_assert_se().
++ */
++
++/**
++ * g_assert_se:
++ * @expr: the expression to check
++ *
++ * Debugging macro to terminate the application if the assertion
++ * fails. If the assertion fails (i.e. the expression is not true),
++ * an error message is logged and the application is terminated.
++ *
++ * The check can be turned off in final releases of code by defining
++ * `G_DISABLE_ASSERT` when compiling the application.
++ *
++ * Unlike g_assert(), this macro is guaranteed to evaluate side effects
++ * of @expr, even if checks are disabled.  It is still undefined if the
++ * program will actually be aborted or not.
+  */
+ /**
+diff --git a/glib/gtestutils.h b/glib/gtestutils.h
+index 30ede25..385ae42 100644
+--- a/glib/gtestutils.h
++++ b/glib/gtestutils.h
+@@ -270,6 +270,7 @@ typedef void (*GTestFixtureFunc) (gpointer      fixture,
+ #endif
+ #define g_assert(expr)                  G_STMT_START { (void) 0; } G_STMT_END
++#define g_assert_se(expr)               ((void) (expr))
+ #else /* !G_DISABLE_ASSERT */
+ #define g_assert_not_reached()          G_STMT_START { g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, NULL); } G_STMT_END
+ #define g_assert(expr)                  G_STMT_START { \
+@@ -277,6 +278,7 @@ typedef void (*GTestFixtureFunc) (gpointer      fixture,
+                                                g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
+                                                                          #expr); \
+                                         } G_STMT_END
++#define g_assert_se(expr)               g_assert((expr))
+ #endif /* !G_DISABLE_ASSERT */
+ GLIB_AVAILABLE_IN_ALL
+diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
+index 06e857b..7bd2ff3 100644
+--- a/glib/gvariant-core.c
++++ b/glib/gvariant-core.c
+@@ -22,6 +22,7 @@
+ #include "config.h"
+ #include <glib/gvariant-core.h>
++#include "glib-private.h"
+ #include <glib/gvariant-internal.h>
+ #include <glib/gvariant-serialiser.h>
+@@ -115,21 +116,11 @@ struct _GVariant
+  *            The type_info field never changes during the life of the
+  *            instance, so it can be accessed without a lock.
+  *
+- * size: this is the size of the serialized form for the instance, if it
+- *       is known.  If the instance is in serialized form then it is, by
+- *       definition, known.  If the instance is in tree form then it may
+- *       be unknown (in which case it is -1).  It is possible for the
+- *       size to be known when in tree form if, for example, the user
+- *       has called g_variant_get_size() without calling
+- *       g_variant_get_data().  Additionally, even when the user calls
+- *       g_variant_get_data() the size of the data must first be
+- *       determined so that a large enough buffer can be allocated for
+- *       the data.
+- *
+- *       Once the size is known, it can never become unknown again.
+- *       g_variant_ensure_size() is used to ensure that the size is in
+- *       the known state -- it calculates the size if needed.  After
+- *       that, the size field can be accessed without a lock.
++ * size: this is the size of the serialised form for the instance.  It
++ *       is known for serialised instances and also tree-form instances
++ *       (for which it is calculated at construction time, from the
++ *       known sizes of the children used).  After construction, it
++ *       never changes and therefore can be accessed without a lock.
+  *
+  * contents: a union containing either the information associated with
+  *           holding a value in serialized form or holding a value in
+@@ -309,7 +300,32 @@ g_variant_release_children (GVariant *value)
+   g_free (value->contents.tree.children);
+ }
+-/* This begins the main body of the recursive serializer.
++/* < private >
++ * g_variant_lock_in_tree_form:
++ * @value: a #GVariant
++ *
++ * Locks @value if it is in tree form.
++ *
++ * Returns: %TRUE if @value is now in tree form with the lock acquired
++ */
++static gboolean
++g_variant_lock_in_tree_form (GVariant *value)
++{
++  if (g_atomic_int_get (&value->state) & STATE_SERIALISED)
++    return FALSE;
++
++  g_variant_lock (value);
++
++  if (value->state & STATE_SERIALISED)
++    {
++      g_variant_unlock (value);
++      return FALSE;
++    }
++
++  return TRUE;
++}
++
++/* This begins the main body of the recursive serialiser.
+  *
+  * There are 3 functions here that work as a team with the serializer to
+  * get things done.  g_variant_store() has a trivial role, but as a
+@@ -326,31 +342,19 @@ g_variant_release_children (GVariant *value)
+  * instances are always in serialized form.  For these instances,
+  * storing their serialized form merely involves a memcpy().
+  *
+- * Serialization is a two-step process.  First, the size of the
+- * serialized data must be calculated so that an appropriately-sized
+- * buffer can be allocated.  Second, the data is written into the
+- * buffer.
++ * Converting to serialised form:
+  *
+- * Determining the size:
+- *   The process of determining the size is triggered by a call to
+- *   g_variant_ensure_size() on a container.  This invokes the
+- *   serializer code to determine the size.  The serializer is passed
+- *   g_variant_fill_gvs() as a callback.
+- *
+- *   g_variant_fill_gvs() is called by the serializer on each child of
+- *   the container which, in turn, calls g_variant_ensure_size() on
+- *   itself and fills in the result of its own size calculation.
+- *
+- *   The serializer uses the size information from the children to
+- *   calculate the size needed for the entire container.
++ *   The first step in the process of converting a GVariant to
++ *   serialised form is to allocate a buffer.  The size of the buffer is
++ *   always known because we computed at construction time of the
++ *   GVariant.
+  *
+- * Writing the data:
+  *   After the buffer has been allocated, g_variant_serialise() is
+- *   called on the container.  This invokes the serializer code to write
+- *   the bytes to the container.  The serializer is, again, passed
++ *   called on the container.  This invokes the serialiser code to write
++ *   the bytes to the container.  The serialiser is passed
+  *   g_variant_fill_gvs() as a callback.
+  *
+- *   This time, when g_variant_fill_gvs() is called for each child, the
++ *   At the time that g_variant_fill_gvs() is called for each child, the
+  *   child is given a pointer to a sub-region of the allocated buffer
+  *   where it should write its data.  This is done by calling
+  *   g_variant_store().  In the event that the instance is in serialized
+@@ -363,34 +367,6 @@ g_variant_release_children (GVariant *value)
+  */
+ static void g_variant_fill_gvs (GVariantSerialised *, gpointer);
+-/* < private >
+- * g_variant_ensure_size:
+- * @value: a #GVariant
+- *
+- * Ensures that the ->size field of @value is filled in properly.  This
+- * must be done as a precursor to any serialization of the value in
+- * order to know how large of a buffer is needed to store the data.
+- *
+- * The current thread must hold the lock on @value.
+- */
+-static void
+-g_variant_ensure_size (GVariant *value)
+-{
+-  g_assert (value->state & STATE_LOCKED);
+-
+-  if (value->size == (gsize) -1)
+-    {
+-      gpointer *children;
+-      gsize n_children;
+-
+-      children = (gpointer *) value->contents.tree.children;
+-      n_children = value->contents.tree.n_children;
+-      value->size = g_variant_serialiser_needed_size (value->type_info,
+-                                                      g_variant_fill_gvs,
+-                                                      children, n_children);
+-    }
+-}
+-
+ /* < private >
+  * g_variant_to_serialised:
+  * @value: a #GVariant
+@@ -462,9 +438,12 @@ g_variant_serialise (GVariant *value,
+  *
+  *  - reporting its type
+  *
+- *  - reporting its serialized size (requires knowing the size first)
++ *  - reporting its serialised size
+  *
+- *  - possibly storing its serialized form into the provided buffer
++ *  - possibly storing its serialised form into the provided buffer
++ *
++ * This callback is also used during g_variant_new_from_children() in
++ * order to discover the size and type of each child.
+  */
+ static void
+ g_variant_fill_gvs (GVariantSerialised *serialised,
+@@ -472,10 +451,6 @@ g_variant_fill_gvs (GVariantSerialised *serialised,
+ {
+   GVariant *value = data;
+-  g_variant_lock (value);
+-  g_variant_ensure_size (value);
+-  g_variant_unlock (value);
+-
+   if (serialised->type_info == NULL)
+     serialised->type_info = value->type_info;
+   g_assert (serialised->type_info == value->type_info);
+@@ -511,26 +486,20 @@ g_variant_fill_gvs (GVariantSerialised *serialised,
+  *
+  * Ensures that @value is in serialized form.
+  *
+- * If @value is in tree form then this function ensures that the
+- * serialized size is known and then allocates a buffer of that size and
+- * serializes the instance into the buffer.  The 'children' array is
+- * then released and the instance is set to serialized form based on the
+- * contents of the buffer.
+- *
+- * The current thread must hold the lock on @value.
++ * If @value is in tree form then this function allocates a buffer of
++ * that size and serialises the instance into the buffer.  The
++ * 'children' array is then released and the instance is set to
++ * serialised form based on the contents of the buffer.
+  */
+ static void
+ g_variant_ensure_serialised (GVariant *value)
+ {
+-  g_assert (value->state & STATE_LOCKED);
+-
+-  if (~value->state & STATE_SERIALISED)
++  if (g_variant_lock_in_tree_form (value))
+     {
+       GBytes *bytes;
+       gpointer data;
+       TRACE(GLIB_VARIANT_START_SERIALISE(value, value->type_info));
+-      g_variant_ensure_size (value);
+       data = g_malloc (value->size);
+       g_variant_serialise (value, data);
+@@ -543,12 +512,69 @@ g_variant_ensure_serialised (GVariant *value)
+       value->contents.serialised.checked_offsets_up_to = G_MAXSIZE;
+       value->state |= STATE_SERIALISED;
+       TRACE(GLIB_VARIANT_END_SERIALISE(value, value->type_info));
++
++      g_variant_unlock (value);
++    }
++}
++
++/* Now we have the code to recursively serialise a GVariant into a
++ * GVariantVectors structure.
++ *
++ * We want to do this in cases where the GVariant contains large chunks
++ * of serialised data in order to avoid having to copy this data.
++ *
++ * This generally works the same as normal serialising (co-recursion
++ * with the serialiser) but instead of using a callback we just hard-code
++ * the callback with the name g_variant_callback_write_to_vectors().
++ *
++ * This is a private API that will be used by GDBus.
++ */
++gsize
++g_variant_callback_write_to_vectors (GVariantVectors   *vectors,
++                                     gpointer           data,
++                                     GVariantTypeInfo **type_info)
++{
++  GVariant *value = data;
++
++  if (g_variant_lock_in_tree_form (value))
++    {
++      g_variant_serialiser_write_to_vectors (vectors, value->type_info, value->size,
++                                             (gpointer *) value->contents.tree.children,
++                                             value->contents.tree.n_children);
++
++      g_variant_unlock (value);
+     }
++  else
++    g_variant_vectors_append_gbytes (vectors, value->contents.serialised.bytes,
++                                     value->contents.serialised.data, value->size);
++
++  if (type_info)
++    *type_info = value->type_info;
++
++  return value->size;
++}
++
++/* < private >
++ * g_variant_serialise_to_vectors:
++ * @value: a #GVariant
++ * @vectors: (out): the result
++ *
++ * Serialises @value into @vectors.
++ *
++ * The caller must free @vectors.
++ */
++void
++g_variant_to_vectors (GVariant        *value,
++                      GVariantVectors *vectors)
++{
++  g_variant_vectors_init (vectors);
++
++  g_variant_callback_write_to_vectors (vectors, value, NULL);
+ }
+ /* < private >
+  * g_variant_alloc:
+- * @type: the type of the new instance
++ * @type_info: (transfer full) the type info of the new instance
+  * @serialised: if the instance will be in serialised form
+  * @trusted: if the instance will be trusted
+  *
+@@ -559,73 +585,105 @@ g_variant_ensure_serialised (GVariant *value)
+  * Returns: a new #GVariant with a floating reference
+  */
+ static GVariant *
+-g_variant_alloc (const GVariantType *type,
+-                 gboolean            serialised,
+-                 gboolean            trusted)
++g_variant_alloc (GVariantTypeInfo *type_info,
++                 gboolean          serialised,
++                 gboolean          trusted)
+ {
+   GVariant *value;
+   value = g_slice_new (GVariant);
+-  value->type_info = g_variant_type_info_get (type);
++  value->type_info = type_info;
+   value->state = (serialised ? STATE_SERIALISED : 0) |
+                  (trusted ? STATE_TRUSTED : 0) |
+                  STATE_FLOATING;
+-  value->size = (gssize) -1;
+   g_atomic_ref_count_init (&value->ref_count);
+   value->depth = 0;
+   return value;
+ }
+-/**
+- * g_variant_new_from_bytes:
+- * @type: a #GVariantType
+- * @bytes: a #GBytes
+- * @trusted: if the contents of @bytes are trusted
++/* -- internal -- */
++
++/* < internal >
++ * g_variant_new_from_children:
++ * @type_info: (transfer full) a #GVariantTypeInfo
++ * @children: an array of #GVariant pointers.  Consumed.
++ * @n_children: the length of @children
++ * @trusted: %TRUE if every child in @children in trusted
++ *
++ * Constructs a new tree-mode #GVariant instance.  This is the inner
++ * interface for creation of new tree-mode values that gets called from
++ * various functions in gvariant.c.
+  *
+- * Constructs a new serialized-mode #GVariant instance.  This is the
+- * inner interface for creation of new serialized values that gets
+- * called from various functions in gvariant.c.
++ * @children is consumed by this function.  g_free() will be called on
++ * it some time later.
++ *
++ * Returns: a new #GVariant with a floating reference
++ */
++GVariant *
++g_variant_new_from_children (GVariantTypeInfo  *type_info,
++                             GVariant         **children,
++                             gsize              n_children,
++                             gboolean           trusted)
++{
++  GVariant *value;
++
++  value = g_variant_alloc (type_info, FALSE, trusted);
++  value->contents.tree.children = children;
++  value->contents.tree.n_children = n_children;
++  value->size = g_variant_serialiser_needed_size (value->type_info, g_variant_fill_gvs,
++                                                  (gpointer *) children, n_children);
++  TRACE(GLIB_VARIANT_FROM_CHILDREN(value, value->type_info, value->ref_count, value->state));
++
++  return value;
++}
++
++/* < internal >
++ * g_variant_new_serialised:
++ * @type_info: (transfer full): a #GVariantTypeInfo
++ * @bytes: (transfer full): the #GBytes holding @data
++ * @data: a pointer to the serialised data
++ * @size: the size of @data, in bytes
++ * @trusted: %TRUE if @data is trusted
++ *
++ * Constructs a new serialised #GVariant instance.  This is the inner
++ * interface for creation of new serialised values that gets called from
++ * various functions in gvariant.c.
+  *
+- * A reference is taken on @bytes.
++ * @bytes is consumed by this function.  g_bytes_unref() will be called
++ * on it some time later.
+  *
+  * The data in @bytes must be aligned appropriately for the @type being loaded.
+  * Otherwise this function will internally create a copy of the memory (since
+  * GLib 2.60) or (in older versions) fail and exit the process.
+  *
+- * Returns: (transfer none): a new #GVariant with a floating reference
+- *
+- * Since: 2.36
++ * Returns: a new #GVariant with a floating reference
+  */
+ GVariant *
+-g_variant_new_from_bytes (const GVariantType *type,
+-                          GBytes             *bytes,
+-                          gboolean            trusted)
++g_variant_new_serialised (GVariantTypeInfo *type_info,
++                          GBytes           *bytes,
++                          gconstpointer     data,
++                          gsize             size,
++                          gboolean          trusted)
+ {
+   GVariant *value;
+   guint alignment;
+-  gsize size;
++  gsize fixed_size;
+   GBytes *owned_bytes = NULL;
+-  GVariantSerialised serialised;
+-  value = g_variant_alloc (type, TRUE, trusted);
++  value = g_variant_alloc (type_info, TRUE, trusted);
++  value->contents.serialised.data = data;
++  value->size = size;
+   g_variant_type_info_query (value->type_info,
+-                             &alignment, &size);
+-
++                             &alignment, &fixed_size);
+   /* Ensure the alignment is correct. This is a huge performance hit if it’s
+    * not correct, but that’s better than aborting if a caller provides data
+    * with the wrong alignment (which is likely to happen very occasionally, and
+    * only cause an abort on some architectures — so is unlikely to be caught
+    * in testing). Callers can always actively ensure they use the correct
+    * alignment to avoid the performance hit. */
+-  serialised.type_info = value->type_info;
+-  serialised.data = (guchar *) g_bytes_get_data (bytes, &serialised.size);
+-  serialised.depth = 0;
+-  serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
+-  serialised.checked_offsets_up_to = trusted ? G_MAXSIZE : 0;
+-
+-  if (!g_variant_serialised_check (serialised))
++  if ((alignment & (gsize) g_bytes_get_data (bytes, NULL)) != 0)
+     {
+ #ifdef HAVE_POSIX_MEMALIGN
+       gpointer aligned_data = NULL;
+@@ -641,37 +699,43 @@ g_variant_new_from_bytes (const GVariantType *type,
+       if (aligned_size != 0)
+         memcpy (aligned_data, g_bytes_get_data (bytes, NULL), aligned_size);
+-      bytes = owned_bytes = g_bytes_new_with_free_func (aligned_data,
+-                                                        aligned_size,
+-                                                        free, aligned_data);
++      owned_bytes = g_bytes_new_with_free_func (aligned_data,
++                                                aligned_size,
++                                                free, aligned_data);
+       aligned_data = NULL;
+ #else
+       /* NOTE: there may be platforms that lack posix_memalign() and also
+        * have malloc() that returns non-8-aligned.  if so, we need to try
+        * harder here.
+        */
+-      bytes = owned_bytes = g_bytes_new (g_bytes_get_data (bytes, NULL),
+-                                         g_bytes_get_size (bytes));
++      owned_bytes = g_bytes_new (g_bytes_get_data (bytes, NULL),
++                                 g_bytes_get_size (bytes));
+ #endif
++      g_bytes_unref (bytes);
++      bytes = g_bytes_ref (owned_bytes);
+     }
+-  value->contents.serialised.bytes = g_bytes_ref (bytes);
++  value->contents.serialised.bytes = bytes;
+-  if (size && g_bytes_get_size (bytes) != size)
++  if G_UNLIKELY (fixed_size && size != fixed_size)
+     {
+       /* Creating a fixed-sized GVariant with a bytes of the wrong
+        * size.
+        *
+        * We should do the equivalent of pulling a fixed-sized child out
+-       * of a brozen container (ie: data is NULL size is equal to the correct
++       * of a broken container (ie: data is NULL size is equal to the correct
+        * fixed size).
++       *
++       * This really ought not to happen if the data is trusted...
++       */
++      if (trusted)
++        g_error ("Attempting to create a trusted GVariant instance out of invalid data");
++
++      /* We hang on to the GBytes (even though we don't use it anymore)
++       * because every GVariant must have a GBytes.
+        */
+       value->contents.serialised.data = NULL;
+-      value->size = size;
+-    }
+-  else
+-    {
+-      value->contents.serialised.data = g_bytes_get_data (bytes, &value->size);
++      value->size = fixed_size;
+     }
+   value->contents.serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
+@@ -684,40 +748,6 @@ g_variant_new_from_bytes (const GVariantType *type,
+   return value;
+ }
+-/* -- internal -- */
+-
+-/* < internal >
+- * g_variant_new_from_children:
+- * @type: a #GVariantType
+- * @children: an array of #GVariant pointers.  Consumed.
+- * @n_children: the length of @children
+- * @trusted: %TRUE if every child in @children is trusted
+- *
+- * Constructs a new tree-mode #GVariant instance.  This is the inner
+- * interface for creation of new serialized values that gets called from
+- * various functions in gvariant.c.
+- *
+- * @children is consumed by this function.  g_free() will be called on
+- * it some time later.
+- *
+- * Returns: a new #GVariant with a floating reference
+- */
+-GVariant *
+-g_variant_new_from_children (const GVariantType  *type,
+-                             GVariant           **children,
+-                             gsize                n_children,
+-                             gboolean             trusted)
+-{
+-  GVariant *value;
+-
+-  value = g_variant_alloc (type, FALSE, trusted);
+-  value->contents.tree.children = children;
+-  value->contents.tree.n_children = n_children;
+-  TRACE(GLIB_VARIANT_FROM_CHILDREN(value, value->type_info, value->ref_count, value->state));
+-
+-  return value;
+-}
+-
+ /* < internal >
+  * g_variant_get_type_info:
+  * @value: a #GVariant
+@@ -756,6 +786,191 @@ g_variant_is_trusted (GVariant *value)
+   return (value->state & STATE_TRUSTED) != 0;
+ }
++/* < internal >
++ * g_variant_get_serialised:
++ * @value: a #GVariant
++ * @bytes: (out) (transfer none): a location to store the #GBytes
++ * @size: (out): a location to store the size of the returned data
++ *
++ * Ensures that @value is in serialised form and returns information
++ * about it.  This is called from various APIs in gvariant.c
++ *
++ * Returns: data, of length @size
++ */
++gconstpointer
++g_variant_get_serialised (GVariant  *value,
++                          GBytes   **bytes,
++                          gsize     *size)
++{
++  g_variant_ensure_serialised (value);
++
++  if (bytes)
++    *bytes = value->contents.serialised.bytes;
++
++  *size = value->size;
++
++  return value->contents.serialised.data;
++}
++
++static GVariant *
++g_variant_vector_deserialise (GVariantTypeInfo *type_info,
++                              GVariantVector   *first_vector,
++                              GVariantVector   *last_vector,
++                              gsize             size,
++                              gboolean          trusted,
++                              GArray           *unpacked_children)
++{
++  g_assert (size > 0);
++
++  if (first_vector < last_vector)
++    {
++      GVariantVector *vector = first_vector;
++      const guchar *end_pointer;
++      GVariant **children;
++      guint save_point;
++      guint n_children;
++      gboolean failed;
++      guint i;
++
++      end_pointer = last_vector->data.pointer + last_vector->size;
++      save_point = unpacked_children->len;
++
++      if (!g_variant_serialiser_unpack_all (type_info, end_pointer, last_vector->size, size, unpacked_children))
++        {
++          for (i = save_point; i < unpacked_children->len; i++)
++            g_variant_type_info_unref (g_array_index (unpacked_children, GVariantUnpacked, i).type_info);
++          g_array_set_size (unpacked_children, save_point);
++
++          g_variant_type_info_unref (type_info);
++
++          return NULL;
++        }
++
++      n_children = unpacked_children->len - save_point;
++      children = g_new (GVariant *, n_children);
++      failed = FALSE;
++
++      for (i = 0; i < n_children; i++)
++        {
++          GVariantUnpacked *unpacked = &g_array_index (unpacked_children, GVariantUnpacked, save_point + i);
++          const guchar *resume_at_data;
++          gsize resume_at_size;
++          GVariantVector *fv;
++
++          /* Skip the alignment.
++           *
++           * We can destroy vectors because we won't be going back.
++           *
++           * We do a >= compare because we want to go to the next vector
++           * if it is the start of our child.
++           */
++          while (unpacked->skip >= vector->size)
++            {
++              unpacked->skip -= vector->size;
++              vector++;
++            }
++          g_assert (vector <= last_vector);
++
++          fv = vector;
++          fv->data.pointer += unpacked->skip;
++          fv->size -= unpacked->skip;
++
++          if (unpacked->size == 0)
++            {
++              children[i] = g_variant_new_serialised (unpacked->type_info, g_bytes_new (NULL, 0), NULL, 0, trusted);
++              g_variant_ref_sink (children[i]);
++              continue;
++            }
++
++          /* Now skip to the end, according to 'size'.
++           *
++           * We cannot destroy everything here because we will probably
++           * end up reusing the last one.
++           *
++           * We do a > compare because we want to stay on this vector if
++           * it is the end of our child.
++           */
++          size = unpacked->size;
++          while (unpacked->size > vector->size)
++            {
++              unpacked->size -= vector->size;
++              vector++;
++            }
++          g_assert (vector <= last_vector);
++
++          /* We have to modify the vectors for the benefit of the
++           * recursive step.  We also have to remember where we left
++           * off, keeping in mind that the recursive step may itself
++           * modify the vectors.
++           */
++          resume_at_data = vector->data.pointer + unpacked->size;
++          resume_at_size = vector->size - unpacked->size;
++          vector->size = unpacked->size;
++
++          children[i] = g_variant_vector_deserialise (unpacked->type_info, fv, vector,
++                                                      size, trusted, unpacked_children);
++
++          vector->data.pointer = resume_at_data;
++          vector->size = resume_at_size;
++
++          if (children[i])
++            g_variant_ref_sink (children[i]);
++          else
++            failed = TRUE;
++        }
++
++      /* We consumed all the type infos */
++      g_array_set_size (unpacked_children, save_point);
++
++      if G_UNLIKELY (failed)
++        {
++          for (i = 0; i < n_children; i++)
++            if (children[i])
++              g_variant_unref (children[i]);
++
++          g_variant_type_info_unref (type_info);
++          g_free (children);
++
++          return NULL;
++        }
++
++      return g_variant_new_from_children (type_info, children, n_children, trusted);
++    }
++  else
++    {
++      g_assert (first_vector == last_vector);
++      g_assert (size == first_vector->size);
++
++      return g_variant_new_serialised (type_info, g_bytes_ref (first_vector->gbytes),
++                                       first_vector->data.pointer, size, trusted);
++    }
++}
++
++GVariant *
++g_variant_from_vectors (const GVariantType *type,
++                        GVariantVector     *vectors,
++                        gsize               n_vectors,
++                        gsize               size,
++                        gboolean            trusted)
++{
++  GVariant *result;
++  GArray *tmp;
++
++  g_return_val_if_fail (vectors != NULL || n_vectors == 0, NULL);
++
++  if (size == 0)
++    return g_variant_new_serialised (g_variant_type_info_get (type), g_bytes_new (NULL, 0), NULL, 0, trusted);
++
++  tmp = g_array_new (FALSE, FALSE, sizeof (GVariantUnpacked));
++  result = g_variant_vector_deserialise (g_variant_type_info_get (type),
++                                         vectors, vectors + n_vectors - 1, size, trusted, tmp);
++  if (result)
++    g_variant_ref_sink (result);
++  g_array_free (tmp, TRUE);
++
++  return result;
++}
++
+ /* < internal >
+  * g_variant_get_depth:
+  * @value: a #GVariant
+@@ -982,10 +1197,6 @@ g_variant_is_floating (GVariant *value)
+ gsize
+ g_variant_get_size (GVariant *value)
+ {
+-  g_variant_lock (value);
+-  g_variant_ensure_size (value);
+-  g_variant_unlock (value);
+-
+   return value->size;
+ }
+@@ -1026,56 +1237,11 @@ g_variant_get_size (GVariant *value)
+ gconstpointer
+ g_variant_get_data (GVariant *value)
+ {
+-  g_variant_lock (value);
+   g_variant_ensure_serialised (value);
+-  g_variant_unlock (value);
+   return value->contents.serialised.data;
+ }
+-/**
+- * g_variant_get_data_as_bytes:
+- * @value: a #GVariant
+- *
+- * Returns a pointer to the serialized form of a #GVariant instance.
+- * The semantics of this function are exactly the same as
+- * g_variant_get_data(), except that the returned #GBytes holds
+- * a reference to the variant data.
+- *
+- * Returns: (transfer full): A new #GBytes representing the variant data
+- *
+- * Since: 2.36
+- */ 
+-GBytes *
+-g_variant_get_data_as_bytes (GVariant *value)
+-{
+-  const gchar *bytes_data;
+-  const gchar *data;
+-  gsize bytes_size;
+-  gsize size;
+-
+-  g_variant_lock (value);
+-  g_variant_ensure_serialised (value);
+-  g_variant_unlock (value);
+-
+-  bytes_data = g_bytes_get_data (value->contents.serialised.bytes, &bytes_size);
+-  data = value->contents.serialised.data;
+-  size = value->size;
+-
+-  if (data == NULL)
+-    {
+-      g_assert (size == 0);
+-      data = bytes_data;
+-    }
+-
+-  if (data == bytes_data && size == bytes_size)
+-    return g_bytes_ref (value->contents.serialised.bytes);
+-  else
+-    return g_bytes_new_from_bytes (value->contents.serialised.bytes,
+-                                   data - bytes_data, size);
+-}
+-
+-
+ /**
+  * g_variant_n_children:
+  * @value: a container #GVariant
+@@ -1101,15 +1267,14 @@ g_variant_n_children (GVariant *value)
+ {
+   gsize n_children;
+-  g_variant_lock (value);
+-
+-  if (value->state & STATE_SERIALISED)
++  if (g_variant_lock_in_tree_form (value))
++    {
++      n_children = value->contents.tree.n_children;
++      g_variant_unlock (value);
++    }
++  else
+     n_children = g_variant_serialised_n_children (
+         g_variant_to_serialised (value));
+-  else
+-    n_children = value->contents.tree.n_children;
+-
+-  g_variant_unlock (value);
+   return n_children;
+ }
+@@ -1151,75 +1316,63 @@ GVariant *
+ g_variant_get_child_value (GVariant *value,
+                            gsize     index_)
+ {
++  GVariant *child;
++
+   g_return_val_if_fail (value->depth < G_MAXSIZE, NULL);
+-  if (~g_atomic_int_get (&value->state) & STATE_SERIALISED)
++  if (g_variant_lock_in_tree_form (value))
+     {
+-      /* g_variant_serialised_get_child() does its own checks on index_ */
+-      g_return_val_if_fail (index_ < g_variant_n_children (value), NULL);
++      /*
++       * g_variant_serialised_get_child() does its own checks on index_.
++       * Additionally, it would be preferable to use g_variant_n_children.
++       * However, it would try to lock the value again.
++       */
++      g_return_val_if_fail (index_ < value->contents.tree.n_children, NULL);
+-      g_variant_lock (value);
++      child = g_variant_ref (value->contents.tree.children[index_]);
++      g_variant_unlock (value);
++    }
++  else
++    {
++      GVariantSerialised serialised = g_variant_to_serialised (value);
++      GVariantSerialised s_child;
+-      if (~value->state & STATE_SERIALISED)
++      /* get the serialiser to extract the serialised data for the child
++       * from the serialised data for the container
++       */
++      s_child = g_variant_serialised_get_child (serialised, index_);
++
++      /* Update the cached ordered_offsets_up_to, since @serialised will be thrown away when this function exits */
++      value->contents.serialised.ordered_offsets_up_to = MAX (value->contents.serialised.ordered_offsets_up_to, serialised.ordered_offsets_up_to);
++      value->contents.serialised.checked_offsets_up_to = MAX (value->contents.serialised.checked_offsets_up_to, serialised.checked_offsets_up_to);
++
++      /* Check whether this would cause nesting too deep. If so, return a fake
++       * child. The only situation we expect this to happen in is with a variant,
++       * as all other deeply-nested types have a static type, and hence should
++       * have been rejected earlier. In the case of a variant whose nesting plus
++       * the depth of its child is too great, return a unit variant () instead of
++       * the real child. */
++      if (!(value->state & STATE_TRUSTED) &&
++          g_variant_type_info_query_depth (s_child.type_info) >=
++          G_VARIANT_MAX_RECURSION_DEPTH - value->depth)
+         {
+-          GVariant *child;
+-
+-          child = g_variant_ref (value->contents.tree.children[index_]);
+-          g_variant_unlock (value);
+-
+-          return child;
++          g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT));
++          g_variant_type_info_unref (s_child.type_info);
++          return g_variant_new_tuple (NULL, 0);
+         }
+-      g_variant_unlock (value);
++      /* create a new serialised instance out of it */
++      child = g_variant_new_serialised (s_child.type_info,
++                                        g_bytes_ref (value->contents.serialised.bytes),
++                                        s_child.data, s_child.size,
++                                        value->state & STATE_TRUSTED);
++      child->state &= ~STATE_FLOATING;
++      child->depth = value->depth + 1;
+     }
+-  {
+-    GVariantSerialised serialised = g_variant_to_serialised (value);
+-    GVariantSerialised s_child;
+-    GVariant *child;
+-
+-    /* get the serializer to extract the serialized data for the child
+-     * from the serialized data for the container
+-     */
+-    s_child = g_variant_serialised_get_child (serialised, index_);
+-
+-    /* Update the cached ordered_offsets_up_to, since @serialised will be thrown away when this function exits */
+-    value->contents.serialised.ordered_offsets_up_to = MAX (value->contents.serialised.ordered_offsets_up_to, serialised.ordered_offsets_up_to);
+-    value->contents.serialised.checked_offsets_up_to = MAX (value->contents.serialised.checked_offsets_up_to, serialised.checked_offsets_up_to);
+-
+-    /* Check whether this would cause nesting too deep. If so, return a fake
+-     * child. The only situation we expect this to happen in is with a variant,
+-     * as all other deeply-nested types have a static type, and hence should
+-     * have been rejected earlier. In the case of a variant whose nesting plus
+-     * the depth of its child is too great, return a unit variant () instead of
+-     * the real child. */
+-    if (!(value->state & STATE_TRUSTED) &&
+-        g_variant_type_info_query_depth (s_child.type_info) >=
+-        G_VARIANT_MAX_RECURSION_DEPTH - value->depth)
+-      {
+-        g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT));
+-        g_variant_type_info_unref (s_child.type_info);
+-        return g_variant_new_tuple (NULL, 0);
+-      }
++  TRACE(GLIB_VARIANT_FROM_PARENT(child, child->type_info, child->ref_count, child->state, value));
+-    /* create a new serialized instance out of it */
+-    child = g_slice_new (GVariant);
+-    child->type_info = s_child.type_info;
+-    child->state = (value->state & STATE_TRUSTED) |
+-                   STATE_SERIALISED;
+-    child->size = s_child.size;
+-    g_atomic_ref_count_init (&child->ref_count);
+-    child->depth = value->depth + 1;
+-    child->contents.serialised.bytes =
+-      g_bytes_ref (value->contents.serialised.bytes);
+-    child->contents.serialised.data = s_child.data;
+-    child->contents.serialised.ordered_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.ordered_offsets_up_to;
+-    child->contents.serialised.checked_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.checked_offsets_up_to;
+-
+-    TRACE(GLIB_VARIANT_FROM_PARENT(child, child->type_info, child->ref_count, child->state, value));
+-
+-    return child;
+-  }
++  return child;
+ }
+ /**
+@@ -1316,19 +1469,18 @@ void
+ g_variant_store (GVariant *value,
+                  gpointer  data)
+ {
+-  g_variant_lock (value);
+-
+-  if (value->state & STATE_SERIALISED)
++  if (g_variant_lock_in_tree_form (value))
++    {
++      g_variant_serialise (value, data);
++      g_variant_unlock (value);
++    }
++  else
+     {
+       if (value->contents.serialised.data != NULL)
+         memcpy (data, value->contents.serialised.data, value->size);
+       else
+         memset (data, 0, value->size);
+     }
+-  else
+-    g_variant_serialise (value, data);
+-
+-  g_variant_unlock (value);
+ }
+ /**
+@@ -1356,9 +1508,13 @@ g_variant_store (GVariant *value,
+ gboolean
+ g_variant_is_normal_form (GVariant *value)
+ {
+-  if (value->state & STATE_TRUSTED)
++  if (g_atomic_int_get (&value->state) & STATE_TRUSTED)
+     return TRUE;
++  /* We always take the lock here because we expect to find that the
++   * value is in normal form and in that case, we need to update the
++   * state, which requires holding the lock.
++   */
+   g_variant_lock (value);
+   if (value->depth >= G_VARIANT_MAX_RECURSION_DEPTH)
+diff --git a/glib/gvariant-core.h b/glib/gvariant-core.h
+index fb5f4bf..b09ac1b 100644
+--- a/glib/gvariant-core.h
++++ b/glib/gvariant-core.h
+@@ -27,15 +27,25 @@
+ /* gvariant-core.c */
+-GVariant *              g_variant_new_from_children                     (const GVariantType  *type,
++GVariant *              g_variant_new_from_children                     (GVariantTypeInfo    *type_info,
+                                                                          GVariant           **children,
+                                                                          gsize                n_children,
+                                                                          gboolean             trusted);
++GVariant *              g_variant_new_serialised                        (GVariantTypeInfo    *type_info,
++                                                                         GBytes              *bytes,
++                                                                         gconstpointer        data,
++                                                                         gsize                size,
++                                                                         gboolean             trusted);
++
+ gboolean                g_variant_is_trusted                            (GVariant            *value);
+ GVariantTypeInfo *      g_variant_get_type_info                         (GVariant            *value);
++gconstpointer           g_variant_get_serialised                        (GVariant            *value,
++                                                                         GBytes             **bytes,
++                                                                         gsize               *size);
++
+ gsize                   g_variant_get_depth                             (GVariant            *value);
+ GVariant *              g_variant_maybe_get_child_value                 (GVariant            *value,
+diff --git a/glib/gvariant-parser.c b/glib/gvariant-parser.c
+index 7973ecf..fe7ef4e 100644
+--- a/glib/gvariant-parser.c
++++ b/glib/gvariant-parser.c
+@@ -470,7 +470,13 @@ pattern_coalesce (const gchar *left,
+               (*one)++;
+             }
+-          else if (**one == 'N' && strchr ("ynqiuxthd", **the_other))
++          else if (**one == 'N' && strchr ("ynqiuxthfd", **the_other))
++            {
++              *out++ = *(*the_other)++;
++              (*one)++;
++            }
++
++          else if (**one == 'D' && (**the_other == 'f' || **the_other == 'd'))
+             {
+               *out++ = *(*the_other)++;
+               (*one)++;
+@@ -631,6 +637,10 @@ ast_resolve (AST     *ast,
+         pattern[j++] = 'i';
+         break;
++      case 'D':
++        pattern[j++] = 'd';
++        break;
++
+       default:
+         pattern[j++] = pattern[i];
+         break;
+@@ -1342,7 +1352,7 @@ dictionary_get_pattern (AST     *ast,
+   /* the basic types,
+    * plus undetermined number type and undetermined string type.
+    */
+-  if (!strchr ("bynqiuxthdsogNS", key_char))
++  if (!strchr ("bynqiuxthfdsogNDS", key_char))
+     {
+       ast_set_error (ast, error, NULL,
+                      G_VARIANT_PARSE_ERROR_BASIC_TYPE_EXPECTED,
+@@ -1909,7 +1919,7 @@ number_get_pattern (AST     *ast,
+       (!g_str_has_prefix (number->token, "0x") && strchr (number->token, 'e')) ||
+       strstr (number->token, "inf") ||
+       strstr (number->token, "nan"))
+-    return g_strdup ("Md");
++    return g_strdup ("MD");
+   return g_strdup ("MN");
+ }
+@@ -1934,17 +1944,16 @@ number_get_value (AST                 *ast,
+   Number *number = (Number *) ast;
+   const gchar *token;
+   gboolean negative;
+-  gboolean floating;
+   guint64 abs_val;
+   gdouble dbl_val;
++  gchar typechar;
+   gchar *end;
++  typechar = *g_variant_type_peek_string (type);
+   token = number->token;
+-  if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
++  if (typechar == 'f' || typechar == 'd')
+     {
+-      floating = TRUE;
+-
+       errno = 0;
+       dbl_val = g_ascii_strtod (token, &end);
+       if (dbl_val != 0.0 && errno == ERANGE)
+@@ -1961,7 +1970,6 @@ number_get_value (AST                 *ast,
+     }
+   else
+     {
+-      floating = FALSE;
+       negative = token[0] == '-';
+       if (token[0] == '-')
+         token++;
+@@ -1997,10 +2005,7 @@ number_get_value (AST                 *ast,
+       return NULL;
+      }
+-  if (floating)
+-    return g_variant_new_double (dbl_val);
+-
+-  switch (*g_variant_type_peek_string (type))
++  switch (typechar)
+     {
+     case 'y':
+       if (negative || abs_val > G_MAXUINT8)
+@@ -2054,6 +2059,12 @@ number_get_value (AST                 *ast,
+       return g_variant_new_handle (negative ?
+                                    -((gint32) abs_val) : ((gint32) abs_val));
++    case 'f':
++      return g_variant_new_float (dbl_val);
++
++    case 'd':
++      return g_variant_new_double (dbl_val);
++
+     default:
+       return ast_type_error (ast, type, error);
+     }
+@@ -2350,6 +2361,9 @@ typedecl_parse (TokenStream  *stream,
+       else if (token_stream_consume (stream, "uint64"))
+         type = g_variant_type_copy (G_VARIANT_TYPE_UINT64);
++      else if (token_stream_consume (stream, "float"))
++        type = g_variant_type_copy (G_VARIANT_TYPE_FLOAT);
++
+       else if (token_stream_consume (stream, "double"))
+         type = g_variant_type_copy (G_VARIANT_TYPE_DOUBLE);
+diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
+index 4e4a73a..40a891a 100644
+--- a/glib/gvariant-serialiser.c
++++ b/glib/gvariant-serialiser.c
+@@ -278,6 +278,27 @@ gvs_fixed_sized_maybe_get_child (GVariantSerialised value,
+   return value;
+ }
++static gboolean
++gvs_fixed_sized_maybe_unpack_all (GVariantTypeInfo *type_info,
++                                  const guchar     *end_pointer,
++                                  gsize             end_size,
++                                  gsize             total_size,
++                                  GArray           *unpacked_children)
++{
++  if (total_size)
++    {
++      GVariantUnpacked unpacked;
++
++      unpacked.type_info = g_variant_type_info_ref (g_variant_type_info_element (type_info));
++      unpacked.skip = 0;
++      unpacked.size = total_size;
++
++      g_array_append_val (unpacked_children, unpacked);
++    }
++
++  return TRUE;
++}
++
+ static gsize
+ gvs_fixed_sized_maybe_needed_size (GVariantTypeInfo         *type_info,
+                                    GVariantSerialisedFiller  gvs_filler,
+@@ -311,6 +332,19 @@ gvs_fixed_sized_maybe_serialise (GVariantSerialised        value,
+     }
+ }
++static gsize
++gvs_fixed_sized_maybe_write_to_vectors (GVariantVectors  *vectors,
++                                        GVariantTypeInfo *type_info,
++                                        gsize             size,
++                                        const gpointer   *children,
++                                        gsize             n_children)
++{
++  if (!n_children)
++    return 0;
++
++  return g_variant_callback_write_to_vectors (vectors, children[0], NULL);
++}
++
+ static gboolean
+ gvs_fixed_sized_maybe_is_normal (GVariantSerialised value)
+ {
+@@ -376,6 +410,27 @@ gvs_variable_sized_maybe_get_child (GVariantSerialised value,
+   return value;
+ }
++static gboolean
++gvs_variable_sized_maybe_unpack_all (GVariantTypeInfo *type_info,
++                                     const guchar     *end_pointer,
++                                     gsize             end_size,
++                                     gsize             total_size,
++                                     GArray           *unpacked_children)
++{
++  if (total_size)
++    {
++      GVariantUnpacked unpacked;
++
++      unpacked.type_info = g_variant_type_info_ref (g_variant_type_info_element (type_info));
++      unpacked.skip = 0;
++      unpacked.size = total_size - 1;
++
++      g_array_append_val (unpacked_children, unpacked);
++    }
++
++  return TRUE;
++}
++
+ static gsize
+ gvs_variable_sized_maybe_needed_size (GVariantTypeInfo         *type_info,
+                                       GVariantSerialisedFiller  gvs_filler,
+@@ -410,6 +465,20 @@ gvs_variable_sized_maybe_serialise (GVariantSerialised        value,
+     }
+ }
++static void
++gvs_variable_sized_maybe_write_to_vectors (GVariantVectors  *vectors,
++                                           GVariantTypeInfo *type_info,
++                                           gsize             size,
++                                           const gpointer   *children,
++                                           gsize             n_children)
++{
++  if (n_children)
++    {
++      g_variant_callback_write_to_vectors (vectors, children[0], NULL);
++      g_variant_vectors_append_copy (vectors, "", 1);
++    }
++}
++
+ static gboolean
+ gvs_variable_sized_maybe_is_normal (GVariantSerialised value)
+ {
+@@ -479,6 +548,39 @@ gvs_fixed_sized_array_get_child (GVariantSerialised value,
+   return child;
+ }
++static gboolean
++gvs_fixed_sized_array_unpack_all (GVariantTypeInfo *type_info,
++                                  const guchar     *end_pointer,
++                                  gsize             end_size,
++                                  gsize             total_size,
++                                  GArray           *unpacked_children)
++{
++  GVariantTypeInfo *element;
++  gsize element_fixed_size;
++  gsize i, n;
++
++  element = g_variant_type_info_element (type_info);
++  g_variant_type_info_query (element, NULL, &element_fixed_size);
++
++  if (total_size % element_fixed_size)
++    return FALSE;
++
++  n = total_size / element_fixed_size;
++
++  for (i = 0; i < n; i++)
++    {
++      GVariantUnpacked unpacked;
++
++      unpacked.type_info = g_variant_type_info_ref (element);
++      unpacked.skip = 0;
++      unpacked.size = element_fixed_size;
++
++      g_array_append_val (unpacked_children, unpacked);
++    }
++
++  return TRUE;
++}
++
+ static gsize
+ gvs_fixed_sized_array_needed_size (GVariantTypeInfo         *type_info,
+                                    GVariantSerialisedFiller  gvs_filler,
+@@ -513,6 +615,19 @@ gvs_fixed_sized_array_serialise (GVariantSerialised        value,
+     }
+ }
++static void
++gvs_fixed_sized_array_write_to_vectors (GVariantVectors  *vectors,
++                                        GVariantTypeInfo *type_info,
++                                        gsize             size,
++                                        const gpointer   *children,
++                                        gsize             n_children)
++{
++  gsize i;
++
++  for (i = 0; i < n_children; i++)
++    g_variant_callback_write_to_vectors (vectors, children[i], NULL);
++}
++
+ static gboolean
+ gvs_fixed_sized_array_is_normal (GVariantSerialised value)
+ {
+@@ -828,6 +943,80 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
+   return child;
+ }
++static gboolean
++gvs_variable_sized_array_unpack_all (GVariantTypeInfo *type_info,
++                                     const guchar     *end_pointer,
++                                     gsize             end_size,
++                                     gsize             total_size,
++                                     GArray           *unpacked_children)
++{
++  GVariantTypeInfo *element;
++  guint element_alignment;
++  const guchar *offsets;
++  gsize offset_size;
++  gsize offsets_array_size;
++  gsize prev_end;
++  gsize last_end;
++  gsize i, n;
++
++  if (total_size == 0)
++    return TRUE;
++
++  element = g_variant_type_info_element (type_info);
++  g_variant_type_info_query (element, &element_alignment, NULL);
++
++  offset_size = gvs_get_offset_size (total_size);
++
++  if (offset_size > end_size)
++    return FALSE;
++
++  last_end = gvs_read_unaligned_le (end_pointer - offset_size, offset_size);
++
++  if (last_end > total_size)
++    return 0;
++
++  offsets_array_size = total_size - last_end;
++
++  if (offsets_array_size > end_size)
++    return FALSE;
++
++  offsets = end_pointer - offsets_array_size;
++
++  if (offsets_array_size % offset_size)
++    return FALSE;
++
++  n = offsets_array_size / offset_size;
++
++  if (n == 0)
++    return FALSE;
++
++  prev_end = 0;
++
++  for (i = 0; i < n; i++)
++    {
++      GVariantUnpacked unpacked;
++      gsize start;
++      gsize end;
++
++      start = prev_end + ((-prev_end) & element_alignment);
++      end = gvs_read_unaligned_le (offsets, offset_size);
++      offsets += offset_size;
++
++      if (start < prev_end || end < start || end > last_end)
++       return FALSE;
++
++      unpacked.type_info = g_variant_type_info_ref (element);
++      unpacked.skip = start - prev_end;
++      unpacked.size = end - start;
++
++      g_array_append_val (unpacked_children, unpacked);
++
++      prev_end = end;
++    }
++
++  return TRUE;
++}
++
+ static gsize
+ gvs_variable_sized_array_needed_size (GVariantTypeInfo         *type_info,
+                                       GVariantSerialisedFiller  gvs_filler,
+@@ -887,6 +1076,38 @@ gvs_variable_sized_array_serialise (GVariantSerialised        value,
+     }
+ }
++static void
++gvs_variable_sized_array_write_to_vectors (GVariantVectors  *vectors,
++                                           GVariantTypeInfo *type_info,
++                                           gsize             size,
++                                           const gpointer   *children,
++                                           gsize             n_children)
++{
++  guint offset_key;
++  guint alignment;
++  gsize offset;
++  gsize i;
++
++  if (n_children == 0)
++    return;
++
++  offset_key = g_variant_vectors_reserve_offsets (vectors, n_children, gvs_get_offset_size (size));
++  g_variant_type_info_query (type_info, &alignment, NULL);
++  offset = 0;
++
++  for (i = 0; i < n_children; i++)
++    {
++      if ((-offset) & alignment)
++        offset += g_variant_vectors_append_pad (vectors, (-offset) & alignment);
++
++      offset += g_variant_callback_write_to_vectors (vectors, children[i], NULL);
++
++      g_variant_vectors_write_to_offsets (vectors, i, offset, offset_key);
++    }
++
++  g_variant_vectors_commit_offsets (vectors, offset_key);
++}
++
+ static gboolean
+ gvs_variable_sized_array_is_normal (GVariantSerialised value)
+ {
+@@ -1126,6 +1347,86 @@ gvs_tuple_get_child (GVariantSerialised value,
+   return child;
+ }
++static gboolean
++gvs_tuple_unpack_all (GVariantTypeInfo *type_info,
++                      const guchar     *end_pointer,
++                      gsize             end_size,
++                      gsize             total_size,
++                      GArray           *unpacked_children)
++{
++  gsize offset_size;
++  gsize prev_end;
++  gsize i, n;
++
++  n = g_variant_type_info_n_members (type_info);
++
++  /* An empty tuple (n = 0) is always encoded as a single byte, which
++   * means that we should not be attempting to unpack it from multiple
++   * vectors.
++   */
++  if (n == 0)
++    return FALSE;
++
++  offset_size = gvs_get_offset_size (total_size);
++
++  prev_end = 0;
++
++  for (i = 0; i < n; i++)
++    {
++      const GVariantMemberInfo *member_info;
++      GVariantUnpacked unpacked;
++      gsize fixed_size;
++      guint alignment;
++      gsize start;
++      gsize end;
++
++      member_info = g_variant_type_info_member_info (type_info, i);
++      g_variant_type_info_query (member_info->type_info, &alignment, &fixed_size);
++
++      start = prev_end + ((-prev_end) & alignment);
++
++      switch (member_info->ending_type)
++        {
++        case G_VARIANT_MEMBER_ENDING_FIXED:
++          end = start + fixed_size;
++          break;
++
++        case G_VARIANT_MEMBER_ENDING_LAST:
++          end = total_size;
++          break;
++
++        case G_VARIANT_MEMBER_ENDING_OFFSET:
++          if (end_size < offset_size)
++            return FALSE;
++
++          end_pointer -= offset_size;
++          total_size -= offset_size;
++          end_size -= offset_size;
++
++          end = gvs_read_unaligned_le (end_pointer, offset_size);
++          break;
++
++        default:
++          g_assert_not_reached ();
++        }
++
++      if (start < prev_end || end < start || end > total_size)
++        return FALSE;
++
++      unpacked.type_info = g_variant_type_info_ref (member_info->type_info);
++      unpacked.skip = start - prev_end;
++      unpacked.size = end - start;
++
++      g_array_append_val (unpacked_children, unpacked);
++
++      prev_end = end;
++    }
++
++  g_assert (prev_end == total_size);
++
++  return TRUE;
++}
++
+ static gsize
+ gvs_tuple_needed_size (GVariantTypeInfo         *type_info,
+                        GVariantSerialisedFiller  gvs_filler,
+@@ -1212,6 +1513,95 @@ gvs_tuple_serialise (GVariantSerialised        value,
+     value.data[offset++] = '\0';
+ }
++
++static void
++gvs_tuple_write_to_vectors (GVariantVectors  *vectors,
++                            GVariantTypeInfo *type_info,
++                            gsize             size,
++                            const gpointer   *children,
++                            gsize             n_children)
++{
++  const GVariantMemberInfo *member_info = NULL;
++  gsize fixed_size;
++  gsize offset;
++  gsize i;
++
++  if (n_children == 0)
++    {
++      g_variant_vectors_append_copy (vectors, "", 1);
++      return;
++    }
++
++  g_variant_type_info_query (type_info, NULL, &fixed_size);
++  offset = 0;
++
++  if (!fixed_size)
++    {
++      gsize n_offsets;
++
++      member_info = g_variant_type_info_member_info (type_info, n_children - 1);
++      n_offsets = member_info->i + 1;
++
++      if (n_offsets)
++        {
++          gsize offset_key = 0;
++
++          offset_key = g_variant_vectors_reserve_offsets (vectors, n_offsets, gvs_get_offset_size (size));
++
++          for (i = 0; i < n_children; i++)
++            {
++              guint alignment;
++
++              member_info = g_variant_type_info_member_info (type_info, i);
++              g_variant_type_info_query (member_info->type_info, &alignment, NULL);
++
++              if ((-offset) & alignment)
++                offset += g_variant_vectors_append_pad (vectors, (-offset) & alignment);
++
++              offset += g_variant_callback_write_to_vectors (vectors, children[i], NULL);
++
++              if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET)
++                g_variant_vectors_write_to_offsets (vectors, --n_offsets, offset, offset_key);
++            }
++
++          g_variant_vectors_commit_offsets (vectors, offset_key);
++        }
++      else
++        {
++          for (i = 0; i < n_children; i++)
++            {
++              guint alignment;
++
++              member_info = g_variant_type_info_member_info (type_info, i);
++              g_variant_type_info_query (member_info->type_info, &alignment, NULL);
++
++              if ((-offset) & alignment)
++                offset += g_variant_vectors_append_pad (vectors, (-offset) & alignment);
++
++              offset += g_variant_callback_write_to_vectors (vectors, children[i], NULL);
++            }
++        }
++    }
++  else
++    {
++      for (i = 0; i < n_children; i++)
++        {
++          guint alignment;
++
++          member_info = g_variant_type_info_member_info (type_info, i);
++          g_variant_type_info_query (member_info->type_info, &alignment, NULL);
++
++          if ((-offset) & alignment)
++            offset += g_variant_vectors_append_pad (vectors, (-offset) & alignment);
++
++          offset += g_variant_callback_write_to_vectors (vectors, children[i], NULL);
++        }
++
++      g_assert (fixed_size - offset < 8);
++      g_variant_vectors_append_pad (vectors, fixed_size - offset);
++    }
++}
++
+ static gboolean
+ gvs_tuple_is_normal (GVariantSerialised value)
+ {
+@@ -1358,66 +1748,100 @@ gvs_variant_n_children (GVariantSerialised value)
+   return 1;
+ }
+-static inline GVariantSerialised
+-gvs_variant_get_child (GVariantSerialised value,
+-                       gsize              index_)
++static GVariantTypeInfo *
++gvs_variant_find_type (const guchar *end_pointer,
++                       gsize         end_size,
++                       gsize         total_size,
++                       gsize        *child_size,
++                       gsize         depth)
+ {
+-  GVariantSerialised child = { 0, };
++  gsize i;
+-  /* NOTE: not O(1) and impossible for it to be... */
+-  if (value.size)
+-    {
+-      /* find '\0' character */
+-      for (child.size = value.size - 1; child.size; child.size--)
+-        if (value.data[child.size] == '\0')
+-          break;
++  for (i = 1; i <= end_size; i++)
++    if (end_pointer[-i] == '\0')
++      {
++        const gchar *type_string = (gchar *) end_pointer - i + 1;
++        const gchar *limit = (gchar *) end_pointer;
++        const gchar *end;
+-      /* ensure we didn't just hit the start of the string */
+-      if (value.data[child.size] == '\0')
+-        {
+-          const gchar *type_string = (gchar *) &value.data[child.size + 1];
+-          const gchar *limit = (gchar *) &value.data[value.size];
+-          const gchar *end;
++        /* We may have a type string of length 'i'.  Check for validity. */
++        if (g_variant_type_string_scan (type_string, limit, &end) && end == limit)
++          {
++            const GVariantType *type = (GVariantType *) type_string;
+-          if (g_variant_type_string_scan (type_string, limit, &end) &&
+-              end == limit)
+-            {
+-              const GVariantType *type = (GVariantType *) type_string;
++            if (g_variant_type_is_definite (type))
++              {
++                GVariantTypeInfo *type_info;
++                gsize fixed_size;
++                gsize child_type_depth;
+-              if (g_variant_type_is_definite (type))
+-                {
+-                  gsize fixed_size;
+-                  gsize child_type_depth;
++                type_info = g_variant_type_info_get (type);
+-                  child.type_info = g_variant_type_info_get (type);
+-                  child.depth = value.depth + 1;
++                g_variant_type_info_query (type_info, NULL, &fixed_size);
++                child_type_depth = g_variant_type_info_query_depth (type_info);
+-                  if (child.size != 0)
+-                    /* only set to non-%NULL if size > 0 */
+-                    child.data = value.data;
++                if ((!fixed_size || fixed_size == total_size - i) &&
++                      depth < G_VARIANT_MAX_RECURSION_DEPTH - child_type_depth)
++                  {
++                    *child_size = total_size - i;
+-                  g_variant_type_info_query (child.type_info,
+-                                             NULL, &fixed_size);
+-                  child_type_depth = g_variant_type_info_query_depth (child.type_info);
++                    return type_info;
++                  }
+-                  if ((!fixed_size || fixed_size == child.size) &&
+-                      value.depth < G_VARIANT_MAX_RECURSION_DEPTH - child_type_depth)
+-                    return child;
++                g_variant_type_info_unref (type_info);
++              }
++          }
+-                  g_variant_type_info_unref (child.type_info);
+-                }
+-            }
+-        }
++        /* No sense in trying other lengths if we already failed */
++        break;
++      }
++
++  return NULL;
++}
++
++static inline GVariantSerialised
++gvs_variant_get_child (GVariantSerialised value,
++                       gsize              index_)
++{
++  GVariantSerialised child = { 0, };
++
++  if ((child.type_info = gvs_variant_find_type (value.data + value.size, value.size, value.size, &child.size, value.depth)))
++    {
++      if (child.size != 0)
++        child.data = value.data;
++    }
++  else
++    {
++      child.type_info = g_variant_type_info_get (G_VARIANT_TYPE_UNIT);
++      child.size = 1;
+     }
+-  child.type_info = g_variant_type_info_get (G_VARIANT_TYPE_UNIT);
+-  child.data = NULL;
+-  child.size = 1;
+   child.depth = value.depth + 1;
+   return child;
+ }
++static gboolean
++gvs_variant_unpack_all (GVariantTypeInfo *type_info,
++                        const guchar     *end_pointer,
++                        gsize             end_size,
++                        gsize             total_size,
++                        GArray           *unpacked_children)
++{
++  GVariantUnpacked unpacked;
++
++  if ((unpacked.type_info = gvs_variant_find_type (end_pointer, end_size, total_size, &unpacked.size, 0)))
++    {
++      unpacked.skip = 0;
++
++      g_array_append_val (unpacked_children, unpacked);
++
++      return TRUE;
++    }
++
++  return FALSE;
++}
++
+ static inline gsize
+ gvs_variant_needed_size (GVariantTypeInfo         *type_info,
+                          GVariantSerialisedFiller  gvs_filler,
+@@ -1450,6 +1874,23 @@ gvs_variant_serialise (GVariantSerialised        value,
+   memcpy (value.data + child.size + 1, type_string, strlen (type_string));
+ }
++static void
++gvs_variant_write_to_vectors (GVariantVectors  *vectors,
++                              GVariantTypeInfo *type_info,
++                              gsize             size,
++                              const gpointer   *children,
++                              gsize             n_children)
++{
++  GVariantTypeInfo *child_type_info;
++  const gchar *type_string;
++
++  g_variant_callback_write_to_vectors (vectors, children[0], &child_type_info);
++  type_string = g_variant_type_info_get_type_string (child_type_info);
++
++  g_variant_vectors_append_copy (vectors, "", 1);
++  g_variant_vectors_append_copy (vectors, type_string, strlen (type_string));
++}
++
+ static inline gboolean
+ gvs_variant_is_normal (GVariantSerialised value)
+ {
+@@ -1662,7 +2103,35 @@ g_variant_serialiser_needed_size (GVariantTypeInfo         *type_info,
+                   return gvs_/**/,/**/_needed_size (type_info, gvs_filler,
+                                                     children, n_children);
++                 )
++  g_assert_not_reached ();
++}
++
++gboolean
++g_variant_serialiser_unpack_all (GVariantTypeInfo *type_info,
++                                 const guchar     *end_pointer,
++                                 gsize             end_size,
++                                 gsize             total_size,
++                                 GArray           *unpacked_children)
++{
++  DISPATCH_CASES (type_info,
++                  return gvs_/**/,/**/_unpack_all (type_info, end_pointer, end_size, total_size, unpacked_children);
++                 )
++
++  /* We are here because type_info is not a container type */
++  return FALSE;
++}
++void
++g_variant_serialiser_write_to_vectors (GVariantVectors  *vectors,
++                                       GVariantTypeInfo *type_info,
++                                       gsize             size,
++                                       const gpointer   *children,
++                                       gsize             n_children)
++{
++  DISPATCH_CASES (type_info,
++                  gvs_/**/,/**/_write_to_vectors (vectors, type_info, size, children, n_children);
++                  return;
+                  )
+   g_assert_not_reached ();
+ }
+diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h
+index eb74fe7..c8b59d9 100644
+--- a/glib/gvariant-serialiser.h
++++ b/glib/gvariant-serialiser.h
+@@ -24,6 +24,7 @@
+ #define __G_VARIANT_SERIALISER_H__
+ #include "gvarianttypeinfo.h"
++#include "gvariant-vectors.h"
+ typedef struct
+ {
+@@ -54,14 +55,27 @@ typedef struct
+   gsize             checked_offsets_up_to;
+ } GVariantSerialised;
+-/* deserialization */
++typedef struct
++{
++  GVariantTypeInfo *type_info;
++  gsize             skip;
++  gsize             size;
++} GVariantUnpacked;
++
++/* deserialisation */
+ GLIB_AVAILABLE_IN_ALL
+ gsize                           g_variant_serialised_n_children         (GVariantSerialised        container);
+ GLIB_AVAILABLE_IN_ALL
+ GVariantSerialised              g_variant_serialised_get_child          (GVariantSerialised        container,
+                                                                          gsize                     index);
+-/* serialization */
++gboolean                        g_variant_serialiser_unpack_all         (GVariantTypeInfo         *type_info,
++                                                                         const guchar             *end_pointer,
++                                                                         gsize                     end_size,
++                                                                         gsize                     total_size,
++                                                                         GArray                   *unpacked_children);
++
++/* serialisation */
+ typedef void                  (*GVariantSerialisedFiller)               (GVariantSerialised       *serialised,
+                                                                          gpointer                  data);
+@@ -96,4 +110,14 @@ GLIB_AVAILABLE_IN_ALL
+ gboolean                        g_variant_serialiser_is_signature       (gconstpointer             data,
+                                                                          gsize                     size);
++
++gsize                           g_variant_callback_write_to_vectors     (GVariantVectors          *vectors,
++                                                                         gpointer                  data,
++                                                                         GVariantTypeInfo        **type_info);
++void                            g_variant_serialiser_write_to_vectors   (GVariantVectors          *items,
++                                                                         GVariantTypeInfo         *type_info,
++                                                                         gsize                     size,
++                                                                         const gpointer           *children,
++                                                                         gsize                     n_children);
++
+ #endif /* __G_VARIANT_SERIALISER_H__ */
+diff --git a/glib/gvariant-vectors.c b/glib/gvariant-vectors.c
+new file mode 100644
+index 0000000..5e07786
+--- /dev/null
++++ b/glib/gvariant-vectors.c
+@@ -0,0 +1,248 @@
++#include "config.h"
++
++#include "gvariant-vectors.h"
++#include "gtestutils.h"
++
++static void
++append_zeros (GByteArray *array,
++              guint       n)
++{
++  guchar zeros[8] = "";
++
++  g_byte_array_append (array, zeros, n);
++}
++
++void
++g_variant_vectors_init (GVariantVectors *vectors)
++{
++
++  /* The first 8 bytes of 'extra_bytes' is always 0.  We use this for
++   * inserting padding in between two GBytes records.
++   */
++  vectors->extra_bytes = g_byte_array_new ();
++  append_zeros (vectors->extra_bytes, 8);
++
++  vectors->vectors = g_array_new (FALSE, FALSE, sizeof (GVariantVector));
++
++  vectors->offsets = g_byte_array_new ();
++}
++
++void
++g_variant_vectors_deinit (GVariantVectors *vectors)
++{
++  int i;
++  for (i = 0; i < vectors->vectors->len; i++)
++    {
++      GVariantVector *v = &g_array_index (vectors->vectors, GVariantVector, i);
++      g_bytes_unref (v->gbytes);
++    }
++  g_byte_array_unref (vectors->extra_bytes);
++  g_array_unref (vectors->vectors);
++  g_byte_array_unref (vectors->offsets);
++}
++
++gsize
++g_variant_vectors_append_pad (GVariantVectors *vectors,
++                              gsize            padding)
++{
++  /* If the last vector that we stored was 'pad' or 'copy' then we will
++   * expand it instead of adding a new one.
++   */
++  if (vectors->vectors->len)
++    {
++      GVariantVector *expand_vector = &g_array_index (vectors->vectors, GVariantVector, vectors->vectors->len - 1);
++
++      if (expand_vector->gbytes == NULL)
++        {
++          expand_vector->size += padding;
++
++          /* If the vector points to data, we need to add the padding to
++           * the end of that data.  If it points to the zero bytes at
++           * the start then we can just grow it (but we must ensure that
++           * it doesn't get too large).
++           */
++          if (expand_vector->data.offset)
++            append_zeros (vectors->extra_bytes, padding);
++          else
++            g_assert (expand_vector->size < 8);
++
++          return padding;
++        }
++
++      /* If the last vector was a GBytes then fall through */
++    }
++
++  /* Otherwise, record a new vector pointing to the padding bytes at the
++   * start.
++   */
++  {
++    GVariantVector v;
++
++    v.gbytes = NULL;
++    v.data.offset = 0;
++    v.size = padding;
++
++    g_array_append_val (vectors->vectors, v);
++  }
++
++  return padding;
++}
++
++void
++g_variant_vectors_append_copy (GVariantVectors *vectors,
++                               gconstpointer    data,
++                               gsize            size)
++{
++  /* If the last vector that we stored was 'pad' or 'copy' then we will
++   * expand it instead of adding a new one.
++   */
++  if (vectors->vectors->len)
++    {
++      GVariantVector *expand_vector = &g_array_index (vectors->vectors, GVariantVector, vectors->vectors->len - 1);
++
++      if (expand_vector->gbytes == NULL)
++        {
++          /* If this was a padding vector then we must convert it to
++           * data first.
++           */
++          if (expand_vector->data.offset == 0)
++            {
++              expand_vector->data.offset = vectors->extra_bytes->len;
++              append_zeros (vectors->extra_bytes, expand_vector->size);
++            }
++
++          /* We have a vector pointing to data at the end of the
++           * extra_bytes array, so just append there and grow the
++           * vector.
++           */
++          g_byte_array_append (vectors->extra_bytes, data, size);
++          expand_vector->size += size;
++          return;
++        }
++
++      /* If the last vector was a GBytes then fall through */
++    }
++
++  /* Otherwise, copy the data and record a new vector. */
++  {
++    GVariantVector v;
++
++    v.gbytes = NULL;
++    v.data.offset = vectors->extra_bytes->len;
++    v.size = size;
++
++    g_byte_array_append (vectors->extra_bytes, data, size);
++    g_array_append_val (vectors->vectors, v);
++  }
++}
++
++void
++g_variant_vectors_append_gbytes (GVariantVectors *vectors,
++                                 GBytes          *gbytes,
++                                 gconstpointer    data,
++                                 gsize            size)
++{
++  GVariantVector v;
++
++  /* Some very rough profiling has indicated that the trade-off for
++   * overhead on the atomic operations involved in the ref/unref on the
++   * GBytes is larger than the cost of the copy at ~128 bytes.
++   */
++  if (size < 128)
++    {
++      g_variant_vectors_append_copy (vectors, data, size);
++      return;
++    }
++
++  v.gbytes = g_bytes_ref (gbytes);
++  v.data.pointer = data;
++  v.size = size;
++
++  g_array_append_val (vectors->vectors, v);
++}
++
++typedef void (* WriteFunction) (gpointer base, gsize offset, gsize value);
++static void write_1 (gpointer base, gsize offset, gsize value) { ((guint8 *) base)[offset] = value; }
++static void write_2 (gpointer base, gsize offset, gsize value) { ((guint16 *) base)[offset] = GUINT16_TO_LE (value); }
++static void write_4 (gpointer base, gsize offset, gsize value) { ((guint32 *) base)[offset] = GUINT32_TO_LE (value); }
++static void write_8 (gpointer base, gsize offset, gsize value) { ((guint64 *) base)[offset] = GUINT64_TO_LE (value); }
++
++typedef struct
++{
++  gsize         size;
++  WriteFunction func;
++} OffsetsHeader;
++
++gsize
++g_variant_vectors_reserve_offsets (GVariantVectors *vectors,
++                                   guint            n_offsets,
++                                   guint            offset_size)
++{
++  OffsetsHeader *header;
++  gsize total_size;
++  gsize add_size;
++  guint key;
++
++  total_size = n_offsets * offset_size;
++
++  /* Add room for the metadata and round up to multiple of 8 */
++  add_size = (sizeof (OffsetsHeader) + total_size + 7) & ~7ull;
++  key = vectors->offsets->len;
++  g_byte_array_set_size (vectors->offsets, key + add_size);
++  header = (OffsetsHeader *) (vectors->offsets->data + key);
++  key += sizeof (OffsetsHeader);
++  header->size = total_size;
++
++  switch (offset_size)
++    {
++    case 1:
++      header->func = write_1;
++      break;
++
++    case 2:
++      header->func = write_2;
++      break;
++
++    case 4:
++      header->func = write_4;
++      break;
++
++    case 8:
++      header->func = write_8;
++      break;
++
++    default:
++      g_assert_not_reached ();
++    }
++
++  return key;
++}
++
++void
++g_variant_vectors_write_to_offsets (GVariantVectors *vectors,
++                                    gsize            offset,
++                                    gsize            value,
++                                    gsize            key)
++{
++  OffsetsHeader *header;
++  guchar *offsets;
++
++  offsets = vectors->offsets->data + key;
++  header = (OffsetsHeader *) (offsets - sizeof (OffsetsHeader));
++
++  header->func (offsets, offset, value);
++}
++
++void
++g_variant_vectors_commit_offsets (GVariantVectors *vectors,
++                                  gsize            key)
++{
++  OffsetsHeader *header;
++  guchar *offsets;
++
++  offsets = vectors->offsets->data + key;
++  header = (OffsetsHeader *) (offsets - sizeof (OffsetsHeader));
++
++  g_variant_vectors_append_copy (vectors, offsets, header->size);
++  g_byte_array_set_size (vectors->offsets, key - sizeof (OffsetsHeader));
++}
+diff --git a/glib/gvariant-vectors.h b/glib/gvariant-vectors.h
+new file mode 100644
+index 0000000..05e2258
+--- /dev/null
++++ b/glib/gvariant-vectors.h
+@@ -0,0 +1,63 @@
++
++#ifndef __G_VARIANT_VECTORS_H__
++#define __G_VARIANT_VECTORS_H__
++
++#include <glib/garray.h>
++
++typedef struct
++{
++  GByteArray *extra_bytes;
++  GArray     *vectors;
++  GByteArray *offsets;
++} GVariantVectors;
++
++
++/* If ->bytes is NULL then offset/size point inside of extra_bytes,
++ * otherwise pointer/size point to memory owned by the GBytes.
++ */
++typedef struct
++{
++  GBytes *gbytes;
++  union {
++    const guchar *pointer;
++    gsize         offset;
++  } data;
++  gsize    size;
++} GVariantVector;
++
++void                    g_variant_vectors_init                          (GVariantVectors *vectors);
++
++
++void                    g_variant_vectors_deinit                        (GVariantVectors *vectors);
++
++
++gsize                   g_variant_vectors_append_pad                    (GVariantVectors *vectors,
++                                                                         gsize            padding);
++
++
++void                    g_variant_vectors_append_copy                   (GVariantVectors *vectors,
++                                                                         gconstpointer    data,
++                                                                         gsize            size);
++
++
++void                    g_variant_vectors_append_gbytes                 (GVariantVectors *vectors,
++                                                                         GBytes          *gbytes,
++                                                                         gconstpointer    data,
++                                                                         gsize            size);
++
++
++gsize                   g_variant_vectors_reserve_offsets               (GVariantVectors *vectors,
++                                                                         guint            n_offsets,
++                                                                         guint            offset_size);
++
++
++void                    g_variant_vectors_write_to_offsets              (GVariantVectors *vectors,
++                                                                         gsize            offset,
++                                                                         gsize            value,
++                                                                         gsize            offset_key);
++
++
++void                    g_variant_vectors_commit_offsets                (GVariantVectors *vectors,
++                                                                         gsize            offset_key);
++
++#endif /* __G_GVARIANT_VECTORS_H__ */
+diff --git a/glib/gvariant.c b/glib/gvariant.c
+index b7e4241..b1838e2 100644
+--- a/glib/gvariant.c
++++ b/glib/gvariant.c
+@@ -139,7 +139,7 @@
+  * endianness, or of the length or type of the top-level variant.
+  *
+  * The amount of memory required to store a boolean is 1 byte. 16,
+- * 32 and 64 bit integers and double precision floating point numbers
++ * 32 and 64 bit integers and floating point numbers
+  * use their "natural" size.  Strings (including object path and
+  * signature strings) are stored with a nul terminator, and as such
+  * use the length of the string plus 1 byte.
+@@ -225,7 +225,7 @@
+  *
+  * This means that in total, for our "a{sv}" example, 91 bytes of
+  * type information would be allocated.
+- * 
++ *
+  * The type information cache, additionally, uses a #GHashTable to
+  * store and look up the cached items and stores a pointer to this
+  * hash table in static storage.  The hash table is freed when there
+@@ -320,14 +320,10 @@ g_variant_new_from_trusted (const GVariantType *type,
+                             gconstpointer       data,
+                             gsize               size)
+ {
+-  GVariant *value;
+-  GBytes *bytes;
+-
+-  bytes = g_bytes_new (data, size);
+-  value = g_variant_new_from_bytes (type, bytes, TRUE);
+-  g_bytes_unref (bytes);
++  gpointer mydata = g_memdup (data, size);
+-  return value;
++  return g_variant_new_serialised (g_variant_type_info_get (type),
++                                   g_bytes_new_take (mydata, size), mydata, size, TRUE);
+ }
+ /**
+@@ -374,7 +370,7 @@ g_variant_get_boolean (GVariant *value)
+ }
+ /* the constructors and accessors for byte, int{16,32,64}, handles and
+- * doubles all look pretty much exactly the same, so we reduce
++ * floats all look pretty much exactly the same, so we reduce
+  * copy/pasting here.
+  */
+ #define NUMERIC_TYPE(TYPE, type, ctype) \
+@@ -598,6 +594,31 @@ NUMERIC_TYPE (UINT64, uint64, guint64)
+  **/
+ NUMERIC_TYPE (HANDLE, handle, gint32)
++/**
++ * g_variant_new_float:
++ * @value: a #gfloat floating point value
++ *
++ * Creates a new float #GVariant instance.
++ *
++ * Returns: (transfer none): a floating reference to a new float #GVariant instance
++ *
++ * Since: 2.44
++ **/
++/**
++ * g_variant_get_float:
++ * @value: a float #GVariant instance
++ *
++ * Returns the single precision floating point value of @value.
++ *
++ * It is an error to call this function with a @value of any type
++ * other than %G_VARIANT_TYPE_FLOAT.
++ *
++ * Returns: a #gfloat
++ *
++ * Since: 2.44
++ **/
++NUMERIC_TYPE (FLOAT, float, gfloat)
++
+ /**
+  * g_variant_new_double:
+  * @value: a #gdouble floating point value
+@@ -648,8 +669,8 @@ GVariant *
+ g_variant_new_maybe (const GVariantType *child_type,
+                      GVariant           *child)
+ {
++  GVariantTypeInfo *type_info;
+   GVariantType *maybe_type;
+-  GVariant *value;
+   g_return_val_if_fail (child_type == NULL || g_variant_type_is_definite
+                         (child_type), 0);
+@@ -662,6 +683,8 @@ g_variant_new_maybe (const GVariantType *child_type,
+     child_type = g_variant_get_type (child);
+   maybe_type = g_variant_type_new_maybe (child_type);
++  type_info = g_variant_type_info_get (maybe_type);
++  g_variant_type_free (maybe_type);
+   if (child != NULL)
+     {
+@@ -672,14 +695,10 @@ g_variant_new_maybe (const GVariantType *child_type,
+       children[0] = g_variant_ref_sink (child);
+       trusted = g_variant_is_trusted (children[0]);
+-      value = g_variant_new_from_children (maybe_type, children, 1, trusted);
++      return g_variant_new_from_children (type_info, children, 1, trusted);
+     }
+   else
+-    value = g_variant_new_from_children (maybe_type, NULL, 0, TRUE);
+-
+-  g_variant_type_free (maybe_type);
+-
+-  return value;
++    return g_variant_new_from_children (type_info, NULL, 0, TRUE);
+ }
+ /**
+@@ -725,7 +744,7 @@ g_variant_new_variant (GVariant *value)
+   g_variant_ref_sink (value);
+-  return g_variant_new_from_children (G_VARIANT_TYPE_VARIANT,
++  return g_variant_new_from_children (g_variant_type_info_get (G_VARIANT_TYPE_VARIANT),
+                                       g_memdup2 (&value, sizeof value),
+                                       1, g_variant_is_trusted (value));
+ }
+@@ -781,10 +800,10 @@ g_variant_new_array (const GVariantType *child_type,
+                      GVariant * const   *children,
+                      gsize               n_children)
+ {
++  GVariantTypeInfo *type_info;
+   GVariantType *array_type;
+   GVariant **my_children;
+   gboolean trusted;
+-  GVariant *value;
+   gsize i;
+   g_return_val_if_fail (n_children > 0 || child_type != NULL, NULL);
+@@ -798,6 +817,8 @@ g_variant_new_array (const GVariantType *child_type,
+   if (child_type == NULL)
+     child_type = g_variant_get_type (children[0]);
+   array_type = g_variant_type_new_array (child_type);
++  type_info = g_variant_type_info_get (array_type);
++  g_variant_type_free (array_type);
+   for (i = 0; i < n_children; i++)
+     {
+@@ -813,11 +834,7 @@ g_variant_new_array (const GVariantType *child_type,
+       trusted &= g_variant_is_trusted (children[i]);
+     }
+-  value = g_variant_new_from_children (array_type, my_children,
+-                                       n_children, trusted);
+-  g_variant_type_free (array_type);
+-
+-  return value;
++  return g_variant_new_from_children (type_info, my_children, n_children, trusted);
+ }
+ /*< private >
+@@ -868,10 +885,10 @@ GVariant *
+ g_variant_new_tuple (GVariant * const *children,
+                      gsize             n_children)
+ {
++  GVariantTypeInfo *type_info;
+   GVariantType *tuple_type;
+   GVariant **my_children;
+   gboolean trusted;
+-  GVariant *value;
+   gsize i;
+   g_return_val_if_fail (n_children == 0 || children != NULL, NULL);
+@@ -886,11 +903,10 @@ g_variant_new_tuple (GVariant * const *children,
+     }
+   tuple_type = g_variant_make_tuple_type (children, n_children);
+-  value = g_variant_new_from_children (tuple_type, my_children,
+-                                       n_children, trusted);
++  type_info = g_variant_type_info_get (tuple_type);
+   g_variant_type_free (tuple_type);
+-  return value;
++  return g_variant_new_from_children (type_info, my_children, n_children, trusted);
+ }
+ /*< private >
+@@ -928,6 +944,7 @@ GVariant *
+ g_variant_new_dict_entry (GVariant *key,
+                           GVariant *value)
+ {
++  GVariantTypeInfo *type_info;
+   GVariantType *dict_type;
+   GVariant **children;
+   gboolean trusted;
+@@ -941,10 +958,10 @@ g_variant_new_dict_entry (GVariant *key,
+   trusted = g_variant_is_trusted (key) && g_variant_is_trusted (value);
+   dict_type = g_variant_make_dict_entry_type (key, value);
+-  value = g_variant_new_from_children (dict_type, children, 2, trusted);
++  type_info = g_variant_type_info_get (dict_type);
+   g_variant_type_free (dict_type);
+-  return value;
++  return g_variant_new_from_children (type_info, children, 2, trusted);
+ }
+ /**
+@@ -1120,6 +1137,7 @@ g_variant_lookup_value (GVariant           *dictionary,
+  * - %G_VARIANT_TYPE_BOOLEAN: #guchar (not #gboolean!)
+  * - %G_VARIANT_TYPE_BYTE: #guint8
+  * - %G_VARIANT_TYPE_HANDLE: #guint32
++ * - %G_VARIANT_TYPE_FLOAT: #gfloat
+  * - %G_VARIANT_TYPE_DOUBLE: #gdouble
+  *
+  * For example, if calling this function for an array of 32-bit integers,
+@@ -1577,7 +1595,7 @@ g_variant_new_strv (const gchar * const *strv,
+   for (i = 0; i < length_unsigned; i++)
+     strings[i] = g_variant_ref_sink (g_variant_new_string (strv[i]));
+-  return g_variant_new_from_children (G_VARIANT_TYPE_STRING_ARRAY,
++  return g_variant_new_from_children (g_variant_type_info_get (G_VARIANT_TYPE_STRING_ARRAY),
+                                       strings, length_unsigned, TRUE);
+ }
+@@ -1714,7 +1732,7 @@ g_variant_new_objv (const gchar * const *strv,
+   for (i = 0; i < length_unsigned; i++)
+     strings[i] = g_variant_ref_sink (g_variant_new_object_path (strv[i]));
+-  return g_variant_new_from_children (G_VARIANT_TYPE_OBJECT_PATH_ARRAY,
++  return g_variant_new_from_children (g_variant_type_info_get (G_VARIANT_TYPE_OBJECT_PATH_ARRAY),
+                                       strings, length_unsigned, TRUE);
+ }
+@@ -1955,7 +1973,7 @@ g_variant_new_bytestring_array (const gchar * const *strv,
+   for (i = 0; i < length_unsigned; i++)
+     strings[i] = g_variant_ref_sink (g_variant_new_bytestring (strv[i]));
+-  return g_variant_new_from_children (G_VARIANT_TYPE_BYTESTRING_ARRAY,
++  return g_variant_new_from_children (g_variant_type_info_get (G_VARIANT_TYPE_BYTESTRING_ARRAY),
+                                       strings, length_unsigned, TRUE);
+ }
+@@ -2166,6 +2184,8 @@ g_variant_is_container (GVariant *value)
+  * @G_VARIANT_CLASS_UINT64: The #GVariant is an unsigned 64 bit integer.
+  * @G_VARIANT_CLASS_HANDLE: The #GVariant is a file handle index.
+  * @G_VARIANT_CLASS_DOUBLE: The #GVariant is a double precision floating 
++ * @G_VARIANT_CLASS_FLOAT: The #GVariant is a single precision floating
++ *                         point value.
+  *                          point value.
+  * @G_VARIANT_CLASS_STRING: The #GVariant is a normal string.
+  * @G_VARIANT_CLASS_OBJECT_PATH: The #GVariant is a D-Bus object path 
+@@ -2587,6 +2607,32 @@ g_variant_print_string (GVariant *value,
+                               g_variant_get_uint64 (value));
+       break;
++    case G_VARIANT_CLASS_FLOAT:
++      {
++        gchar buffer[100];
++        gint i;
++
++        g_ascii_dtostr (buffer, sizeof buffer, g_variant_get_float (value));
++
++        for (i = 0; buffer[i]; i++)
++          if (buffer[i] == '.' || buffer[i] == 'e' ||
++              buffer[i] == 'n' || buffer[i] == 'N')
++            break;
++
++        /* if there is no '.' or 'e' in the float then add one */
++        if (buffer[i] == '\0')
++          {
++            buffer[i++] = '.';
++            buffer[i++] = '0';
++            buffer[i++] = '\0';
++          }
++
++        if (type_annotate)
++          g_string_append (string, "float ");
++        g_string_append (string, buffer);
++      }
++      break;
++
+     case G_VARIANT_CLASS_DOUBLE:
+       {
+         gchar buffer[100];
+@@ -2711,6 +2757,7 @@ g_variant_hash (gconstpointer value_)
+     case G_VARIANT_CLASS_INT32:
+     case G_VARIANT_CLASS_UINT32:
+     case G_VARIANT_CLASS_HANDLE:
++    case G_VARIANT_CLASS_FLOAT:
+       {
+         const guint *ptr;
+@@ -2833,7 +2880,7 @@ g_variant_equal (gconstpointer one,
+  * two values that have types that are not exactly equal.  For example,
+  * you cannot compare a 32-bit signed integer with a 32-bit unsigned
+  * integer.  Also note that this function is not particularly
+- * well-behaved when it comes to comparison of doubles; in particular,
++ * well-behaved when it comes to comparison of floats; in particular,
+  * the handling of incomparable values (ie: NaN) is undefined.
+  *
+  * If you only require an equality comparison, g_variant_equal() is more
+@@ -2904,6 +2951,14 @@ g_variant_compare (gconstpointer one,
+         return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1;
+       }
++    case G_VARIANT_CLASS_FLOAT:
++      {
++        gfloat a_val = g_variant_get_float (a);
++        gfloat b_val = g_variant_get_float (b);
++
++        return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1;
++      }
++
+     case G_VARIANT_CLASS_DOUBLE:
+       {
+         gdouble a_val = g_variant_get_double (a);
+@@ -3109,7 +3164,7 @@ g_variant_iter_free (GVariantIter *iter)
+  * you no longer need it.
+  *
+  * Here is an example for iterating with g_variant_iter_next_value():
+- * |[<!-- language="C" --> 
++ * |[<!-- language="C" -->
+  *   // recursively iterate a container
+  *   void
+  *   iterate_container_recursive (GVariant *container)
+@@ -3773,7 +3828,7 @@ g_variant_builder_end (GVariantBuilder *builder)
+   else
+     g_assert_not_reached ();
+-  value = g_variant_new_from_children (my_type,
++  value = g_variant_new_from_children (g_variant_type_info_get (my_type),
+                                        g_renew (GVariant *,
+                                                 GVSB(builder)->children,
+                                                 GVSB(builder)->offset),
+@@ -4374,8 +4429,8 @@ g_variant_format_string_scan (const gchar  *string,
+   switch (next_char())
+     {
+     case 'b': case 'y': case 'n': case 'q': case 'i': case 'u':
+-    case 'x': case 't': case 'h': case 'd': case 's': case 'o':
+-    case 'g': case 'v': case '*': case '?': case 'r':
++    case 'x': case 't': case 'h': case 'f': case 'd': case 's':
++    case 'o': case 'g': case 'v': case '*': case '?': case 'r':
+       break;
+     case 'm':
+@@ -5052,6 +5107,7 @@ g_variant_valist_skip_leaf (const gchar **str,
+       va_arg (*app, guint64);
+       return;
++    case 'f':
+     case 'd':
+       va_arg (*app, gdouble);
+       return;
+@@ -5097,6 +5153,9 @@ g_variant_valist_new_leaf (const gchar **str,
+     case 'h':
+       return g_variant_new_handle (va_arg (*app, gint));
++    case 'f':
++      return g_variant_new_float (va_arg (*app, gdouble));
++
+     case 'd':
+       return g_variant_new_double (va_arg (*app, gdouble));
+@@ -5107,6 +5166,7 @@ g_variant_valist_new_leaf (const gchar **str,
+ /* The code below assumes this */
+ G_STATIC_ASSERT (sizeof (gboolean) == sizeof (guint32));
++G_STATIC_ASSERT (sizeof (gfloat) == sizeof (guint32));
+ G_STATIC_ASSERT (sizeof (gdouble) == sizeof (guint64));
+ static void
+@@ -5180,6 +5240,10 @@ g_variant_valist_get_leaf (const gchar **str,
+           *(gint32 *) ptr = g_variant_get_handle (value);
+           return;
++        case 'f':
++          *(gfloat *) ptr = g_variant_get_float (value);
++          return;
++
+         case 'd':
+           *(gdouble *) ptr = g_variant_get_double (value);
+           return;
+@@ -5202,6 +5266,7 @@ g_variant_valist_get_leaf (const gchar **str,
+         case 'u':
+         case 'h':
+         case 'b':
++        case 'f':
+           *(guint32 *) ptr = 0;
+           return;
+@@ -6011,6 +6076,9 @@ g_variant_deep_copy (GVariant *value,
+       else
+         return g_variant_new_handle (g_variant_get_handle (value));
++    case G_VARIANT_CLASS_FLOAT:
++      return g_variant_new_float (g_variant_get_float (value));
++
+     case G_VARIANT_CLASS_DOUBLE:
+       if (byteswap)
+         {
+@@ -6096,8 +6164,7 @@ g_variant_get_normal_form (GVariant *value)
+  * Performs a byteswapping operation on the contents of @value.  The
+  * result is that all multi-byte numeric data contained in @value is
+  * byteswapped.  That includes 16, 32, and 64bit signed and unsigned
+- * integers as well as file handles and double precision floating point
+- * values.
++ * integers as well as file handles and floating point values.
+  *
+  * This function is an identity mapping on any value that does not
+  * contain multi-byte numeric data.  That include strings, booleans,
+@@ -6211,21 +6278,99 @@ g_variant_new_from_data (const GVariantType *type,
+                          GDestroyNotify      notify,
+                          gpointer            user_data)
+ {
+-  GVariant *value;
+   GBytes *bytes;
+   g_return_val_if_fail (g_variant_type_is_definite (type), NULL);
+   g_return_val_if_fail (data != NULL || size == 0, NULL);
++  if (size == 0)
++    {
++      if (notify)
++        {
++          (* notify) (user_data);
++          notify = NULL;
++        }
++
++      data = NULL;
++    }
++
+   if (notify)
+     bytes = g_bytes_new_with_free_func (data, size, notify, user_data);
+   else
+     bytes = g_bytes_new_static (data, size);
+-  value = g_variant_new_from_bytes (type, bytes, trusted);
+-  g_bytes_unref (bytes);
++  return g_variant_new_serialised (g_variant_type_info_get (type), bytes, data, size, trusted);
++}
+-  return value;
++/**
++ * g_variant_new_from_bytes:
++ * @type: a #GVariantType
++ * @bytes: a #GBytes
++ * @trusted: if the contents of @bytes are trusted
++ *
++ * Constructs a new serialised-mode #GVariant instance.  This is the
++ * inner interface for creation of new serialised values that gets
++ * called from various functions in gvariant.c.
++ *
++ * A reference is taken on @bytes.
++ *
++ * Returns: (transfer none): a new #GVariant with a floating reference
++ *
++ * Since: 2.36
++ */
++GVariant *
++g_variant_new_from_bytes (const GVariantType *type,
++                          GBytes             *bytes,
++                          gboolean            trusted)
++{
++  gconstpointer data;
++  gsize size;
++
++  g_return_val_if_fail (g_variant_type_is_definite (type), NULL);
++
++  data = g_bytes_get_data (bytes, &size);
++
++  return g_variant_new_serialised (g_variant_type_info_get (type), g_bytes_ref (bytes), data, size, trusted);
++}
++
++/**
++ * g_variant_get_data_as_bytes:
++ * @value: a #GVariant
++ *
++ * Returns a pointer to the serialised form of a #GVariant instance.
++ * The semantics of this function are exactly the same as
++ * g_variant_get_data(), except that the returned #GBytes holds
++ * a reference to the variant data.
++ *
++ * Returns: (transfer full): A new #GBytes representing the variant data
++ *
++ * Since: 2.36
++ */
++GBytes *
++g_variant_get_data_as_bytes (GVariant *value)
++{
++  gconstpointer data;
++  GBytes *bytes;
++  gsize size;
++  gconstpointer bytes_data;
++  gsize bytes_size;
++
++  data = g_variant_get_serialised (value, &bytes, &size);
++  bytes_data = g_bytes_get_data (bytes, &bytes_size);
++
++  /* Try to reuse the GBytes held internally by GVariant, if it exists
++   * and is covering exactly the correct range.
++   */
++  if (data == bytes_data && size == bytes_size)
++    return g_bytes_ref (bytes);
++
++  /* See g_variant_get_data() about why it can return NULL... */
++  else if (data == NULL)
++    return g_bytes_new_take (g_malloc0 (size), size);
++
++  /* Otherwise, make a new GBytes with reference to the old. */
++  else
++    return g_bytes_new_with_free_func (data, size, (GDestroyNotify) g_bytes_unref, g_bytes_ref (bytes));
+ }
+ /* Epilogue {{{1 */
+diff --git a/glib/gvariant.h b/glib/gvariant.h
+index bdc3795..852376a 100644
+--- a/glib/gvariant.h
++++ b/glib/gvariant.h
+@@ -46,6 +46,7 @@ typedef enum
+   G_VARIANT_CLASS_INT64         = 'x',
+   G_VARIANT_CLASS_UINT64        = 't',
+   G_VARIANT_CLASS_HANDLE        = 'h',
++  G_VARIANT_CLASS_FLOAT         = 'f',
+   G_VARIANT_CLASS_DOUBLE        = 'd',
+   G_VARIANT_CLASS_STRING        = 's',
+   G_VARIANT_CLASS_OBJECT_PATH   = 'o',
+@@ -97,6 +98,8 @@ GLIB_AVAILABLE_IN_ALL
+ GVariant *                      g_variant_new_uint64                    (guint64               value);
+ GLIB_AVAILABLE_IN_ALL
+ GVariant *                      g_variant_new_handle                    (gint32                value);
++GLIB_AVAILABLE_IN_2_44
++GVariant *                      g_variant_new_float                     (gfloat                value);
+ GLIB_AVAILABLE_IN_ALL
+ GVariant *                      g_variant_new_double                    (gdouble               value);
+ GLIB_AVAILABLE_IN_ALL
+@@ -150,6 +153,8 @@ GLIB_AVAILABLE_IN_ALL
+ guint64                         g_variant_get_uint64                    (GVariant             *value);
+ GLIB_AVAILABLE_IN_ALL
+ gint32                          g_variant_get_handle                    (GVariant             *value);
++GLIB_AVAILABLE_IN_2_44
++gfloat                          g_variant_get_float                     (GVariant             *value);
+ GLIB_AVAILABLE_IN_ALL
+ gdouble                         g_variant_get_double                    (GVariant             *value);
+ GLIB_AVAILABLE_IN_ALL
+diff --git a/glib/gvarianttype.c b/glib/gvarianttype.c
+index 58a4a59..2959da5 100644
+--- a/glib/gvarianttype.c
++++ b/glib/gvarianttype.c
+@@ -113,7 +113,7 @@
+  * A basic type string describes a basic type (as per
+  * g_variant_type_is_basic()) and is always a single character in length.
+  * The valid basic type strings are "b", "y", "n", "q", "i", "u", "x", "t",
+- * "h", "d", "s", "o", "g" and "?".
++ * "h", "f", "d", "s", "o", "g" and "?".
+  *
+  * The above definition is recursive to arbitrary depth. "aaaaai" and
+  * "(ui(nq((y)))s)" are both valid type strings, as is
+@@ -134,6 +134,8 @@
+  * - `h`: the type string of %G_VARIANT_TYPE_HANDLE; a signed 32 bit value
+  *   that, by convention, is used as an index into an array of file
+  *   descriptors that are sent alongside a D-Bus message.
++ * - `f`: the type string of %G_VARIANT_TYPE_FLOAT; a single precision
++ *   floating point value.
+  * - `d`: the type string of %G_VARIANT_TYPE_DOUBLE; a double precision
+  *   floating point value.
+  * - `s`: the type string of %G_VARIANT_TYPE_STRING; a string.
+@@ -234,7 +236,7 @@ variant_type_string_scan_internal (const gchar  *string,
+     case '{':
+       if (depth_limit == 0 ||
+           string == limit || *string == '\0' ||                                  /* { */
+-          !strchr ("bynqihuxtdsog?", *string++) ||                               /* key */
++          !strchr ("bynqihuxtfdsog?", *string++) ||                               /* key */
+           !variant_type_string_scan_internal (string, limit, &string,
+                                               &child_depth, depth_limit - 1) ||  /* value */
+           string == limit || *string++ != '}')                                   /* } */
+@@ -254,7 +256,7 @@ variant_type_string_scan_internal (const gchar  *string,
+     case 'b': case 'y': case 'n': case 'q': case 'i': case 'u':
+     case 'x': case 't': case 'd': case 's': case 'o': case 'g':
+-    case 'v': case 'r': case '*': case '?': case 'h':
++    case 'v': case 'r': case '*': case '?': case 'h': case 'f':
+       max_depth = MAX (max_depth, 1);
+       break;
+@@ -600,8 +602,8 @@ g_variant_type_is_container (const GVariantType *type)
+  *
+  * Determines if the given @type is a basic type.
+  *
+- * Basic types are booleans, bytes, integers, doubles, strings, object
+- * paths and signatures.
++ * Basic types are booleans, bytes, integers, floats, doubles, strings,
++ * object paths and signatures.
+  *
+  * Only a basic type may be used as the key of a dictionary entry.
+  *
+@@ -631,6 +633,7 @@ g_variant_type_is_basic (const GVariantType *type)
+     case 'u':
+     case 't':
+     case 'x':
++    case 'f':
+     case 'd':
+     case 's':
+     case 'o':
+diff --git a/glib/gvarianttype.h b/glib/gvarianttype.h
+index 6374957..c613c5a 100644
+--- a/glib/gvarianttype.h
++++ b/glib/gvarianttype.h
+@@ -105,6 +105,15 @@ typedef struct _GVariantType GVariantType;
+  **/
+ #define G_VARIANT_TYPE_UINT64               ((const GVariantType *) "t")
++/**
++ * G_VARIANT_TYPE_FLOAT:
++ *
++ * The type of a single precision IEEE754 floating point number.  You
++ * can store a number as large as 3.40e38 in these (plus and minus) but
++ * there are some gaps on the way.
++ **/
++#define G_VARIANT_TYPE_FLOAT                ((const GVariantType *) "f")
++
+ /**
+  * G_VARIANT_TYPE_DOUBLE:
+  *
+diff --git a/glib/gvarianttypeinfo.c b/glib/gvarianttypeinfo.c
+index d2df7fd..466055d 100644
+--- a/glib/gvarianttypeinfo.c
++++ b/glib/gvarianttypeinfo.c
+@@ -120,7 +120,7 @@ static const GVariantTypeInfo g_variant_type_info_basic_table[24] = {
+   /* 'c' */ { not_a_type },
+   /* 'd' */ { fixed_aligned(8) },   /* double */
+   /* 'e' */ { not_a_type },
+-  /* 'f' */ { not_a_type },
++  /* 'f' */ { fixed_aligned(4) },   /* float */
+   /* 'g' */ { unaligned        },   /* signature string */
+   /* 'h' */ { fixed_aligned(4) },   /* file handle (int32) */
+   /* 'i' */ { fixed_aligned(4) },   /* int32 */
+@@ -152,7 +152,7 @@ static const GVariantTypeInfo g_variant_type_info_basic_table[24] = {
+  * GVariantTypeInfo itself, we save a bunch of relocations.
+  */
+ static const char g_variant_type_info_basic_chars[24][2] = {
+-  "b", " ", "d", " ", " ", "g", "h", "i", " ", " ", " ", " ",
++  "b", " ", "d", " ", "f", "g", "h", "i", " ", " ", " ", " ",
+   "n", "o", " ", "q", " ", "s", "t", "u", "v", " ", "x", "y"
+ };
+diff --git a/glib/meson.build b/glib/meson.build
+index c26a35e..6703deb 100644
+--- a/glib/meson.build
++++ b/glib/meson.build
+@@ -344,6 +344,7 @@ glib_sources += files(
+   'gvariant-core.c',
+   'gvariant-parser.c',
+   'gvariant-serialiser.c',
++  'gvariant-vectors.c',
+   'gvarianttypeinfo.c',
+   'gvarianttype.c',
+   'gversion.c',
+diff --git a/glib/tests/bytes.c b/glib/tests/bytes.c
+index e81a5f4..cd87734 100644
+--- a/glib/tests/bytes.c
++++ b/glib/tests/bytes.c
+@@ -16,7 +16,12 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include <errno.h>
++#include <unistd.h>
+ #include "glib.h"
++#include "glib-unix.h"
++
++#include "glib-linux.h"
+ /* Keep in sync with glib/gbytes.c */
+ struct _GBytes
+@@ -271,7 +276,7 @@ test_to_data_transferred (void)
+   GBytes *bytes;
+   /* Memory transferred: one reference, and allocated with g_malloc */
+-  bytes = g_bytes_new (NYAN, N_NYAN);
++  bytes = g_bytes_new_take (g_memdup (NYAN, N_NYAN), N_NYAN);
+   memory = g_bytes_get_data (bytes, NULL);
+   data = g_bytes_unref_to_data (bytes, &size);
+   g_assert_true (data == memory);
+@@ -349,7 +354,7 @@ test_to_array_transferred (void)
+   GBytes *bytes;
+   /* Memory transferred: one reference, and allocated with g_malloc */
+-  bytes = g_bytes_new (NYAN, N_NYAN);
++  bytes = g_bytes_new_take (g_memdup (NYAN, N_NYAN), N_NYAN);
+   memory = g_bytes_get_data (bytes, NULL);
+   array = g_bytes_unref_to_array (bytes);
+   g_assert_nonnull (array);
+@@ -483,6 +488,46 @@ test_unref_null (void)
+   g_bytes_unref (NULL);
+ }
++#ifdef GLIB_LINUX
++static void
++test_memfd (void)
++{
++  GBytes *bytes;
++  gint fd;
++
++  fd = glib_linux_memfd_create ("", MFD_CLOEXEC);
++  if (fd == -1 && errno == EINVAL)
++    {
++      g_test_skip ("missing kernel memfd support");
++      return;
++    }
++
++  /* We should not be able to seal this one */
++  g_assert (!g_unix_fd_ensure_zero_copy_safe (fd));
++  close (fd);
++
++  /* but this one will work */
++  fd = glib_linux_memfd_create ("", MFD_CLOEXEC | MFD_ALLOW_SEALING);
++  bytes = g_bytes_new_take_zero_copy_fd (fd);
++  g_assert_cmpint (g_bytes_get_size (bytes), ==, 0);
++  g_bytes_unref (bytes);
++
++  /* try with real data */
++  fd = glib_linux_memfd_create ("", MFD_CLOEXEC | MFD_ALLOW_SEALING);
++  g_assert_se (write (fd, NYAN, N_NYAN) == N_NYAN);
++  bytes = g_bytes_new_take_zero_copy_fd (fd);
++  g_assert_cmpint (g_bytes_get_size (bytes), ==, N_NYAN);
++  g_assert (memcmp (g_bytes_get_data (bytes, NULL), NYAN, N_NYAN) == 0);
++  g_assert (g_bytes_get_zero_copy_fd (bytes) == fd);
++
++  /* ensure that we cannot modify the fd further */
++  g_assert_se (write (fd, NYAN, N_NYAN) == -1);
++
++  /* that's enough for now */
++  g_bytes_unref (bytes);
++}
++#endif
++
+ int
+ main (int argc, char *argv[])
+ {
+@@ -507,6 +552,9 @@ main (int argc, char *argv[])
+   g_test_add_func ("/bytes/to-array/two-refs", test_to_array_two_refs);
+   g_test_add_func ("/bytes/to-array/non-malloc", test_to_array_non_malloc);
+   g_test_add_func ("/bytes/null", test_null);
++#ifdef GLIB_LINUX
++  g_test_add_func ("/bytes/memfd", test_memfd);
++#endif
+   g_test_add_func ("/bytes/get-region", test_get_region);
+   g_test_add_func ("/bytes/unref-null", test_unref_null);
+diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
+index c8f1336..3dc781c 100644
+--- a/glib/tests/gvariant.c
++++ b/glib/tests/gvariant.c
+@@ -18,14 +18,15 @@
+ #include "config.h"
+ #include <glib/gvariant-internal.h>
++#include <glib/glib-private.h>
+ #include <string.h>
+ #include <stdlib.h>
+ #include <glib.h>
+-#define BASIC "bynqiuxthdsog?"
++#define BASIC "bynqiuxthfdsog?"
+ #define N_BASIC (G_N_ELEMENTS (BASIC) - 1)
+-#define INVALIDS "cefjklpwz&@^$"
++#define INVALIDS "cejklpwz&@^$"
+ #define N_INVALIDS (G_N_ELEMENTS (INVALIDS) - 1)
+ /* see comment in gvariant-serialiser.c about this madness.
+@@ -85,6 +86,8 @@ append_type_string (GString  *string,
+           return g_variant_type_copy (G_VARIANT_TYPE_UINT64);
+         case 'h':
+           return g_variant_type_copy (G_VARIANT_TYPE_HANDLE);
++        case 'f':
++          return g_variant_type_copy (G_VARIANT_TYPE_FLOAT);
+         case 'd':
+           return g_variant_type_copy (G_VARIANT_TYPE_DOUBLE);
+         case 's':
+@@ -452,6 +455,8 @@ describe_type (const GVariantType *type)
+             result = g_strdup ("t");
+           else if (g_variant_type_equal (type, G_VARIANT_TYPE_HANDLE))
+             result = g_strdup ("h");
++          else if (g_variant_type_equal (type, G_VARIANT_TYPE_FLOAT))
++            result = g_strdup ("f");
+           else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
+             result = g_strdup ("d");
+           else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
+@@ -807,6 +812,7 @@ calculate_type_info (const GVariantType *type,
+       else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32) ||
+                g_variant_type_equal (type, G_VARIANT_TYPE_UINT32) ||
++               g_variant_type_equal (type, G_VARIANT_TYPE_FLOAT) ||
+                g_variant_type_equal (type, G_VARIANT_TYPE_HANDLE))
+         {
+           al = fs = 4;
+@@ -1960,6 +1966,7 @@ struct _TreeInstance
+   union {
+     guint64 integer;
++    gfloat single;
+     gdouble floating;
+     gchar string[200];
+   } data;
+@@ -2074,6 +2081,11 @@ tree_instance_new (const GVariantType *type,
+       instance->data_size = 8;
+       break;
++    case 'f':
++      instance->data.single = g_test_rand_double ();
++      instance->data_size = 4;
++      break;
++
+     case 'd':
+       instance->data.floating = g_test_rand_double ();
+       instance->data_size = 8;
+@@ -2597,6 +2609,10 @@ tree_instance_get_gvariant (TreeInstance *tree)
+       result = g_variant_new_handle (tree->data.integer);
+       break;
++    case 'f':
++      result = g_variant_new_float (tree->data.single);
++      break;
++
+     case 'd':
+       result = g_variant_new_double (tree->data.floating);
+       break;
+@@ -2620,6 +2636,19 @@ tree_instance_get_gvariant (TreeInstance *tree)
+   return result;
+ }
++static GVariant *
++create_random_gvariant (guint depth)
++{
++  TreeInstance *tree;
++  GVariant *value;
++
++  tree = tree_instance_new (NULL, depth);
++  value = g_variant_take_ref (tree_instance_get_gvariant (tree));
++  tree_instance_free (tree);
++
++  return value;
++}
++
+ static gboolean
+ tree_instance_check_gvariant (TreeInstance *tree,
+                               GVariant     *value)
+@@ -2724,6 +2753,13 @@ tree_instance_check_gvariant (TreeInstance *tree,
+     case 'h':
+       return g_variant_get_handle (value) == (gint32) tree->data.integer;
++    case 'f':
++      {
++        gfloat floating = g_variant_get_float (value);
++
++        return memcmp (&floating, &tree->data.single, sizeof floating) == 0;
++      }
++
+     case 'd':
+       {
+         gdouble floating = g_variant_get_double (value);
+@@ -3897,6 +3933,7 @@ test_gv_byteswap_non_normal_non_aligned (void)
+ static void
+ test_parser (void)
+ {
++  GError *error = NULL;
+   TreeInstance *tree;
+   GVariant *parsed;
+   GVariant *value;
+@@ -3910,16 +3947,19 @@ test_parser (void)
+   pt = g_variant_print (value, TRUE);
+   p = g_variant_print (value, FALSE);
+-  parsed = g_variant_parse (NULL, pt, NULL, NULL, NULL);
++  parsed = g_variant_parse (NULL, pt, NULL, NULL, &error);
++  g_assert_no_error (error);
+   res = g_variant_print (parsed, FALSE);
+-  g_assert_cmpstr (p, ==, res);
++  if (!strstr (pt, "float")) /* FIXME: need reliable round-trip for floats */
++    g_assert_cmpstr (p, ==, res);
+   g_variant_unref (parsed);
+   g_free (res);
+-  parsed = g_variant_parse (g_variant_get_type (value), p,
+-                            NULL, NULL, NULL);
++  parsed = g_variant_parse (g_variant_get_type (value), p, NULL, NULL, &error);
++  g_assert_no_error (error);
+   res = g_variant_print (parsed, TRUE);
+-  g_assert_cmpstr (pt, ==, res);
++  if (!strstr (pt, "float")) /* FIXME: need reliable round-trip for floats */
++    g_assert_cmpstr (pt, ==, res);
+   g_variant_unref (parsed);
+   g_free (res);
+@@ -4925,10 +4965,13 @@ test_gbytes (void)
+ {
+   GVariant *a;
+   GVariant *tuple;
++  GVariant *b;
++  GVariant *c;
+   GBytes *bytes;
+   GBytes *bytes2;
+   const guint8 values[5] = { 1, 2, 3, 4, 5 };
+   const guint8 *elts;
++  gchar *tmp;
+   gsize n_elts;
+   gsize i;
+@@ -4959,6 +5002,41 @@ test_gbytes (void)
+   g_bytes_unref (bytes2);
+   g_variant_unref (a);
+   g_variant_unref (tuple);
++
++  /* Feed in some non-normal data... make sure it's aligned.
++   *
++   * Here we have an array of three elements.  The first and last are
++   * normal ints ('iiii') and array-of-bytes data ('ayay').  The middle
++   * element is zero-bytes wide, which will present a problem when
++   * fetching the fixed-size integer out of it.
++   * */
++  tmp = g_strdup ("iiiiayayiiiisayay\x08\x08\x10");
++  bytes = g_bytes_new_take (tmp, strlen (tmp));
++  a = g_variant_new_from_bytes (G_VARIANT_TYPE ("a(iay)"), bytes, FALSE);
++  g_bytes_unref (bytes);
++
++  /* The middle tuple is zero bytes */
++  b = g_variant_get_child_value (a, 1);
++  g_assert_cmpint (g_variant_get_size (b), ==, 0);
++
++  /* But we're going to pull a 4-byte child out of it... */
++  c = g_variant_get_child_value (b, 0);
++  g_assert_cmpint (g_variant_get_size (c), ==, 4);
++
++  /* g_variant_get_data() is allowed to fail in this case.
++   * NB: if someone finds a way to avoid this then that's fine too...
++   */
++  g_assert (g_variant_get_data (c) == NULL);
++
++  /* but since it's four bytes, it ought to have data... */
++  bytes = g_variant_get_data_as_bytes (c);
++  g_assert_cmpint (g_bytes_get_size (bytes), ==, 4);
++  g_assert (memcmp (g_bytes_get_data (bytes, NULL), "\0\0\0\0", 4) == 0);
++  g_bytes_unref (bytes);
++
++  g_variant_unref (c);
++  g_variant_unref (b);
++  g_variant_unref (a);
+ }
+ typedef struct {
+@@ -5002,6 +5080,81 @@ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ G_GNUC_END_IGNORE_DEPRECATIONS
+ }
++static GByteArray *
++flatten_vectors (GVariantVectors *v)
++{
++  GByteArray *result;
++  guint i;
++
++  result = g_byte_array_new ();
++
++  for (i = 0; i < v->vectors->len; i++)
++    {
++      GVariantVector vec = g_array_index (v->vectors, GVariantVector, i);
++
++      if (vec.gbytes)
++        g_byte_array_append (result, vec.data.pointer, vec.size);
++      else
++        g_byte_array_append (result, v->extra_bytes->data + vec.data.offset, vec.size);
++    }
++
++  return result;
++}
++
++static void
++test_vector_serialiser (void)
++{
++  GVariantVectors vectors;
++  GByteArray *flattened;
++  GVariant *value;
++  guint i;
++
++  for (i = 0; i < 100; i++)
++    {
++      guint j;
++
++      value = create_random_gvariant (2);
++      //g_print (">>> %s\n", g_variant_print (value, TRUE));
++
++      GLIB_PRIVATE_CALL(g_variant_to_vectors) (value, &vectors);
++      for (j = 0; j < vectors.vectors->len; j++)
++        {
++          GVariantVector *v = &g_array_index (vectors.vectors, GVariantVector, j);
++
++          if (!v->gbytes)
++            {
++              v->gbytes = g_bytes_new (NULL, 0);
++              v->data.pointer = v->data.offset + vectors.extra_bytes->data;
++            }
++
++          //g_print ("  V %p %p %d\n", v, v->data.pointer, (guint) v->size);
++        }
++      GLIB_PRIVATE_CALL(g_variant_from_vectors) (g_variant_get_type (value), (GVariantVector *) vectors.vectors->data, vectors.vectors->len, g_variant_get_size (value), TRUE);
++      continue;
++      flattened = flatten_vectors (&vectors);
++      g_byte_array_free (vectors.extra_bytes, TRUE);
++      g_byte_array_free (vectors.offsets, TRUE);
++      g_array_free (vectors.vectors, TRUE);
++
++#if 0
++      if (flattened->len != g_variant_get_size (value) ||
++          memcmp (flattened->data, g_variant_get_data (value), flattened->len) != 0)
++        {
++          g_file_set_contents ("flattened", flattened->data, flattened->len, NULL);
++          g_file_set_contents ("serialised", g_variant_get_data (value), g_variant_get_size (value), NULL);
++          g_print ("type is %s\n", g_variant_get_type_string (value));
++          g_assert_not_reached ();
++        }
++#endif
++
++      g_assert_cmpint (flattened->len, ==, g_variant_get_size (value));
++      g_assert (memcmp (flattened->data, g_variant_get_data (value), flattened->len) == 0);
++
++      g_byte_array_free (flattened, TRUE);
++      g_variant_unref (value);
++    }
++}
++
+ static void
+ test_stack_builder_init (void)
+ {
+@@ -5866,6 +6019,7 @@ main (int argc, char **argv)
+   g_test_add_func ("/gvariant/gbytes", test_gbytes);
+   g_test_add_func ("/gvariant/print-context", test_print_context);
+   g_test_add_func ("/gvariant/error-quark", test_error_quark);
++  g_test_add_func ("/gvariant/vector-serialiser", test_vector_serialiser);
+   g_test_add_func ("/gvariant/stack-builder-init", test_stack_builder_init);
+   g_test_add_func ("/gvariant/stack-dict-init", test_stack_dict_init);
+-- 
+2.25.1
+
diff --git a/packaging/kdbus2.patch b/packaging/kdbus2.patch
new file mode 100644 (file)
index 0000000..edf5f8f
--- /dev/null
@@ -0,0 +1,8287 @@
+From 3800128928e1df0df28629e1d16250f342a6cfff Mon Sep 17 00:00:00 2001
+From: Karol Lewandowski <k.lewandowsk@samsung.com>
+Date: Tue, 6 Feb 2024 16:00:27 +0100
+Subject: [PATCH] tizen: Add kdbus
+
+This commit provides kdbus originally from upstream but later long
+maintained and significantly improved for Tizen project by Lukasz Skalski,
+Adrian Szyndela, Hyotaek Shim, Insun Pyo and Michal Eljasiewicz.
+
+See tizen_8.0 and older branches for complete history. The commits below
+list only the start of kdbus history (for identifiaction purposes).
+
+.
+
+commit f8c5231931f3a0d7b0508598e3f51e5cef13017f
+Author: Lukasz Skalski <l.skalski@samsung.com>
+Date:   Fri Aug 21 17:42:53 2015 +0200
+
+    gdbus: integrate kdbus into GDBus core
+    
+    Patch includes also changes made by Ryan Lortie <desrt@desrt.ca>
+    
+    https://bugzilla.gnome.org/show_bug.cgi?id=721861
+    
+    Change-Id: I6bab6fee34787816efeb42d8e3e60c4c551a8a2b
+
+commit d2e6e8fd848d104a4ae83af5eced5cf009b5f29c
+Author: Lukasz Skalski <l.skalski@samsung.com>
+Date:   Fri Aug 21 17:41:27 2015 +0200
+
+    gdbus: add kdbus core files
+    
+    Patch includes also changes made by Ryan Lortie <desrt@desrt.ca>
+    and Adrian Szyndela <a.szyndela@samsung.com>
+    
+    https://bugzilla.gnome.org/show_bug.cgi?id=721861
+    
+    Change-Id: Ie90076a28c611320cd9b5ed6f2123d0ef85c6326
+
+commit 2eae20f44d0300b3d151ae8a1f260bf122da702a
+Author: Lukasz Skalski <l.skalski@samsung.com>
+Date:   Fri Aug 21 16:47:05 2015 +0200
+
+    gdbus: import kdbus interface header
+    
+    kdbus.h taken from https://github.com/systemd/kdbus, commit 9c04b6b989c9.
+    
+    Hosting of our own copy of the kdbus header is temporary solution.
+    In the future, we should include it from the system kernel headers.
+    
+    https://bugzilla.gnome.org/show_bug.cgi?id=721861
+    
+    Change-Id: I2a42c91648b10c7c14f4df9ce2755f7c60524903 
+
+---
+ gio/gdbusaddress.c             |  107 +-
+ gio/gdbusconnection.c          | 1376 ++++++++--
+ gio/gdbusconnection.h          |   42 +
+ gio/gdbusmessage.c             |    7 +
+ gio/gdbusmethodinvocation.c    |   38 +
+ gio/gdbusmethodinvocation.h    |    5 +
+ gio/gdbusnameowning.c          |   19 +-
+ gio/gdbusnamewatching.c        |   24 +-
+ gio/gdbusobjectmanagerclient.c |   34 +-
+ gio/gdbusprivate.c             |  202 +-
+ gio/gdbusprivate.h             |   20 +-
+ gio/gdbusproxy.c               |   20 +-
+ gio/gdbusutils.c               |    9 +
+ gio/gioenums.h                 |   60 +
+ gio/gkdbus.c                   | 4356 ++++++++++++++++++++++++++++++++
+ gio/gkdbus.h                   |  203 ++
+ gio/gkdbusfakedaemon.c         |  694 +++++
+ gio/gkdbusfakedaemon.h         |   39 +
+ gio/gunixfdlist.h              |    3 +
+ gio/meson.build                |   19 +-
+ meson_options.txt              |   10 +
+ 21 files changed, 6936 insertions(+), 351 deletions(-)
+ mode change 100644 => 100755 gio/gdbusconnection.c
+ mode change 100644 => 100755 gio/gdbusnamewatching.c
+ create mode 100755 gio/gkdbus.c
+ create mode 100644 gio/gkdbus.h
+ create mode 100644 gio/gkdbusfakedaemon.c
+ create mode 100644 gio/gkdbusfakedaemon.h
+
+diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c
+index b73ff0d..e23dff8 100644
+--- a/gio/gdbusaddress.c
++++ b/gio/gdbusaddress.c
+@@ -42,6 +42,9 @@
+ #include "gdbusprivate.h"
+ #include "gstdio.h"
++#ifdef G_OS_UNIX
++#include "gkdbus.h"
++#endif
+ #ifdef HAVE_UNISTD_H
+ #include <unistd.h>
+ #endif
+@@ -550,8 +553,9 @@ out:
+ /* ---------------------------------------------------------------------------------------------------- */
+-static GIOStream *
++static GObject *
+ g_dbus_address_try_connect_one (const gchar   *address_entry,
++                                gboolean       kdbus_okay,
+                                 gchar        **out_guid,
+                                 GCancellable  *cancellable,
+                                 GError       **error);
+@@ -561,14 +565,15 @@ g_dbus_address_try_connect_one (const gchar   *address_entry,
+  * point. That way we can implement a D-Bus transport over X11 without
+  * making libgio link to libX11...
+  */
+-static GIOStream *
++static GObject *
+ g_dbus_address_connect (const gchar   *address_entry,
+                         const gchar   *transport_name,
++                        gboolean       kdbus_okay,
+                         GHashTable    *key_value_pairs,
+                         GCancellable  *cancellable,
+                         GError       **error)
+ {
+-  GIOStream *ret;
++  GObject *ret;
+   GSocketConnectable *connectable;
+   const gchar *nonce_file;
+@@ -576,7 +581,35 @@ g_dbus_address_connect (const gchar   *address_entry,
+   ret = NULL;
+   nonce_file = NULL;
+-  if (g_strcmp0 (transport_name, "unix") == 0)
++  if (FALSE)
++    {
++    }
++#if defined (G_OS_UNIX) && defined (KDBUS)
++  else if (kdbus_okay && g_str_equal (transport_name, "kernel"))
++    {
++      GKDBusWorker *worker;
++      const gchar *path;
++
++      path = g_hash_table_lookup (key_value_pairs, "path");
++
++      if (path == NULL)
++        {
++          g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
++                       _("Error in address '%s' - the kernel transport requires a path"),
++                       address_entry);
++        }
++      else
++        {
++          worker = _g_kdbus_worker_new (path, error);
++
++          if (worker == NULL)
++            return NULL;
++
++          return G_OBJECT (worker);
++        }
++    }
++#endif
++  else if (g_strcmp0 (transport_name, "unix") == 0)
+     {
+       const gchar *path;
+       const gchar *abstract;
+@@ -665,7 +698,7 @@ g_dbus_address_connect (const gchar   *address_entry,
+       autolaunch_address = get_session_address_dbus_launch (error);
+       if (autolaunch_address != NULL)
+         {
+-          ret = g_dbus_address_try_connect_one (autolaunch_address, NULL, cancellable, error);
++          ret = g_dbus_address_try_connect_one (autolaunch_address, kdbus_okay, NULL, cancellable, error);
+           g_free (autolaunch_address);
+           goto out;
+         }
+@@ -707,7 +740,7 @@ g_dbus_address_connect (const gchar   *address_entry,
+       if (connection == NULL)
+         goto out;
+-      ret = G_IO_STREAM (connection);
++      ret = G_OBJECT (connection);
+       if (nonce_file != NULL)
+         {
+@@ -763,7 +796,7 @@ g_dbus_address_connect (const gchar   *address_entry,
+             }
+           fclose (f);
+-          if (!g_output_stream_write_all (g_io_stream_get_output_stream (ret),
++          if (!g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (connection)),
+                                           nonce_contents,
+                                           16,
+                                           NULL,
+@@ -783,13 +816,14 @@ g_dbus_address_connect (const gchar   *address_entry,
+   return ret;
+ }
+-static GIOStream *
++static GObject *
+ g_dbus_address_try_connect_one (const gchar   *address_entry,
++                                gboolean       kdbus_okay,
+                                 gchar        **out_guid,
+                                 GCancellable  *cancellable,
+                                 GError       **error)
+ {
+-  GIOStream *ret;
++  GObject *ret;
+   GHashTable *key_value_pairs;
+   gchar *transport_name;
+   const gchar *guid;
+@@ -806,6 +840,7 @@ g_dbus_address_try_connect_one (const gchar   *address_entry,
+   ret = g_dbus_address_connect (address_entry,
+                                 transport_name,
++                                kdbus_okay,
+                                 key_value_pairs,
+                                 cancellable,
+                                 error);
+@@ -969,7 +1004,25 @@ g_dbus_address_get_stream_sync (const gchar   *address,
+                                 GCancellable  *cancellable,
+                                 GError       **error)
+ {
+-  GIOStream *ret;
++  GObject *result;
++
++  result = g_dbus_address_get_stream_internal (address, FALSE, out_guid, cancellable, error);
++  g_assert (result == NULL || G_IS_IO_STREAM (result));
++
++  if (result)
++    return G_IO_STREAM (result);
++
++  return NULL;
++}
++
++GObject *
++g_dbus_address_get_stream_internal (const gchar   *address,
++                                    gboolean       kdbus_okay,
++                                    gchar        **out_guid,
++                                    GCancellable  *cancellable,
++                                    GError       **error)
++{
++  GObject *ret;
+   gchar **addr_array;
+   guint n;
+   GError *last_error;
+@@ -996,6 +1049,7 @@ g_dbus_address_get_stream_sync (const gchar   *address,
+       this_error = NULL;
+       ret = g_dbus_address_try_connect_one (addr,
++                                            kdbus_okay,
+                                             out_guid,
+                                             cancellable,
+                                             &this_error);
+@@ -1071,6 +1125,30 @@ out:
+ #endif
+ }
++static gchar *
++get_session_address_kdbus (void)
++{
++#ifdef G_OS_UNIX
++  gchar *ret = NULL;
++  gchar *bus;
++  GStatBuf buf;
++
++  bus = g_strdup_printf ("/sys/fs/kdbus/%d-user/bus", getuid());
++
++  /* if ENOENT, EPERM, etc., quietly don't use it */
++  if (g_stat (bus, &buf) < 0)
++    goto out;
++
++  ret = g_strconcat ("kernel:path=", bus, NULL);
++
++out:
++  g_free (bus);
++  return ret;
++#else
++  return NULL;
++#endif
++}
++
+ /* ---------------------------------------------------------------------------------------------------- */
+ #ifdef G_OS_UNIX
+@@ -1299,6 +1377,7 @@ g_dbus_address_get_for_bus_sync (GBusType       bus_type,
+   if (G_UNLIKELY (_g_dbus_debug_address ()))
+     {
+       guint n;
++      gchar *s;
+       _g_dbus_debug_print_lock ();
+       s = _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type);
+       g_print ("GDBus-debug:Address: In g_dbus_address_get_for_bus_sync() for bus type '%s'\n",
+@@ -1342,7 +1421,7 @@ g_dbus_address_get_for_bus_sync (GBusType       bus_type,
+            * https://dbus.freedesktop.org/doc/dbus-specification.html#ftn.id-1.13.6.4.3.3
+            * or, on systems where /run is the same as /var/run, runstatedir:
+            * https://gitlab.freedesktop.org/dbus/dbus/-/merge_requests/209 */
+-          ret = g_strdup ("unix:path=" GLIB_RUNSTATEDIR "/dbus/system_bus_socket");
++          ret = g_strdup ("kernel:path=/sys/fs/kdbus/0-system/bus;unix:path=" GLIB_RUNSTATEDIR "/dbus/system_bus_socket");
+         }
+       break;
+@@ -1354,7 +1433,11 @@ g_dbus_address_get_for_bus_sync (GBusType       bus_type,
+       if (ret == NULL)
+         {
+-          ret = get_session_address_platform_specific (&local_error);
++          ret = get_session_address_kdbus ();
++          if (ret == NULL)
++            ret = get_session_address_platform_specific (&local_error);
++          if (ret == NULL)
++            goto out;
+         }
+       break;
+diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
+old mode 100644
+new mode 100755
+index 1ee2ab4..960a83c
+--- a/gio/gdbusconnection.c
++++ b/gio/gdbusconnection.c
+@@ -116,6 +116,9 @@
+ #include "gmarshal-internal.h"
+ #ifdef G_OS_UNIX
++#ifdef KDBUS
++#include "gkdbus.h"
++#endif
+ #include "gunixconnection.h"
+ #include "gunixfdmessage.h"
+ #endif
+@@ -365,6 +368,12 @@ struct _GDBusConnection
+    */
+   GDBusWorker *worker;
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  GKDBusWorker *kdbus_worker;
++#endif
++#endif
++
+   /* If connected to a message bus, this contains the unique name assigned to
+    * us by the bus (e.g. ":1.42").
+    * Read-only after initable_init(), so it may be read if you either
+@@ -603,6 +612,7 @@ g_dbus_connection_dispose (GObject *object)
+   G_LOCK (message_bus_lock);
+   CONNECTION_LOCK (connection);
++
+   if (connection->worker != NULL)
+     {
+       _g_dbus_worker_stop (connection->worker);
+@@ -610,6 +620,17 @@ g_dbus_connection_dispose (GObject *object)
+       if (alive_connections != NULL)
+         g_warn_if_fail (g_hash_table_remove (alive_connections, connection));
+     }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  else if (connection->kdbus_worker != NULL)
++    {
++      _g_kdbus_worker_stop (connection->kdbus_worker);
++      connection->kdbus_worker = NULL;
++      if (alive_connections != NULL)
++        g_warn_if_fail (g_hash_table_remove (alive_connections, connection));
++    }
++#endif
++#endif
+   else
+     {
+       if (alive_connections != NULL)
+@@ -1096,6 +1117,19 @@ g_dbus_connection_init (GDBusConnection *connection)
+   connection->filters = g_ptr_array_new ();
+ }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++gboolean
++_g_dbus_connection_is_kdbus (GDBusConnection *connection)
++{
++  if (connection->kdbus_worker)
++    return TRUE;
++  else
++    return FALSE;
++}
++#endif
++#endif
++
+ /**
+  * g_dbus_connection_get_stream:
+  * @connection: a #GDBusConnection
+@@ -1142,8 +1176,16 @@ g_dbus_connection_start_message_processing (GDBusConnection *connection)
+   if (!check_initialized (connection))
+     return;
+-  g_assert (connection->worker != NULL);
+-  _g_dbus_worker_unfreeze (connection->worker);
++  if (connection->worker)
++    _g_dbus_worker_unfreeze (connection->worker);
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  else if (connection->kdbus_worker)
++    _g_kdbus_worker_unfreeze (connection->kdbus_worker);
++#endif
++#endif
++  else
++    g_assert_not_reached ();
+ }
+ /**
+@@ -1334,11 +1376,22 @@ g_dbus_connection_flush_sync (GDBusConnection  *connection,
+   if (!check_unclosed (connection, 0, error))
+     goto out;
+-  g_assert (connection->worker != NULL);
+-
+-  ret = _g_dbus_worker_flush_sync (connection->worker,
+-                                   cancellable,
+-                                   error);
++  if (connection->worker != NULL)
++    {
++      ret = _g_dbus_worker_flush_sync (connection->worker,
++                                       cancellable,
++                                       error);
++    }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  else if (connection->kdbus_worker != NULL)
++    {
++      ret = _g_kdbus_worker_flush_sync (connection->kdbus_worker);
++    }
++#endif
++#endif
++  else
++    g_assert_not_reached();
+  out:
+   return ret;
+@@ -1438,7 +1491,7 @@ schedule_closed_unlocked (GDBusConnection *connection,
+  * of the thread that @connection was constructed in.
+  *
+  * This is an asynchronous method. When the operation is finished,
+- * @callback will be invoked in the 
++ * @callback will be invoked in the
+  * [thread-default main context][g-main-context-push-thread-default]
+  * of the thread you are calling this method from. You can
+  * then call g_dbus_connection_close_finish() to get the result of the
+@@ -1457,108 +1510,763 @@ g_dbus_connection_close (GDBusConnection     *connection,
+   g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+-  /* do not use g_return_val_if_fail(), we want the memory barrier */
+-  if (!check_initialized (connection))
+-    return;
++  /* do not use g_return_val_if_fail(), we want the memory barrier */
++  if (!check_initialized (connection))
++    return;
++
++  task = g_task_new (connection, cancellable, callback, user_data);
++  g_task_set_source_tag (task, g_dbus_connection_close);
++  if (connection->worker)
++    {
++      _g_dbus_worker_close (connection->worker, task);
++    }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  else if (connection->kdbus_worker)
++    {
++      _g_kdbus_worker_close (connection->kdbus_worker, task);
++    }
++#endif
++#endif
++  else
++    g_assert_not_reached();
++  g_object_unref (task);
++}
++
++/**
++ * g_dbus_connection_close_finish:
++ * @connection: a #GDBusConnection
++ * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed
++ *     to g_dbus_connection_close()
++ * @error: return location for error or %NULL
++ *
++ * Finishes an operation started with g_dbus_connection_close().
++ *
++ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set
++ *
++ * Since: 2.26
++ */
++gboolean
++g_dbus_connection_close_finish (GDBusConnection  *connection,
++                                GAsyncResult     *res,
++                                GError          **error)
++{
++  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
++  g_return_val_if_fail (g_task_is_valid (res, connection), FALSE);
++  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
++
++  return g_task_propagate_boolean (G_TASK (res), error);
++}
++
++typedef struct {
++    GMainLoop *loop;
++    GAsyncResult *result;
++} SyncCloseData;
++
++/* Can be called by any thread, without the connection lock */
++static void
++sync_close_cb (GObject *source_object,
++               GAsyncResult *res,
++               gpointer user_data)
++{
++  SyncCloseData *data = user_data;
++
++  data->result = g_object_ref (res);
++  g_main_loop_quit (data->loop);
++}
++
++/**
++ * g_dbus_connection_close_sync:
++ * @connection: a #GDBusConnection
++ * @cancellable: (nullable): a #GCancellable or %NULL
++ * @error: return location for error or %NULL
++ *
++ * Synchronously closes @connection. The calling thread is blocked
++ * until this is done. See g_dbus_connection_close() for the
++ * asynchronous version of this method and more details about what it
++ * does.
++ *
++ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set
++ *
++ * Since: 2.26
++ */
++gboolean
++g_dbus_connection_close_sync (GDBusConnection  *connection,
++                              GCancellable     *cancellable,
++                              GError          **error)
++{
++  gboolean ret;
++
++  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
++  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
++
++  ret = FALSE;
++
++  if (check_unclosed (connection, 0, error))
++    {
++      GMainContext *context;
++      SyncCloseData data;
++
++      context = g_main_context_new ();
++      g_main_context_push_thread_default (context);
++      data.loop = g_main_loop_new (context, TRUE);
++      data.result = NULL;
++
++      g_dbus_connection_close (connection, cancellable, sync_close_cb, &data);
++      g_main_loop_run (data.loop);
++      ret = g_dbus_connection_close_finish (connection, data.result, error);
++
++      g_object_unref (data.result);
++      g_main_loop_unref (data.loop);
++      g_main_context_pop_thread_default (context);
++      g_main_context_unref (context);
++    }
++
++  return ret;
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++/**
++ * g_dbus_request_name:
++ * @connection: a #GDBusConnection
++ * @name: well-known bus name (name to request)
++ * @flags: a set of flags from the GBusNameOwnerFlags enumeration
++ * @error: return location for error or %NULL
++ *
++ * Synchronously acquires name on the bus and returns status code
++ * from the #GBusRequestNameReplyFlags enumeration.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: status code or %G_BUS_REQUEST_NAME_FLAGS_ERROR
++ *     if @error is set.
++ *
++ * Since: 2.44
++ */
++GBusRequestNameReplyFlags
++g_dbus_request_name (GDBusConnection     *connection,
++                     const gchar         *name,
++                     GBusNameOwnerFlags   flags,
++                     GError             **error)
++{
++  GVariant *result;
++  guint32 request_name_reply;
++
++  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), G_BUS_RELEASE_NAME_FLAGS_ERROR);
++  g_return_val_if_fail (g_dbus_is_name (name) && !g_dbus_is_unique_name (name), G_BUS_RELEASE_NAME_FLAGS_ERROR);
++  g_return_val_if_fail (error == NULL || *error == NULL, G_BUS_RELEASE_NAME_FLAGS_ERROR);
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  if (connection->kdbus_worker)
++    return _g_kdbus_RequestName (connection->kdbus_worker, name, flags, error);
++#endif
++#endif
++
++  result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/org/freedesktop/DBus",
++                                        "org.freedesktop.DBus", "RequestName",
++                                        g_variant_new ("(su)", name, flags), G_VARIANT_TYPE ("(u)"),
++                                        G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++  if (result != NULL)
++    {
++      g_variant_get (result, "(u)", &request_name_reply);
++      g_variant_unref (result);
++    }
++  else
++    request_name_reply = G_BUS_REQUEST_NAME_FLAGS_ERROR;
++
++  return (GBusRequestNameReplyFlags) request_name_reply;
++}
++
++/**
++ * g_dbus_release_name:
++ * @connection: a #GDBusConnection
++ * @name: well-known bus name (name to release)
++ * @error: return location for error or %NULL
++ *
++ * Synchronously releases name on the bus and returns status code
++ * from the #GBusReleaseNameReplyFlags enumeration.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: status code or %G_BUS_RELEASE_NAME_FLAGS_ERROR
++ *     if @error is set.
++ *
++ * Since: 2.44
++ */
++GBusReleaseNameReplyFlags
++g_dbus_release_name (GDBusConnection  *connection,
++                     const gchar      *name,
++                     GError          **error)
++{
++  GVariant *result;
++  guint32 release_name_reply;
++
++  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), G_BUS_RELEASE_NAME_FLAGS_ERROR);
++  g_return_val_if_fail (g_dbus_is_name (name) && !g_dbus_is_unique_name (name), G_BUS_RELEASE_NAME_FLAGS_ERROR);
++  g_return_val_if_fail (error == NULL || *error == NULL, G_BUS_RELEASE_NAME_FLAGS_ERROR);
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  if (connection->kdbus_worker)
++    return _g_kdbus_ReleaseName (connection->kdbus_worker, name, error);
++#endif
++#endif
++
++  result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/org/freedesktop/DBus",
++                                        "org.freedesktop.DBus", "ReleaseName",
++                                        g_variant_new ("(s)", name), G_VARIANT_TYPE ("(u)"),
++                                        G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++  if (result != NULL)
++    {
++      g_variant_get (result, "(u)", &release_name_reply);
++      g_variant_unref (result);
++    }
++  else
++    release_name_reply = G_BUS_RELEASE_NAME_FLAGS_ERROR;
++
++  return (GBusReleaseNameReplyFlags) release_name_reply;
++}
++
++/**
++ * g_dbus_add_match:
++ * @connection: a #GDBusConnection
++ * @match_rule: match rule to add to the @connection
++ * @error: return location for error or %NULL
++ *
++ * Synchronously adds a match rule to match messages.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: %TRUE if the operation succeeded, %FALSE
++ *     if @error is set.
++ *
++ * Since: 2.44
++ */
++gboolean
++g_dbus_add_match (GDBusConnection  *connection,
++                  const gchar      *match_rule,
++                  GError          **error)
++{
++  GVariant *result;
++  gboolean ret;
++
++  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
++  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
++
++  result = NULL;
++  ret = FALSE;
++
++  if (match_rule[0] == '-')
++    return ret;
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  if (connection->kdbus_worker)
++    return _g_kdbus_AddMatch (connection->kdbus_worker, match_rule, error);
++#endif
++#endif
++
++  result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/org/freedesktop/DBus",
++                                        "org.freedesktop.DBus", "AddMatch",
++                                        g_variant_new ("(s)", match_rule), NULL,
++                                        G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++  if (result != NULL)
++    {
++      ret = TRUE;
++      g_variant_unref (result);
++    }
++
++  return ret;
++}
++
++/**
++ * g_dbus_remove_match:
++ * @connection: a #GDBusConnection
++ * @match_rule: match rule to remove from the @connection
++ * @error: return location for error or %NULL
++ *
++ * Synchronously removes the first rule that matches.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: %TRUE if the operation succeeded, %FALSE
++ *     if @error is set.
++ *
++ * Since: 2.44
++ */
++gboolean
++g_dbus_remove_match (GDBusConnection  *connection,
++                     const gchar      *match_rule,
++                     GError          **error)
++{
++  GVariant *result;
++  gboolean ret;
++
++  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
++  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
++
++  result = NULL;
++  ret = FALSE;
++
++  if (match_rule[0] == '-')
++    return ret;
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  if (connection->kdbus_worker)
++    return _g_kdbus_RemoveMatch (connection->kdbus_worker, match_rule, error);
++#endif
++#endif
++
++  result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/org/freedesktop/DBus",
++                                        "org.freedesktop.DBus", "RemoveMatch",
++                                        g_variant_new ("(s)", match_rule), NULL,
++                                        G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++  if (result != NULL)
++    {
++      ret = TRUE;
++      g_variant_unref (result);
++    }
++
++  return ret;
++}
++
++/**
++ * g_dbus_get_bus_id:
++ * @connection: a #GDBusConnection
++ * @error: return location for error or %NULL
++ *
++ * Synchronously returns the unique ID of the bus.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: the unique ID of the bus or %NULL if @error is set.
++ *     Free with g_free().
++ *
++ * Since: 2.44
++ */
++gchar *
++g_dbus_get_bus_id (GDBusConnection  *connection,
++                   GError          **error)
++{
++  GVariant *result;
++  gchar *bus_id;
++
++  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
++  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
++
++  result = NULL;
++  bus_id = NULL;
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  if (connection->kdbus_worker)
++    return _g_kdbus_GetBusId (connection->kdbus_worker, error);
++#endif
++#endif
++
++  result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/",
++                                        "org.freedesktop.DBus", "GetId",
++                                        NULL, G_VARIANT_TYPE ("(s)"),
++                                        G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++  if (result != NULL)
++    {
++      g_variant_get (result, "(s)", &bus_id);
++      g_variant_unref (result);
++    }
++
++  return bus_id;
++}
++
++typedef enum
++{
++  LIST_NAMES,
++  LIST_ACTIVATABLE_NAMES,
++  LIST_QUEUED_OWNERS
++} GDBusListNameType;
++
++static gchar **
++_g_dbus_get_list_internal (GDBusConnection    *connection,
++                           const gchar        *name,
++                           GDBusListNameType   list_name_type,
++                           GError            **error)
++{
++  gchar **strv;
++  GVariant *result;
++  GVariantIter *iter;
++  gchar *str;
++  gsize n, i;
++
++  result = NULL;
++  strv = NULL;
++
++  if (list_name_type == LIST_QUEUED_OWNERS)
++    {
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++      if (connection->kdbus_worker)
++        return _g_kdbus_GetListQueuedOwners (connection->kdbus_worker, name, error);
++#endif
++#endif
++
++      result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/",
++                                            "org.freedesktop.DBus", "ListQueuedOwners",
++                                            g_variant_new ("(s)", name), G_VARIANT_TYPE ("(as)"),
++                                            G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++    }
++  else
++    {
++      gchar *method_name;
++
++      if (list_name_type == LIST_NAMES)
++        method_name = "ListNames";
++      else
++        method_name = "ListActivatableNames";
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++      if (connection->kdbus_worker)
++        return _g_kdbus_GetListNames (connection->kdbus_worker, list_name_type, error);
++#endif
++#endif
++
++      result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/",
++                                            "org.freedesktop.DBus", method_name,
++                                            NULL, G_VARIANT_TYPE ("(as)"),
++                                            G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++    }
++
++  if (result != NULL)
++    {
++      g_variant_get (result, "(as)", &iter);
++      n = g_variant_iter_n_children (iter);
++      strv = g_new (gchar *, n + 1);
++
++      i = 0;
++      while (g_variant_iter_loop (iter, "s", &str))
++        {
++          strv[i] = g_strdup (str);
++          i++;
++        }
++      strv[i] = NULL;
++
++      g_variant_iter_free (iter);
++      g_variant_unref (result);
++    }
++
++  return strv;
++}
++
++/**
++ * g_dbus_get_list_names:
++ * @connection: a #GDBusConnection
++ * @error: return location for error or %NULL
++ *
++ * Synchronously returns a list of all currently-owned names on the bus.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: a list of all currently-owned names on the bus or %NULL if
++ *     @error is set. Free with g_strfreev().
++ *
++ * Since: 2.44
++ */
++gchar **
++g_dbus_get_list_names (GDBusConnection  *connection,
++                       GError          **error)
++{
++  gchar **strv;
++
++  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
++  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
++
++  strv = _g_dbus_get_list_internal (connection, NULL, LIST_NAMES, error);
++
++  return strv;
++}
++
++/**
++ * g_dbus_get_list_activatable_names:
++ * @connection: a #GDBusConnection
++ * @error: return location for error or %NULL
++ *
++ * Synchronously returns a list of all names that can be activated on the bus.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: a list of all names that can be activated on the bus or %NULL if
++ *     @error is set. Free with g_strfreev().
++ *
++ * Since: 2.44
++ */
++gchar **
++g_dbus_get_list_activatable_names (GDBusConnection  *connection,
++                                   GError          **error)
++{
++  gchar **strv;
++
++  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
++  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
++
++  strv = _g_dbus_get_list_internal (connection, NULL, LIST_ACTIVATABLE_NAMES, error);
++
++  return strv;
++}
++
++/**
++ * g_dbus_get_list_queued_names:
++ * @connection: a #GDBusConnection
++ * @name: a unique or well-known bus name
++ * @error: return location for error or %NULL
++ *
++ * Synchronously returns the unique bus names of connections currently queued
++ * for the @name.
++ *
++ * If @name contains a value not compatible with the D-Bus syntax and naming
++ * conventions for bus names, the operation returns %NULL and @error is set.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: the unique bus names of connections currently queued for the @name
++ *     or %NULL if @error is set. Free with g_strfreev().
++ *
++ * Since: 2.44
++ */
++gchar **
++g_dbus_get_list_queued_owners (GDBusConnection  *connection,
++                               const gchar      *name,
++                               GError          **error)
++{
++  gchar **strv;
++
++  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
++  g_return_val_if_fail (name == NULL || g_dbus_is_name (name), NULL);
++  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
++
++  strv = _g_dbus_get_list_internal (connection, name, LIST_QUEUED_OWNERS, error);
++
++  return strv;
++}
++
++/**
++ * g_dbus_get_name_owner:
++ * @connection: a #GDBusConnection
++ * @name: well-known bus name to get the owner of
++ * @error: return location for error or %NULL
++ *
++ * Synchronously returns the unique connection name of the primary owner of
++ * the name given. If the requested name doesn't have an owner, an @error is
++ * returned.
++ *
++ * If @name contains a value not compatible with the D-Bus syntax and naming
++ * conventions for bus names, the operation returns %NULL and @error is set.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: the unique connection name of the primary owner of the
++ *     name given. If the requested name doesn't have an owner, function
++ *     returns %NULL and @error is set. Free with g_free().
++ *
++ * Since: 2.44
++ */
++gchar *
++g_dbus_get_name_owner (GDBusConnection  *connection,
++                       const gchar      *name,
++                       GError          **error)
++{
++  GVariant *result;
++  gchar *name_owner;
++
++  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
++  g_return_val_if_fail (name == NULL || g_dbus_is_name (name), NULL);
++  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
++
++  name_owner = NULL;
++  result = NULL;
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  if (connection->kdbus_worker)
++    return _g_kdbus_GetNameOwner (connection->kdbus_worker, name, error);
++#endif
++#endif
++
++  result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/",
++                                        "org.freedesktop.DBus", "GetNameOwner",
++                                        g_variant_new ("(s)", name), G_VARIANT_TYPE ("(s)"),
++                                        G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++  if (result != NULL)
++    {
++      g_variant_get (result, "(s)", &name_owner);
++      g_variant_unref (result);
++    }
++  else
++    name_owner = NULL;
++
++  return name_owner;
++}
++
++/**
++ * g_dbus_get_connection_pid:
++ * @connection: a #GDBusConnection
++ * @name: a unique or well-known bus name of the connection to query
++ * @error: return location for error or %NULL
++ *
++ * Synchronously returns the Unix process ID of the process connected to the
++ * bus. If unable to determine it, an @error is returned.
++ *
++ * If @name contains a value not compatible with the D-Bus syntax and naming
++ * conventions for bus names, the operation returns -1 and @error is set.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: the Unix process ID of the process connected to the bus or -1
++ *     if @error is set.
++ *
++ * Since: 2.44
++ */
++pid_t
++g_dbus_get_connection_pid (GDBusConnection  *connection,
++                           const gchar      *name,
++                           GError          **error)
++{
++  GVariant *result;
++  pid_t pid;
++
++  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), -1);
++  g_return_val_if_fail (name == NULL || g_dbus_is_name (name), -1);
++  g_return_val_if_fail (error == NULL || *error == NULL, -1);
++
++  result = NULL;
++  pid = -1;
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  if (connection->kdbus_worker)
++    return _g_kdbus_GetConnectionUnixProcessID (connection->kdbus_worker, name, error);
++#endif
++#endif
+-  g_assert (connection->worker != NULL);
++  result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/",
++                                        "org.freedesktop.DBus", "GetConnectionUnixProcessID",
++                                        g_variant_new ("(s)", name), G_VARIANT_TYPE ("(u)"),
++                                        G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++  if (result != NULL)
++    {
++      g_variant_get (result, "(u)", &pid);
++      g_variant_unref (result);
++    }
+-  task = g_task_new (connection, cancellable, callback, user_data);
+-  g_task_set_source_tag (task, g_dbus_connection_close);
+-  _g_dbus_worker_close (connection->worker, task);
+-  g_object_unref (task);
++  return pid;
+ }
+ /**
+- * g_dbus_connection_close_finish:
++ * g_dbus_get_connection_uid:
+  * @connection: a #GDBusConnection
+- * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed
+- *     to g_dbus_connection_close()
++ * @name: a unique or well-known bus name of the connection to query
+  * @error: return location for error or %NULL
+  *
+- * Finishes an operation started with g_dbus_connection_close().
++ * Synchronously returns the Unix user ID of the process connected to the
++ * bus. If unable to determine it, an @error is returned.
+  *
+- * Returns: %TRUE if the operation succeeded, %FALSE if @error is set
++ * If @name contains a value not compatible with the D-Bus syntax and naming
++ * conventions for bus names, the operation returns -1 and @error is set.
+  *
+- * Since: 2.26
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: the Unix user ID of the process connected to the bus or -1
++ *     if @error is set.
++ *
++ * Since: 2.44
+  */
+-gboolean
+-g_dbus_connection_close_finish (GDBusConnection  *connection,
+-                                GAsyncResult     *res,
+-                                GError          **error)
++uid_t
++g_dbus_get_connection_uid (GDBusConnection  *connection,
++                           const gchar      *name,
++                           GError          **error)
+ {
+-  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
+-  g_return_val_if_fail (g_task_is_valid (res, connection), FALSE);
+-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
++  GVariant *result;
++  uid_t uid;
+-  return g_task_propagate_boolean (G_TASK (res), error);
+-}
++  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), -1);
++  g_return_val_if_fail (name == NULL || g_dbus_is_name (name), -1);
++  g_return_val_if_fail (error == NULL || *error == NULL, -1);
+-typedef struct {
+-    GMainLoop *loop;
+-    GAsyncResult *result;
+-} SyncCloseData;
++  result = NULL;
++  uid = -1;
+-/* Can be called by any thread, without the connection lock */
+-static void
+-sync_close_cb (GObject *source_object,
+-               GAsyncResult *res,
+-               gpointer user_data)
+-{
+-  SyncCloseData *data = user_data;
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  if (connection->kdbus_worker)
++    return _g_kdbus_GetConnectionUnixUser (connection->kdbus_worker, name, error);
++#endif
++#endif
+-  data->result = g_object_ref (res);
+-  g_main_loop_quit (data->loop);
++  result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/",
++                                        "org.freedesktop.DBus", "GetConnectionUnixUser",
++                                        g_variant_new ("(s)", name), G_VARIANT_TYPE ("(u)"),
++                                        G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++  if (result != NULL)
++    {
++      g_variant_get (result, "(u)", &uid);
++      g_variant_unref (result);
++    }
++
++  return uid;
+ }
+ /**
+- * g_dbus_connection_close_sync:
++ * g_dbus_start_service_by_name:
+  * @connection: a #GDBusConnection
+- * @cancellable: (nullable): a #GCancellable or %NULL
++ * @name: name of the service to start
++ * @flags: (currently not used)
+  * @error: return location for error or %NULL
+  *
+- * Synchronously closes @connection. The calling thread is blocked
+- * until this is done. See g_dbus_connection_close() for the
+- * asynchronous version of this method and more details about what it
+- * does.
++ * Synchronously tries to launch the executable associated
++ * with a @name.
+  *
+- * Returns: %TRUE if the operation succeeded, %FALSE if @error is set
++ * The calling thread is blocked until a reply is received.
+  *
+- * Since: 2.26
++ * Returns: status code or %G_BUS_START_SERVICE_REPLY_ERROR
++ *     if @error is set.
++ *
++ * Since: 2.44
+  */
+-gboolean
+-g_dbus_connection_close_sync (GDBusConnection  *connection,
+-                              GCancellable     *cancellable,
++GBusStartServiceReplyFlags
++g_dbus_start_service_by_name (GDBusConnection  *connection,
++                              const gchar      *name,
++                              guint32           flags,
+                               GError          **error)
+ {
+-  gboolean ret;
+-
+-  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
+-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+-
+-  ret = FALSE;
++  GVariant *result;
++  guint32 ret;
+-  if (check_unclosed (connection, 0, error))
+-    {
+-      GMainContext *context;
+-      SyncCloseData data;
++  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), -1);
++  g_return_val_if_fail (name == NULL || g_dbus_is_name (name), -1);
++  g_return_val_if_fail (error == NULL || *error == NULL, -1);
+-      context = g_main_context_new ();
+-      g_main_context_push_thread_default (context);
+-      data.loop = g_main_loop_new (context, TRUE);
+-      data.result = NULL;
++  result = NULL;
++  ret = G_BUS_START_SERVICE_REPLY_ERROR;
+-      g_dbus_connection_close (connection, cancellable, sync_close_cb, &data);
+-      g_main_loop_run (data.loop);
+-      ret = g_dbus_connection_close_finish (connection, data.result, error);
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  if (connection->kdbus_worker)
++    return _g_kdbus_StartServiceByName (connection->kdbus_worker, name, flags, NULL, error);
++#endif
++#endif
+-      g_object_unref (data.result);
+-      g_main_loop_unref (data.loop);
+-      g_main_context_pop_thread_default (context);
+-      g_main_context_unref (context);
++  result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/",
++                                        "org.freedesktop.DBus", "StartServiceByName",
++                                        g_variant_new ("(su)", name, flags), G_VARIANT_TYPE ("(u)"),
++                                        G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++  if (result != NULL)
++    {
++      g_variant_get (result, "(u)", &ret);
++      g_variant_unref (result);
+     }
+-  return ret;
++  return (GBusStartServiceReplyFlags) ret;
+ }
+ /* ---------------------------------------------------------------------------------------------------- */
+@@ -1601,11 +2309,13 @@ g_dbus_connection_send_message_unlocked (GDBusConnection   *connection,
+                                          GDBusMessage      *message,
+                                          GDBusSendMessageFlags flags,
+                                          guint32           *out_serial,
+-                                         GError           **error)
++                                         GError           **error,
++                                         gint timeout_msec)
+ {
+   guchar *blob;
+   gsize blob_size;
+   guint32 serial_to_use;
++  gboolean ret;
+   CONNECTION_ENSURE_LOCK (connection);
+@@ -1614,6 +2324,9 @@ g_dbus_connection_send_message_unlocked (GDBusConnection   *connection,
+   /* TODO: check all necessary headers are present */
++  ret = FALSE;
++  blob = NULL;
++
+   if (out_serial != NULL)
+     *out_serial = 0;
+@@ -1625,33 +2338,41 @@ g_dbus_connection_send_message_unlocked (GDBusConnection   *connection,
+   if (!check_unclosed (connection,
+                        (flags & SEND_MESSAGE_FLAGS_INITIALIZING) ? MAY_BE_UNINITIALIZED : 0,
+                        error))
+-    return FALSE;
++    goto out;
+-  blob = g_dbus_message_to_blob (message,
+-                                 &blob_size,
+-                                 connection->capabilities,
+-                                 error);
+-  if (blob == NULL)
+-    return FALSE;
++  if (!(flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL))
++    g_dbus_message_set_serial (message, ++connection->last_serial);
+-  if (flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL)
+-    serial_to_use = g_dbus_message_get_serial (message);
+-  else
+-    serial_to_use = ++connection->last_serial; /* TODO: handle overflow */
++  serial_to_use = g_dbus_message_get_serial (message);
+-  switch (blob[0])
++  /*
++   * serializes message to a blob
++   */
++  if (connection->worker)
+     {
+-    case 'l':
+-      ((guint32 *) blob)[2] = GUINT32_TO_LE (serial_to_use);
+-      break;
+-    case 'B':
+-      ((guint32 *) blob)[2] = GUINT32_TO_BE (serial_to_use);
+-      break;
+-    default:
+-      g_assert_not_reached ();
+-      break;
++      blob = g_dbus_message_to_blob (message,
++                                     &blob_size,
++                                     connection->capabilities,
++                                     error);
++      if (blob == NULL)
++        goto out;
++
++      switch (blob[0])
++        {
++        case 'l':
++          ((guint32 *) blob)[2] = GUINT32_TO_LE (serial_to_use);
++          break;
++        case 'B':
++          ((guint32 *) blob)[2] = GUINT32_TO_BE (serial_to_use);
++          break;
++        default:
++          g_assert_not_reached ();
++          break;
++        }
+     }
++  g_dbus_message_lock (message);
++
+ #if 0
+   g_printerr ("Writing message of %" G_GSIZE_FORMAT " bytes (serial %d) on %p:\n",
+               blob_size, serial_to_use, connection);
+@@ -1674,17 +2395,32 @@ g_dbus_connection_send_message_unlocked (GDBusConnection   *connection,
+                         g_thread_self (),
+                         GUINT_TO_POINTER (serial_to_use));
+-  if (!(flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL))
+-    g_dbus_message_set_serial (message, serial_to_use);
++  ret = TRUE;
+-  g_dbus_message_lock (message);
++  if (connection->worker)
++    {
++      _g_dbus_worker_send_message (connection->worker,
++                                   message,
++                                   (gchar*) blob,
++                                   blob_size);
++    }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  else if (connection->kdbus_worker)
++    {
++      ret = _g_kdbus_worker_send_message (connection->kdbus_worker, message, timeout_msec, error);
++    }
++#endif
++#endif
++  else
++    g_assert_not_reached ();
+-  _g_dbus_worker_send_message (connection->worker,
+-                               message,
+-                               (gchar*) blob, /* transfer ownership */
+-                               blob_size);
++  blob = NULL; /* since _g_dbus_worker_send_message() steals the blob */
+-  return TRUE;
++ out:
++  g_free (blob);
++
++  return ret;
+ }
+ /**
+@@ -1738,7 +2474,7 @@ g_dbus_connection_send_message (GDBusConnection        *connection,
+   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+   CONNECTION_LOCK (connection);
+-  ret = g_dbus_connection_send_message_unlocked (connection, message, flags, (guint32 *) out_serial, error);
++  ret = g_dbus_connection_send_message_unlocked (connection, message, flags, (guint32 *) out_serial, error, -1);
+   CONNECTION_UNLOCK (connection);
+   return ret;
+ }
+@@ -1921,9 +2657,6 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection     *connect
+   if (out_serial == NULL)
+     out_serial = &serial;
+-  if (timeout_msec == -1)
+-    timeout_msec = 25 * 1000;
+-
+   data = g_slice_new0 (SendMessageData);
+   task = g_task_new (connection, cancellable, callback, user_data);
+   g_task_set_source_tag (task,
+@@ -1936,7 +2669,7 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection     *connect
+       return;
+     }
+-  if (!g_dbus_connection_send_message_unlocked (connection, message, flags, out_serial, &error))
++  if (!g_dbus_connection_send_message_unlocked (connection, message, flags, out_serial, &error, timeout_msec))
+     {
+       g_task_return_error (task, error);
+       g_object_unref (task);
+@@ -1952,9 +2685,16 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection     *connect
+                                                             g_object_unref);
+     }
+-  if (timeout_msec != G_MAXINT)
++  if (timeout_msec != G_MAXINT
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++        /* Userspace timeouts unnecessary on unix/kdbus - kdbus handles timeouts. */
++        && !connection->kdbus_worker
++#endif
++#endif
++     )
+     {
+-      data->timeout_source = g_timeout_source_new (timeout_msec);
++      data->timeout_source = g_timeout_source_new (timeout_msec == -1 ? DBUS_DEFAULT_TIMEOUT_MSEC : timeout_msec);
+       g_source_set_static_name (data->timeout_source, "[gio] send_message_with_reply_unlocked");
+       g_task_attach_source (task, data->timeout_source,
+                             (GSourceFunc) send_message_with_reply_timeout_cb);
+@@ -1996,7 +2736,7 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection     *connect
+  * the operation fails with %G_IO_ERROR_INVALID_ARGUMENT.
+  *
+  * This is an asynchronous method. When the operation is finished, @callback
+- * will be invoked in the 
++ * will be invoked in the
+  * [thread-default main context][g-main-context-push-thread-default]
+  * of the thread you are calling this method from. You can then call
+  * g_dbus_connection_send_message_with_reply_finish() to get the result of the operation.
+@@ -2158,32 +2898,102 @@ g_dbus_connection_send_message_with_reply_sync (GDBusConnection        *connecti
+   g_return_val_if_fail (timeout_msec >= 0 || timeout_msec == -1, NULL);
+   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+-  data.res = NULL;
+-  data.context = g_main_context_new ();
+-  data.loop = g_main_loop_new (data.context, FALSE);
++  if (connection->worker)
++    {
++      data.res = NULL;
++      data.context = g_main_context_new ();
++      data.loop = g_main_loop_new (data.context, FALSE);
+-  g_main_context_push_thread_default (data.context);
++      g_main_context_push_thread_default (data.context);
+-  g_dbus_connection_send_message_with_reply (connection,
+-                                             message,
+-                                             flags,
+-                                             timeout_msec,
+-                                             out_serial,
+-                                             cancellable,
+-                                             (GAsyncReadyCallback) send_message_with_reply_sync_cb,
+-                                             &data);
+-  g_main_loop_run (data.loop);
+-  reply = g_dbus_connection_send_message_with_reply_finish (connection,
+-                                                            data.res,
+-                                                            error);
++      g_dbus_connection_send_message_with_reply (connection,
++                                                 message,
++                                                 flags,
++                                                 timeout_msec,
++                                                 out_serial,
++                                                 cancellable,
++                                                 (GAsyncReadyCallback) send_message_with_reply_sync_cb,
++                                                 &data);
++      g_main_loop_run (data.loop);
++      reply = g_dbus_connection_send_message_with_reply_finish (connection,
++                                                                data.res,
++                                                                error);
++
++      g_main_context_pop_thread_default (data.context);
++
++      g_main_context_unref (data.context);
++      g_main_loop_unref (data.loop);
++      if (data.res)
++        g_object_unref (data.res);
++    }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  else if (connection->kdbus_worker)
++    {
++      /* kdbus supports blocking synchronous calls, so let's use them instead of mainloops */
++
++      volatile guint32 serial;
++      guint32 serial_to_use;
++
++      reply = NULL;
++
++      CONNECTION_LOCK (connection);
++
++      if (out_serial == NULL)
++        out_serial = &serial;
++      else
++        *out_serial = 0;
++
++      /*
++       * Check that we never actually send a message if the GCancellable
++       * is already cancelled
++       */
++      if (g_cancellable_is_cancelled (cancellable))
++        {
++          g_set_error_literal (error,
++                               G_IO_ERROR,
++                               G_IO_ERROR_CANCELLED,
++                               _("Operation was cancelled"));
++          CONNECTION_UNLOCK (connection);
++          goto out;
++        }
++
++      if (!check_unclosed (connection,
++                           (flags & SEND_MESSAGE_FLAGS_INITIALIZING) ? MAY_BE_UNINITIALIZED : 0,
++                           error))
++        {
++          CONNECTION_UNLOCK (connection);
++          goto out;
++        }
++
++      if (!(flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL))
++        g_dbus_message_set_serial (message, ++connection->last_serial);
++
++      serial_to_use = g_dbus_message_get_serial (message);
+-  g_main_context_pop_thread_default (data.context);
++      g_dbus_message_lock (message);
++
++      if (out_serial != NULL)
++        *out_serial = serial_to_use;
++
++      CONNECTION_UNLOCK (connection);
+-  g_main_context_unref (data.context);
+-  g_main_loop_unref (data.loop);
+-  if (data.res)
+-    g_object_unref (data.res);
++      /* Reference is still kept for the connection, so there is no worry
++       * that it will be freed. Same for connection->kdbus_worker - by reference
++       * from the connection. Inside _g_kdbus_worker_send_message_sync only
++       * initialized-once, read-only fields from kdbus_worker are used, except
++       * 'matches', which now has got its own mutex.
++       */
++      _g_kdbus_worker_send_message_sync (connection->kdbus_worker, message,
++                                         &reply, timeout_msec, cancellable, error);
++
++    }
++#endif /* G_OS_UNIX */
++#endif /* G_OS_UNIX */
++  else
++    g_assert_not_reached ();
++out:
+   return reply;
+ }
+@@ -2252,8 +3062,7 @@ free_filter_list (FilterData **filters)
+ /* Called in GDBusWorker's thread - we must not block - with no lock held */
+ static void
+-on_worker_message_received (GDBusWorker  *worker,
+-                            GDBusMessage *message,
++on_worker_message_received (GDBusMessage *message,
+                             gpointer      user_data)
+ {
+   GDBusConnection *connection;
+@@ -2272,7 +3081,25 @@ on_worker_message_received (GDBusWorker  *worker,
+   g_object_ref (connection);
+   G_UNLOCK (message_bus_lock);
+-  //g_debug ("in on_worker_message_received");
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  if (_g_dbus_connection_is_kdbus (connection))
++    {
++      if (G_UNLIKELY (_g_dbus_debug_message ()))
++        {
++          gchar *s;
++          _g_dbus_debug_print_lock ();
++          g_print ("========================================================================\n"
++                   "GDBus-debug:Message:\n"
++                   "  <<<< RECEIVED D-Bus message\n");
++          s = g_dbus_message_print (message, 2);
++          g_print ("%s", s);
++          g_free (s);
++          _g_dbus_debug_print_unlock ();
++        }
++    }
++#endif
++#endif
+   g_object_ref (message);
+   g_dbus_message_lock (message);
+@@ -2350,8 +3177,7 @@ on_worker_message_received (GDBusWorker  *worker,
+ /* Called in GDBusWorker's thread, lock is not held */
+ static GDBusMessage *
+-on_worker_message_about_to_be_sent (GDBusWorker  *worker,
+-                                    GDBusMessage *message,
++on_worker_message_about_to_be_sent (GDBusMessage *message,
+                                     gpointer      user_data)
+ {
+   GDBusConnection *connection;
+@@ -2424,10 +3250,9 @@ cancel_method_on_close (gpointer key, gpointer value, gpointer user_data)
+ /* Called in GDBusWorker's thread - we must not block - without lock held */
+ static void
+-on_worker_closed (GDBusWorker *worker,
+-                  gboolean     remote_peer_vanished,
+-                  GError      *error,
+-                  gpointer     user_data)
++on_worker_closed (gboolean  remote_peer_vanished,
++                  GError   *error,
++                  gpointer  user_data)
+ {
+   GDBusConnection *connection;
+   gboolean alive;
+@@ -2489,6 +3314,7 @@ initable_init (GInitable     *initable,
+                GError       **error)
+ {
+   GDBusConnection *connection = G_DBUS_CONNECTION (initable);
++  gboolean initially_frozen;
+   gboolean ret;
+   /* This method needs to be idempotent to work with the singleton
+@@ -2526,6 +3352,8 @@ initable_init (GInitable     *initable,
+    */
+   if (connection->address != NULL)
+     {
++      GObject *ret;
++
+       g_assert (connection->stream == NULL);
+       if ((connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER) ||
+@@ -2539,12 +3367,22 @@ initable_init (GInitable     *initable,
+           goto out;
+         }
+-      connection->stream = g_dbus_address_get_stream_sync (connection->address,
+-                                                           NULL, /* TODO: out_guid */
+-                                                           cancellable,
+-                                                           &connection->initialization_error);
+-      if (connection->stream == NULL)
++      ret = g_dbus_address_get_stream_internal (connection->address, TRUE,
++                                                NULL, /* TODO: out_guid */
++                                                cancellable, &connection->initialization_error);
++      if (ret == NULL)
+         goto out;
++
++      if (G_IS_IO_STREAM (ret))
++        connection->stream = G_IO_STREAM (ret);
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++      else if (G_IS_KDBUS_WORKER (ret))
++        connection->kdbus_worker = G_KDBUS_WORKER (ret);
++#endif
++#endif
++      else
++        g_assert_not_reached ();
+     }
+   else if (connection->stream != NULL)
+     {
+@@ -2555,6 +3393,22 @@ initable_init (GInitable     *initable,
+       g_assert_not_reached ();
+     }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  /* Skip authentication process for kdbus transport */
++  if (connection->kdbus_worker)
++    {
++      if (!_g_kdbus_can_connect (connection->kdbus_worker,
++                                 &connection->initialization_error))
++        goto out;
++
++      /* kdbus connection always supports exchanging UNIX file descriptors with the remote peer */
++      connection->capabilities |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING;
++      goto authenticated;
++    }
++#endif
++#endif
++
+   /* Authenticate the connection */
+   if (connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER)
+     {
+@@ -2595,6 +3449,8 @@ initable_init (GInitable     *initable,
+       connection->authentication_observer = NULL;
+     }
++authenticated:
++
+   //g_output_stream_flush (G_SOCKET_CONNECTION (connection->stream)
+   //g_debug ("haz unix fd passing powers: %d", connection->capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING);
+@@ -2615,13 +3471,34 @@ initable_init (GInitable     *initable,
+   g_hash_table_add (alive_connections, connection);
+   G_UNLOCK (message_bus_lock);
+-  connection->worker = _g_dbus_worker_new (connection->stream,
+-                                           connection->capabilities,
+-                                           ((connection->flags & G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING) != 0),
+-                                           on_worker_message_received,
+-                                           on_worker_message_about_to_be_sent,
+-                                           on_worker_closed,
+-                                           connection);
++  initially_frozen = (connection->flags & G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING) != 0;
++
++  if (0)
++    {
++    }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  else if (connection->kdbus_worker)
++    {
++      _g_kdbus_worker_associate (connection->kdbus_worker,
++                                 connection->capabilities,
++                                 on_worker_message_received,
++                                 on_worker_message_about_to_be_sent,
++                                 on_worker_closed,
++                                 connection);
++    }
++#endif
++#endif
++  else
++    {
++      connection->worker = _g_dbus_worker_new (connection->stream,
++                                               connection->capabilities,
++                                               initially_frozen,
++                                               on_worker_message_received,
++                                               on_worker_message_about_to_be_sent,
++                                               on_worker_closed,
++                                               connection);
++    }
+   /* if a bus connection, call org.freedesktop.DBus.Hello - this is how we're getting a name */
+   if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION)
+@@ -2638,25 +3515,50 @@ initable_init (GInitable     *initable,
+           goto out;
+         }
+-      hello_result = g_dbus_connection_call_sync (connection,
+-                                                  "org.freedesktop.DBus", /* name */
+-                                                  "/org/freedesktop/DBus", /* path */
+-                                                  "org.freedesktop.DBus", /* interface */
+-                                                  "Hello",
+-                                                  NULL, /* parameters */
+-                                                  G_VARIANT_TYPE ("(s)"),
+-                                                  CALL_FLAGS_INITIALIZING,
+-                                                  -1,
+-                                                  NULL, /* TODO: cancellable */
+-                                                  &connection->initialization_error);
+-      if (hello_result == NULL)
+-        goto out;
++      if (connection->worker)
++        {
++          hello_result = g_dbus_connection_call_sync (connection,
++                                                      "org.freedesktop.DBus", /* name */
++                                                      "/org/freedesktop/DBus", /* path */
++                                                      "org.freedesktop.DBus", /* interface */
++                                                      "Hello",
++                                                      NULL, /* parameters */
++                                                      G_VARIANT_TYPE ("(s)"),
++                                                      CALL_FLAGS_INITIALIZING,
++                                                      -1,
++                                                      NULL, /* TODO: cancellable */
++                                                      &connection->initialization_error);
++          if (hello_result == NULL)
++            goto out;
++
++          g_variant_get (hello_result, "(s)", &connection->bus_unique_name);
++          g_variant_unref (hello_result);
++        }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++      else if (connection->kdbus_worker)
++        {
++          const gchar *unique_name;
++
++          unique_name = _g_kdbus_Hello (connection->kdbus_worker, &connection->initialization_error);
++          if (unique_name == NULL)
++            goto out;
+-      g_variant_get (hello_result, "(s)", &connection->bus_unique_name);
+-      g_variant_unref (hello_result);
+-      //g_debug ("unique name is '%s'", connection->bus_unique_name);
++          connection->bus_unique_name = g_strdup (unique_name);
++        }
++#endif
++#endif
++      else
++        g_assert_not_reached ();
+     }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++  if (connection->kdbus_worker && !initially_frozen)
++    _g_kdbus_worker_unfreeze (connection->kdbus_worker);
++#endif
++#endif
++
+   ret = TRUE;
+  out:
+   if (!ret)
+@@ -3376,7 +4278,8 @@ add_match_rule (GDBusConnection *connection,
+                                                 message,
+                                                 G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+                                                 NULL,
+-                                                &error))
++                                                &error,
++                                                -1))
+     {
+       g_critical ("Error while sending AddMatch() message: %s", error->message);
+       g_error_free (error);
+@@ -3408,7 +4311,8 @@ remove_match_rule (GDBusConnection *connection,
+                                                 message,
+                                                 G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+                                                 NULL,
+-                                                &error))
++                                                &error,
++                                                -1))
+     {
+       /* If we could get G_IO_ERROR_CLOSED here, it wouldn't be reasonable to
+        * critical; but we're holding the lock, and our caller checked whether
+@@ -3455,7 +4359,7 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data)
+  *     subscription is removed or %NULL
+  *
+  * Subscribes to signals on @connection and invokes @callback whenever
+- * the signal is received. Note that @callback will be invoked in the 
++ * the signal is received. Note that @callback will be invoked in the
+  * [thread-default main context][g-main-context-push-thread-default]
+  * of the thread you are calling this method from.
+  *
+@@ -3603,8 +4507,60 @@ g_dbus_connection_signal_subscribe (GDBusConnection     *connection,
+    */
+   if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION)
+     {
+-      if (!is_signal_data_for_name_lost_or_acquired (signal_data))
+-        add_match_rule (connection, signal_data->rule);
++      if (connection->worker)
++        {
++          if (!is_signal_data_for_name_lost_or_acquired (signal_data))
++            add_match_rule (connection, signal_data->rule);
++        }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++      else if (connection->kdbus_worker)
++        {
++          gboolean special_rule = FALSE;
++          /* rule for special message */
++          if (!signal_data->sender_unique_name[0] || g_strcmp0 (signal_data->sender_unique_name, "org.freedesktop.DBus") == 0)
++            {
++              if (signal_data->sender_unique_name[0]) /* So, this is org.freedesktop.DBus */
++                special_rule = TRUE;
++              if (!signal_data->object_path || g_strcmp0 (signal_data->object_path, "/org/freedesktop/DBus") == 0)
++                {
++                  if (!signal_data->interface_name || g_strcmp0 (signal_data->interface_name, "org.freedesktop.DBus") == 0)
++                    {
++                      /* By https://dbus.freedesktop.org/doc/dbus-specification.html, org.freedesktop.DBus
++                       * has three signal types: NameAcquired, NameLost, NameOwnerChanged (all covered below).
++                       *
++                       * 1. if sender is NULL and other parameters are matched with special rule, add BOTH special and standard,
++                       *   - (NULL, NULL or DBUS, NULL or DBUS, NULL or Special Name)
++                       *
++                       *    if other parameters are NOT matched with special rule, add standard rule
++                       *   - (NULL, X, X, "NotSpecial")
++                       *
++                       * 2. if sender is 'org.freedesktop.DBus' and other parameters are matched with special rule, add special rule
++                       *   - (org.freedesktop.DBus, NULL or DBUS, NULL or DBUS, NULL or Special Name)
++                       *
++                       *    if other parameters are NOT matched with special rule, then ignore
++                       *   - (org.freedesktop.DBus, X, X, "NotSpecial")
++                       *
++                       * for every other cases, add standard rule,
++                       */
++
++                      if (g_strcmp0 (signal_data->member, "NameAcquired") == 0)
++                        _g_kdbus_subscribe_name_acquired (connection->kdbus_worker, signal_data->rule, arg0, NULL);
++                      else if (g_strcmp0 (signal_data->member, "NameLost") == 0)
++                        _g_kdbus_subscribe_name_lost (connection->kdbus_worker, signal_data->rule, arg0, NULL);
++                      else if (!signal_data->member || g_strcmp0 (signal_data->member, "NameOwnerChanged") == 0)
++                        _g_kdbus_subscribe_name_owner_changed (connection->kdbus_worker, signal_data->rule, arg0, NULL);
++                    }
++                }
++            }
++            /* standard rule */
++            if (!special_rule)
++              _g_kdbus_AddMatch (connection->kdbus_worker, signal_data->rule, NULL);
++        }
++#endif
++#endif
++      else
++        g_assert_not_reached ();
+     }
+   signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array,
+@@ -3683,7 +4639,6 @@ unsubscribe_id_internal (GDBusConnection *connection,
+           /* remove the match rule from the bus unless NameLost or NameAcquired (see subscribe()) */
+           if ((connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) &&
+-              !is_signal_data_for_name_lost_or_acquired (signal_data) &&
+               !g_dbus_connection_is_closed (connection) &&
+               !connection->finalizing)
+             {
+@@ -3693,7 +4648,21 @@ unsubscribe_id_internal (GDBusConnection *connection,
+                * so on_worker_closed() can't happen between the check we just
+                * did, and releasing the lock later.
+                */
+-              remove_match_rule (connection, signal_data->rule);
++              if (connection->worker)
++                {
++                  if (!is_signal_data_for_name_lost_or_acquired (signal_data))
++                    remove_match_rule (connection, signal_data->rule);
++                }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++              else if (connection->kdbus_worker)
++                {
++                  _g_kdbus_RemoveMatch (connection->kdbus_worker, signal_data->rule, NULL);
++                }
++#endif
++#endif
++              else
++                g_assert_not_reached ();
+             }
+           signal_data_free (signal_data);
+@@ -3777,7 +4746,7 @@ emit_signal_instance_in_idle_cb (gpointer data)
+     }
+   else
+     {
+-      g_variant_ref_sink (parameters);
++      g_variant_ref (parameters);
+     }
+ #if 0
+@@ -4421,7 +5390,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection            *connect
+                                                "org.freedesktop.DBus.Error.InvalidArgs",
+                                                _("No such property “%s”"),
+                                                property_name);
+-      g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++      g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+       g_object_unref (reply);
+       handled = TRUE;
+       goto out;
+@@ -4433,7 +5402,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection            *connect
+                                                "org.freedesktop.DBus.Error.InvalidArgs",
+                                                _("Property “%s” is not readable"),
+                                                property_name);
+-      g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++      g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+       g_object_unref (reply);
+       handled = TRUE;
+       goto out;
+@@ -4444,7 +5413,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection            *connect
+                                                "org.freedesktop.DBus.Error.InvalidArgs",
+                                                _("Property “%s” is not writable"),
+                                                property_name);
+-      g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++      g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+       g_object_unref (reply);
+       handled = TRUE;
+       goto out;
+@@ -4465,7 +5434,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection            *connect
+                                                    _("Error setting property “%s”: Expected type “%s” but got “%s”"),
+                                                    property_name, property_info->signature,
+                                                    g_variant_get_type_string (value));
+-          g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++          g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+           g_variant_unref (value);
+           g_object_unref (reply);
+           handled = TRUE;
+@@ -4569,7 +5538,7 @@ handle_getset_property (GDBusConnection *connection,
+                                                "org.freedesktop.DBus.Error.InvalidArgs",
+                                                _("No such interface “%s”"),
+                                                interface_name);
+-      g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++      g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+       g_object_unref (reply);
+       handled = TRUE;
+       goto out;
+@@ -4784,7 +5753,7 @@ handle_get_all_properties (GDBusConnection *connection,
+                                                "org.freedesktop.DBus.Error.InvalidArgs",
+                                                _("No such interface “%s”"),
+                                                interface_name);
+-      g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++      g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+       g_object_unref (reply);
+       handled = TRUE;
+       goto out;
+@@ -4968,7 +5937,7 @@ handle_introspect (GDBusConnection *connection,
+   reply = g_dbus_message_new_method_reply (message);
+   g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str));
+-  g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++  g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+   g_object_unref (reply);
+   g_string_free (s, TRUE);
+@@ -5101,7 +6070,7 @@ validate_and_maybe_schedule_method_call (GDBusConnection            *connection,
+                                                "org.freedesktop.DBus.Error.UnknownMethod",
+                                                _("No such method “%s”"),
+                                                g_dbus_message_get_member (message));
+-      g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++      g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+       g_object_unref (reply);
+       handled = TRUE;
+       goto out;
+@@ -5133,7 +6102,7 @@ validate_and_maybe_schedule_method_call (GDBusConnection            *connection,
+                                                _("Type of message, “%s”, does not match expected type “%s”"),
+                                                g_variant_get_type_string (parameters),
+                                                type_string);
+-      g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++      g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+       g_variant_type_free (in_type);
+       g_variant_unref (parameters);
+       g_object_unref (reply);
+@@ -5250,7 +6219,7 @@ obj_message_func (GDBusConnection *connection,
+  * D-Bus interface that is described in @interface_info.
+  *
+  * Calls to functions in @vtable (and @user_data_free_func) will happen
+- * in the 
++ * in the
+  * [thread-default main context][g-main-context-push-thread-default]
+  * of the thread you are calling this method from.
+  *
+@@ -5818,6 +6787,16 @@ decode_method_reply (GDBusMessage        *reply,
+     case G_DBUS_MESSAGE_TYPE_ERROR:
+       g_dbus_message_to_gerror (reply, error);
++      if (error == NULL || *error == NULL)
++        break;
++      if ((*error)->code == G_DBUS_ERROR_NO_REPLY)
++        {
++          g_clear_error(error);
++          g_set_error (error,
++                       G_IO_ERROR,
++                       G_IO_ERROR_TIMED_OUT,
++                       _("Timeout was reached"));
++        }
+       break;
+     default:
+@@ -7076,7 +8055,7 @@ handle_generic_ping_unlocked (GDBusConnection *connection,
+ {
+   GDBusMessage *reply;
+   reply = g_dbus_message_new_method_reply (message);
+-  g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++  g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+   g_object_unref (reply);
+ }
+@@ -7109,7 +8088,7 @@ handle_generic_get_machine_id_unlocked (GDBusConnection *connection,
+       reply = g_dbus_message_new_method_reply (message);
+       g_dbus_message_set_body (reply, g_variant_new ("(s)", connection->machine_id));
+     }
+-  g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++  g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+   g_object_unref (reply);
+ }
+@@ -7136,7 +8115,7 @@ handle_generic_introspect_unlocked (GDBusConnection *connection,
+   reply = g_dbus_message_new_method_reply (message);
+   g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str));
+-  g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++  g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+   g_object_unref (reply);
+   g_string_free (s, TRUE);
+ }
+@@ -7288,7 +8267,7 @@ distribute_method_call (GDBusConnection *connection,
+                                            path);
+     }
+-  g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++  g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+   g_object_unref (reply);
+  out:
+@@ -7602,3 +8581,10 @@ g_bus_get_finish (GAsyncResult  *res,
+ }
+ /* ---------------------------------------------------------------------------------------------------- */
++#ifdef _TIZEN_DBUS_TOUCH
++static void __attribute__((constructor)) glib_type_dbus_connection_gc( void )
++{
++      GObject* temp = g_type_class_ref(G_TYPE_DBUS_CONNECTION);
++      g_type_class_unref(temp);
++}
++#endif
+diff --git a/gio/gdbusconnection.h b/gio/gdbusconnection.h
+index feda7c0..b4452b3 100644
+--- a/gio/gdbusconnection.h
++++ b/gio/gdbusconnection.h
+@@ -32,6 +32,8 @@
+ G_BEGIN_DECLS
++#define DBUS_DEFAULT_TIMEOUT_MSEC   25000U
++
+ #define G_TYPE_DBUS_CONNECTION         (g_dbus_connection_get_type ())
+ #define G_DBUS_CONNECTION(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_CONNECTION, GDBusConnection))
+ #define G_IS_DBUS_CONNECTION(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_CONNECTION))
+@@ -94,6 +96,46 @@ GDBusConnection *g_dbus_connection_new_for_address_sync       (const gchar
+ /* ---------------------------------------------------------------------------------------------------- */
++gboolean                     _g_dbus_connection_is_kdbus         (GDBusConnection     *connection) TIZEN_PUBLIC_DEPRECATED_API;
++
++GBusRequestNameReplyFlags    g_dbus_request_name                 (GDBusConnection     *connection,
++                                                                  const gchar         *name,
++                                                                  GBusNameOwnerFlags   flags,
++                                                                  GError             **error) TIZEN_PUBLIC_DEPRECATED_API;
++GBusReleaseNameReplyFlags    g_dbus_release_name                 (GDBusConnection     *connection,
++                                                                  const gchar         *name,
++                                                                  GError             **error) TIZEN_PUBLIC_DEPRECATED_API;
++gboolean                     g_dbus_add_match                    (GDBusConnection     *connection,
++                                                                  const gchar         *match_rule,
++                                                                  GError             **error) TIZEN_PUBLIC_DEPRECATED_API;
++gboolean                     g_dbus_remove_match                 (GDBusConnection     *connection,
++                                                                  const gchar         *match_rule,
++                                                                  GError             **error) TIZEN_PUBLIC_DEPRECATED_API;
++gchar                       *g_dbus_get_bus_id                   (GDBusConnection     *connection,
++                                                                  GError             **error) TIZEN_PUBLIC_DEPRECATED_API;
++gchar                      **g_dbus_get_list_names               (GDBusConnection     *connection,
++                                                                  GError             **error) TIZEN_PUBLIC_DEPRECATED_API;
++gchar                      **g_dbus_get_list_activatable_names   (GDBusConnection     *connection,
++                                                                  GError             **error) TIZEN_PUBLIC_DEPRECATED_API;
++gchar                      **g_dbus_get_list_queued_owners       (GDBusConnection     *connection,
++                                                                  const gchar         *name,
++                                                                  GError             **error) TIZEN_PUBLIC_DEPRECATED_API;
++gchar                       *g_dbus_get_name_owner               (GDBusConnection     *connection,
++                                                                  const gchar         *name,
++                                                                  GError             **error) TIZEN_PUBLIC_DEPRECATED_API;
++pid_t                        g_dbus_get_connection_pid           (GDBusConnection     *connection,
++                                                                  const gchar         *name,
++                                                                  GError             **error) TIZEN_PUBLIC_DEPRECATED_API;
++uid_t                        g_dbus_get_connection_uid           (GDBusConnection     *connection,
++                                                                  const gchar         *name,
++                                                                  GError             **error) TIZEN_PUBLIC_DEPRECATED_API;
++GBusStartServiceReplyFlags   g_dbus_start_service_by_name        (GDBusConnection     *connection,
++                                                                  const gchar         *name,
++                                                                  guint32              flags,
++                                                                  GError             **error) TIZEN_PUBLIC_DEPRECATED_API;
++
++/* ---------------------------------------------------------------------------------------------------- */
++
+ GIO_AVAILABLE_IN_ALL
+ void             g_dbus_connection_start_message_processing   (GDBusConnection    *connection) TIZEN_PUBLIC_DEPRECATED_API;
+ GIO_AVAILABLE_IN_ALL
+diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c
+index adddb31..3894d19 100644
+--- a/gio/gdbusmessage.c
++++ b/gio/gdbusmessage.c
+@@ -3868,3 +3868,10 @@ g_dbus_message_copy (GDBusMessage  *message,
+ #endif
+   return ret;
+ }
++
++void
++g_dbus_message_init_header_iter (GDBusMessage   *message,
++                                 GHashTableIter *iter)
++{
++  g_hash_table_iter_init (iter, message->headers);
++}
+diff --git a/gio/gdbusmethodinvocation.c b/gio/gdbusmethodinvocation.c
+index e5a9166..cd210c2 100644
+--- a/gio/gdbusmethodinvocation.c
++++ b/gio/gdbusmethodinvocation.c
+@@ -310,6 +310,44 @@ g_dbus_method_invocation_get_parameters (GDBusMethodInvocation *invocation)
+   return invocation->parameters;
+ }
++/**
++ * g_dbus_method_invocation_peek_unix_fd:
++ * @invocation: A #GDBusMethodInvocation.
++ * @index_: the index
++ *
++ * Gets the fd associated with @index in the method invocation.
++ *
++ * If there is no file descriptor at the given index, -1 is returned.
++ *
++ * The returned file descriptor is owned by the message and must not be
++ * closed by the caller.  Use dup() if you want your own copy.
++ *
++ * Returns: the file descriptor, or -1
++ */
++#ifdef G_OS_UNIX
++gint
++g_dbus_method_invocation_peek_unix_fd (GDBusMethodInvocation *invocation,
++                                       guint                  index_)
++{
++  GUnixFDList *fd_list;
++
++  fd_list = g_dbus_message_get_unix_fd_list (invocation->message);
++
++  if (fd_list)
++    {
++      const gint *fds;
++      gint n_fds;
++
++      fds = g_unix_fd_list_peek_fds (fd_list, &n_fds);
++
++      if (index_ < (guint) n_fds)
++        return fds[index_];
++    }
++
++  return -1;
++}
++#endif
++
+ /**
+  * g_dbus_method_invocation_get_user_data: (skip)
+  * @invocation: A #GDBusMethodInvocation.
+diff --git a/gio/gdbusmethodinvocation.h b/gio/gdbusmethodinvocation.h
+index eecef03..bbfa190 100644
+--- a/gio/gdbusmethodinvocation.h
++++ b/gio/gdbusmethodinvocation.h
+@@ -91,6 +91,11 @@ GIO_AVAILABLE_IN_ALL
+ GDBusMessage          *g_dbus_method_invocation_get_message          (GDBusMethodInvocation *invocation) TIZEN_PUBLIC_DEPRECATED_API;
+ GIO_AVAILABLE_IN_ALL
+ GVariant              *g_dbus_method_invocation_get_parameters       (GDBusMethodInvocation *invocation) TIZEN_PUBLIC_DEPRECATED_API;
++#ifdef G_OS_UNIX
++GIO_AVAILABLE_IN_2_44
++gint                   g_dbus_method_invocation_peek_unix_fd         (GDBusMethodInvocation *invocation,
++                                                                      guint                  index_) TIZEN_PUBLIC_DEPRECATED_API;
++#endif
+ GIO_AVAILABLE_IN_ALL
+ gpointer               g_dbus_method_invocation_get_user_data        (GDBusMethodInvocation *invocation) TIZEN_PUBLIC_DEPRECATED_API;
+diff --git a/gio/gdbusnameowning.c b/gio/gdbusnameowning.c
+index 289ea03..5090d26 100644
+--- a/gio/gdbusnameowning.c
++++ b/gio/gdbusnameowning.c
+@@ -926,6 +926,19 @@ g_bus_unown_name (guint owner_id)
+   /* do callback without holding lock */
+   if (client != NULL)
+     {
++      /* Kdbus sends NameLost after replying to ReleaseName,
++       * contrary to dbus-daemon. Let's disable subscriptions now,
++       * as we're no longer interested in them, anyway. It prevents
++       * a race condition with receiving NameLost after
++       * an unowned name gets owned again.
++       */
++      if (client->name_lost_subscription_id > 0)
++        g_dbus_connection_signal_unsubscribe (client->connection, client->name_lost_subscription_id);
++      if (client->name_acquired_subscription_id > 0)
++        g_dbus_connection_signal_unsubscribe (client->connection, client->name_acquired_subscription_id);
++      client->name_acquired_subscription_id = 0;
++      client->name_lost_subscription_id = 0;
++
+       /* Release the name if needed */
+       if (client->needs_release &&
+           client->connection != NULL &&
+@@ -975,13 +988,7 @@ g_bus_unown_name (guint owner_id)
+       if (client->disconnected_signal_handler_id > 0)
+         g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id);
+-      if (client->name_acquired_subscription_id > 0)
+-        g_dbus_connection_signal_unsubscribe (client->connection, client->name_acquired_subscription_id);
+-      if (client->name_lost_subscription_id > 0)
+-        g_dbus_connection_signal_unsubscribe (client->connection, client->name_lost_subscription_id);
+       client->disconnected_signal_handler_id = 0;
+-      client->name_acquired_subscription_id = 0;
+-      client->name_lost_subscription_id = 0;
+       if (client->connection != NULL)
+         {
+           g_object_unref (client->connection);
+diff --git a/gio/gdbusnamewatching.c b/gio/gdbusnamewatching.c
+old mode 100644
+new mode 100755
+index c834fe1..fc230a8
+--- a/gio/gdbusnamewatching.c
++++ b/gio/gdbusnamewatching.c
+@@ -353,9 +353,6 @@ on_name_owner_changed (GDBusConnection *connection,
+   if (client == NULL)
+     return;
+-  if (!client->initialized)
+-    goto out;
+-
+   if (g_strcmp0 (object_path, "/org/freedesktop/DBus") != 0 ||
+       g_strcmp0 (interface_name, "org.freedesktop.DBus") != 0 ||
+       g_strcmp0 (sender_name, "org.freedesktop.DBus") != 0)
+@@ -371,7 +368,7 @@ on_name_owner_changed (GDBusConnection *connection,
+   if (g_strcmp0 (name, client->name) != 0)
+     goto out;
+-  if ((old_owner != NULL && strlen (old_owner) > 0) && client->name_owner != NULL)
++  if (old_owner != NULL && strlen (old_owner) > 0)
+     {
+       g_free (client->name_owner);
+       client->name_owner = NULL;
+@@ -380,12 +377,18 @@ on_name_owner_changed (GDBusConnection *connection,
+   if (new_owner != NULL && strlen (new_owner) > 0)
+     {
+-      g_warn_if_fail (client->name_owner == NULL);
+       g_free (client->name_owner);
+       client->name_owner = g_strdup (new_owner);
+       call_appeared_handler (client);
+     }
++  /* initialized set to TRUE means that signal was delivered and processed.
++   * Now, if we receive a reply to GetNameOwner call, we may just ignore it as it carries the same
++   * information as the current signal, or if something changed in the meantime we will
++   * get next signal very soon.
++   */
++  client->initialized = TRUE;
++
+  out:
+   client_unref (client);
+ }
+@@ -407,6 +410,13 @@ get_name_owner_cb (GObject      *source_object,
+   result = g_dbus_connection_call_finish (client->connection,
+                                           res,
+                                           NULL);
++  /* In case we already received NameOwnerChanged signal, we don't need to
++   * process GetNameOwner answer, because all the information we needed was already
++   * delivered with the signal and processed by the signal handler.
++   */
++  if (client->initialized)
++    goto out;
++
+   if (result != NULL)
+     {
+       g_variant_get (result, "(&s)", &name_owner);
+@@ -423,10 +433,10 @@ get_name_owner_cb (GObject      *source_object,
+       call_vanished_handler (client);
+     }
+-  client->initialized = TRUE;
+-
++ out:
+   if (result != NULL)
+     g_variant_unref (result);
++
+   client_unref (client);
+ }
+diff --git a/gio/gdbusobjectmanagerclient.c b/gio/gdbusobjectmanagerclient.c
+index b6b3b21..8997e8c 100644
+--- a/gio/gdbusobjectmanagerclient.c
++++ b/gio/gdbusobjectmanagerclient.c
+@@ -1133,7 +1133,6 @@ subscribe_signals (GDBusObjectManagerClient *manager,
+                    const gchar *name_owner)
+ {
+   GError *error = NULL;
+-  GVariant *ret;
+   g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager));
+   g_return_if_fail (manager->priv->signal_subscription_id == 0);
+@@ -1159,22 +1158,7 @@ subscribe_signals (GDBusObjectManagerClient *manager,
+       /* The bus daemon may not implement path_namespace so gracefully
+        * handle this by using a fallback triggered if @error is set. */
+-      ret = g_dbus_connection_call_sync (manager->priv->connection,
+-                                         "org.freedesktop.DBus",
+-                                         "/org/freedesktop/DBus",
+-                                         "org.freedesktop.DBus",
+-                                         "AddMatch",
+-                                         g_variant_new ("(s)",
+-                                                        manager->priv->match_rule),
+-                                         NULL, /* reply_type */
+-                                         G_DBUS_CALL_FLAGS_NONE,
+-                                         -1, /* default timeout */
+-                                         NULL, /* TODO: Cancellable */
+-                                         &error);
+-
+-      /* yay, bus daemon supports path_namespace */
+-      if (ret != NULL)
+-        g_variant_unref (ret);
++      g_dbus_add_match (manager->priv->connection, manager->priv->match_rule, &error);
+     }
+   if (error == NULL)
+@@ -1244,19 +1228,9 @@ maybe_unsubscribe_signals (GDBusObjectManagerClient *manager)
+       /* Since the AddMatch call succeeded this is guaranteed to not
+        * fail - therefore, don't bother checking the return value
+        */
+-      g_dbus_connection_call (manager->priv->connection,
+-                              "org.freedesktop.DBus",
+-                              "/org/freedesktop/DBus",
+-                              "org.freedesktop.DBus",
+-                              "RemoveMatch",
+-                              g_variant_new ("(s)",
+-                                             manager->priv->match_rule),
+-                              NULL, /* reply_type */
+-                              G_DBUS_CALL_FLAGS_NONE,
+-                              -1, /* default timeout */
+-                              NULL, /* GCancellable */
+-                              NULL, /* GAsyncReadyCallback */
+-                              NULL); /* user data */
++      g_dbus_remove_match (manager->priv->connection,
++                           manager->priv->match_rule,
++                           NULL);
+       g_free (manager->priv->match_rule);
+       manager->priv->match_rule = NULL;
+     }
+diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c
+index 2c9238c..4132e82 100644
+--- a/gio/gdbusprivate.c
++++ b/gio/gdbusprivate.c
+@@ -63,6 +63,10 @@
+ #include "glibintl.h"
++#define READ_BUFFER_MIN_READ_SIZE_FOR_HEADER 16
++/* TODO: 4096 is randomly chosen; might want a better chosen default minimum */
++#define READ_BUFFER_MIN_BUFFER_SIZE 4096
++
+ static gboolean _g_dbus_worker_do_initial_read (gpointer data);
+ static void schedule_pending_close (GDBusWorker *worker);
+@@ -481,7 +485,7 @@ _g_dbus_worker_emit_disconnected (GDBusWorker  *worker,
+                                   GError       *error)
+ {
+   if (!g_atomic_int_get (&worker->stopped))
+-    worker->disconnected_callback (worker, remote_peer_vanished, error, worker->user_data);
++    worker->disconnected_callback (remote_peer_vanished, error, worker->user_data);
+ }
+ static void
+@@ -489,7 +493,7 @@ _g_dbus_worker_emit_message_received (GDBusWorker  *worker,
+                                       GDBusMessage *message)
+ {
+   if (!g_atomic_int_get (&worker->stopped))
+-    worker->message_received_callback (worker, message, worker->user_data);
++    worker->message_received_callback (message, worker->user_data);
+ }
+ static GDBusMessage *
+@@ -498,7 +502,7 @@ _g_dbus_worker_emit_message_about_to_be_sent (GDBusWorker  *worker,
+ {
+   GDBusMessage *ret;
+   if (!g_atomic_int_get (&worker->stopped))
+-    ret = worker->message_about_to_be_sent_callback (worker, g_steal_pointer (&message), worker->user_data);
++    ret = worker->message_about_to_be_sent_callback (g_steal_pointer (&message), worker->user_data);
+   else
+     ret = g_steal_pointer (&message);
+   return ret;
+@@ -567,6 +571,36 @@ _g_dbus_worker_unfreeze (GDBusWorker *worker)
+ static void _g_dbus_worker_do_read_unlocked (GDBusWorker *worker);
++static gssize _g_dbus_worker_buffer_get_next_full_message_length (GDBusWorker *worker)
++{
++  GError *error;
++
++  if (worker->read_buffer == NULL)
++    return 0;
++
++  if (worker->read_buffer_cur_size >= READ_BUFFER_MIN_READ_SIZE_FOR_HEADER)
++    {
++      gssize message_len;
++      /* OK, got the header - determine how many more bytes are needed */
++      error = NULL;
++      message_len = g_dbus_message_bytes_needed ((guchar *) worker->read_buffer,
++                                                 READ_BUFFER_MIN_READ_SIZE_FOR_HEADER,
++                                                 &error);
++      if (message_len == -1)
++        {
++          g_warning ("_g_dbus_worker_do_read_cb: error determining bytes needed: %s", error->message);
++          _g_dbus_worker_emit_disconnected (worker, FALSE, error);
++          g_error_free (error);
++          return -1;
++        }
++      if (message_len <= worker->read_buffer_cur_size)
++        return message_len;
++
++      worker->read_buffer_bytes_wanted = message_len;
++    }
++  return 0;
++}
++
+ /* called in private thread shared by all GDBusConnection instances (without read-lock held) */
+ static void
+ _g_dbus_worker_do_read_cb (GInputStream  *input_stream,
+@@ -576,6 +610,7 @@ _g_dbus_worker_do_read_cb (GInputStream  *input_stream,
+   GDBusWorker *worker = user_data;
+   GError *error;
+   gssize bytes_read;
++  gssize message_len;
+   g_mutex_lock (&worker->read_lock);
+@@ -722,97 +757,106 @@ _g_dbus_worker_do_read_cb (GInputStream  *input_stream,
+   read_message_print_transport_debug (bytes_read, worker);
+   worker->read_buffer_cur_size += bytes_read;
+-  if (worker->read_buffer_bytes_wanted == worker->read_buffer_cur_size)
++
++  while ((message_len = _g_dbus_worker_buffer_get_next_full_message_length (worker) ) > 0)
+     {
+       /* OK, got what we asked for! */
+-      if (worker->read_buffer_bytes_wanted == 16)
+-        {
+-          gssize message_len;
+-          /* OK, got the header - determine how many more bytes are needed */
+-          error = NULL;
+-          message_len = g_dbus_message_bytes_needed ((guchar *) worker->read_buffer,
+-                                                     16,
+-                                                     &error);
+-          if (message_len == -1)
+-            {
+-              g_warning ("_g_dbus_worker_do_read_cb: error determining bytes needed: %s", error->message);
+-              _g_dbus_worker_emit_disconnected (worker, FALSE, error);
+-              g_error_free (error);
+-              goto out;
+-            }
++      GDBusMessage *message;
++      error = NULL;
+-          worker->read_buffer_bytes_wanted = message_len;
+-          _g_dbus_worker_do_read_unlocked (worker);
++      /* TODO: use connection->priv->auth to decode the message */
++
++      message = g_dbus_message_new_from_blob ((guchar *) worker->read_buffer,
++                                              message_len,
++                                              worker->capabilities,
++                                              &error);
++      if (message == NULL)
++        {
++          gchar *s;
++          s = _g_dbus_hexdump (worker->read_buffer, message_len, 2);
++          g_warning ("Error decoding D-Bus message of %" G_GSIZE_FORMAT " bytes\n"
++                     "The error is: %s\n"
++                     "The payload is as follows:\n"
++                     "%s",
++                     message_len,
++                     error->message,
++                     s);
++          g_free (s);
++          _g_dbus_worker_emit_disconnected (worker, FALSE, error);
++          g_error_free (error);
++          goto out;
+         }
+-      else
++
++#ifdef G_OS_UNIX
++      if (worker->read_fd_list != NULL)
+         {
+-          GDBusMessage *message;
+-          error = NULL;
++          guint fds_needed;
++          GUnixFDList *fd_list_for_message;
+-          /* TODO: use connection->priv->auth to decode the message */
++          fds_needed = g_dbus_message_get_num_unix_fds (message);
+-          message = g_dbus_message_new_from_blob ((guchar *) worker->read_buffer,
+-                                                  worker->read_buffer_cur_size,
+-                                                  worker->capabilities,
+-                                                  &error);
+-          if (message == NULL)
++          if ((guint)g_unix_fd_list_get_length (worker->read_fd_list) > fds_needed)
+             {
+-              gchar *s;
+-              s = _g_dbus_hexdump (worker->read_buffer, worker->read_buffer_cur_size, 2);
+-              g_warning ("Error decoding D-Bus message of %" G_GSIZE_FORMAT " bytes\n"
+-                         "The error is: %s\n"
+-                         "The payload is as follows:\n"
+-                         "%s",
+-                         worker->read_buffer_cur_size,
+-                         error->message,
+-                         s);
+-              g_free (s);
+-              _g_dbus_worker_emit_disconnected (worker, FALSE, error);
+-              g_error_free (error);
+-              goto out;
+-            }
++              gint num_fds;
++              GUnixFDList *old_worker_read_fd_list;
++              gint *fds;
+-#ifdef G_OS_UNIX
+-          if (worker->read_fd_list != NULL)
++              old_worker_read_fd_list = worker->read_fd_list;
++              fds = g_unix_fd_list_steal_fds (old_worker_read_fd_list, &num_fds);
++              fd_list_for_message = g_unix_fd_list_new_from_array (fds, fds_needed);
++              worker->read_fd_list = g_unix_fd_list_new_from_array (fds + fds_needed, num_fds - fds_needed);
++              g_object_unref (old_worker_read_fd_list);
++              g_free (fds);
++            }
++          else
+             {
+-              g_dbus_message_set_unix_fd_list (message, worker->read_fd_list);
+-              g_object_unref (worker->read_fd_list);
++              fd_list_for_message = worker->read_fd_list;
+               worker->read_fd_list = NULL;
+             }
++
++          if (fds_needed > 0)
++            g_dbus_message_set_unix_fd_list (message, fd_list_for_message);
++          g_object_unref (fd_list_for_message);
++        }
+ #endif
+-          if (G_UNLIKELY (_g_dbus_debug_message ()))
++      if (G_UNLIKELY (_g_dbus_debug_message ()))
++        {
++          gchar *s;
++          _g_dbus_debug_print_lock ();
++          g_print ("========================================================================\n"
++                   "GDBus-debug:Message:\n"
++                   "  <<<< RECEIVED D-Bus message (%" G_GSIZE_FORMAT " bytes)\n",
++                   message_len);
++          s = g_dbus_message_print (message, 2);
++          g_print ("%s", s);
++          g_free (s);
++          if (G_UNLIKELY (_g_dbus_debug_payload ()))
+             {
+-              gchar *s;
+-              _g_dbus_debug_print_lock ();
+-              g_print ("========================================================================\n"
+-                       "GDBus-debug:Message:\n"
+-                       "  <<<< RECEIVED D-Bus message (%" G_GSIZE_FORMAT " bytes)\n",
+-                       worker->read_buffer_cur_size);
+-              s = g_dbus_message_print (message, 2);
+-              g_print ("%s", s);
++              s = _g_dbus_hexdump (worker->read_buffer, message_len, 2);
++              g_print ("%s\n", s);
+               g_free (s);
+-              if (G_UNLIKELY (_g_dbus_debug_payload ()))
+-                {
+-                  s = _g_dbus_hexdump (worker->read_buffer, worker->read_buffer_cur_size, 2);
+-                  g_print ("%s\n", s);
+-                  g_free (s);
+-                }
+-              _g_dbus_debug_print_unlock ();
+             }
++          _g_dbus_debug_print_unlock ();
++        }
+-          /* yay, got a message, go deliver it */
+-          _g_dbus_worker_queue_or_deliver_received_message (worker, g_steal_pointer (&message));
++      /* yay, got a message, go deliver it */
++      _g_dbus_worker_queue_or_deliver_received_message (worker, g_steal_pointer (&message));
+-          /* start reading another message! */
+-          worker->read_buffer_bytes_wanted = 0;
+-          worker->read_buffer_cur_size = 0;
+-          _g_dbus_worker_do_read_unlocked (worker);
++      /* set up reading another message */
++      if (worker->read_buffer_cur_size > message_len)
++        {
++          memmove (worker->read_buffer,
++                   worker->read_buffer + message_len,
++                   worker->read_buffer_cur_size - message_len);
+         }
++      worker->read_buffer_cur_size -= message_len;
++      worker->read_buffer_bytes_wanted = 0;
+     }
+-  else
++
++  if (message_len == 0)
+     {
+-      /* didn't get all the bytes we requested - so repeat the request... */
++      /* start reading another message or repeat the request to get all the bytes */
+       _g_dbus_worker_do_read_unlocked (worker);
+     }
+@@ -837,15 +881,21 @@ _g_dbus_worker_do_read_unlocked (GDBusWorker *worker)
+   /* if bytes_wanted is zero, it means start reading a message */
+   if (worker->read_buffer_bytes_wanted == 0)
+     {
+-      worker->read_buffer_cur_size = 0;
+-      worker->read_buffer_bytes_wanted = 16;
++      if (worker->socket != NULL)
++        {
++          worker->read_buffer_bytes_wanted = READ_BUFFER_MIN_BUFFER_SIZE;
++        }
++      else
++        {
++          worker->read_buffer_cur_size = 0;
++          worker->read_buffer_bytes_wanted = READ_BUFFER_MIN_READ_SIZE_FOR_HEADER;
++        }
+     }
+   /* ensure we have a (big enough) buffer */
+   if (worker->read_buffer == NULL || worker->read_buffer_bytes_wanted > worker->read_buffer_allocated_size)
+     {
+-      /* TODO: 4096 is randomly chosen; might want a better chosen default minimum */
+-      worker->read_buffer_allocated_size = MAX (worker->read_buffer_bytes_wanted, 4096);
++      worker->read_buffer_allocated_size = MAX (worker->read_buffer_bytes_wanted, READ_BUFFER_MIN_BUFFER_SIZE);
+       worker->read_buffer = g_realloc (worker->read_buffer, worker->read_buffer_allocated_size);
+     }
+diff --git a/gio/gdbusprivate.h b/gio/gdbusprivate.h
+index e7a5bfa..898b11d 100644
+--- a/gio/gdbusprivate.h
++++ b/gio/gdbusprivate.h
+@@ -31,16 +31,13 @@ G_BEGIN_DECLS
+ typedef struct GDBusWorker GDBusWorker;
+-typedef void (*GDBusWorkerMessageReceivedCallback) (GDBusWorker   *worker,
+-                                                    GDBusMessage  *message,
++typedef void (*GDBusWorkerMessageReceivedCallback) (GDBusMessage  *message,
+                                                     gpointer       user_data);
+-typedef GDBusMessage *(*GDBusWorkerMessageAboutToBeSentCallback) (GDBusWorker   *worker,
+-                                                                  GDBusMessage  *message,
++typedef GDBusMessage *(*GDBusWorkerMessageAboutToBeSentCallback) (GDBusMessage  *message,
+                                                                   gpointer       user_data);
+-typedef void (*GDBusWorkerDisconnectedCallback)    (GDBusWorker   *worker,
+-                                                    gboolean       remote_peer_vanished,
++typedef void (*GDBusWorkerDisconnectedCallback)    (gboolean       remote_peer_vanished,
+                                                     GError        *error,
+                                                     gpointer       user_data);
+@@ -158,6 +155,17 @@ gchar *_g_dbus_hexencode (const gchar *str,
+ GDBusConnection *_g_bus_get_singleton_if_exists (GBusType bus_type);
+ void             _g_bus_forget_singleton        (GBusType bus_type);
++void g_dbus_message_init_header_iter (GDBusMessage   *message,
++                                      GHashTableIter *iter);
++
++GObject *
++g_dbus_address_get_stream_internal (const gchar   *address,
++                                    gboolean       kdbus_okay,
++                                    gchar        **out_uuid,
++                                    GCancellable  *cancellable,
++                                    GError       **error);
++
++
+ G_END_DECLS
+ #endif /* __G_DBUS_PRIVATE_H__ */
+diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c
+index afc6fe9..3166dd6 100644
+--- a/gio/gdbusproxy.c
++++ b/gio/gdbusproxy.c
+@@ -2598,26 +2598,10 @@ out:
+ static const gchar *
+ get_destination_for_call (GDBusProxy *proxy)
+ {
+-  const gchar *ret;
+-
+-  ret = NULL;
+-
+-  /* If proxy->priv->name is a unique name, then proxy->priv->name_owner
+-   * is never NULL and always the same as proxy->priv->name. We use this
+-   * knowledge to avoid checking if proxy->priv->name is a unique or
+-   * well-known name.
+-   */
+-  ret = proxy->priv->name_owner;
+-  if (ret != NULL)
+-    goto out;
+-
+   if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START)
+-    goto out;
+-
+-  ret = proxy->priv->name;
++    return proxy->priv->name_owner;
+- out:
+-  return ret;
++  return proxy->priv->name;
+ }
+ /* ---------------------------------------------------------------------------------------------------- */
+diff --git a/gio/gdbusutils.c b/gio/gdbusutils.c
+index 4b4b7e1..7fb3305 100644
+--- a/gio/gdbusutils.c
++++ b/gio/gdbusutils.c
+@@ -453,6 +453,11 @@ g_dbus_gvariant_to_gvalue (GVariant  *value,
+       g_value_set_uint64 (out_gvalue, g_variant_get_uint64 (value));
+       break;
++    case G_VARIANT_CLASS_FLOAT:
++      g_value_init (out_gvalue, G_TYPE_FLOAT);
++      g_value_set_float (out_gvalue, g_variant_get_float (value));
++      break;
++
+     case G_VARIANT_CLASS_DOUBLE:
+       g_value_init (out_gvalue, G_TYPE_DOUBLE);
+       g_value_set_double (out_gvalue, g_variant_get_double (value));
+@@ -627,6 +632,10 @@ g_dbus_gvalue_to_gvariant (const GValue       *gvalue,
+           ret = g_variant_ref_sink (g_variant_new_uint64 (g_value_get_uint64 (gvalue)));
+           break;
++        case G_VARIANT_CLASS_FLOAT:
++          ret = g_variant_ref_sink (g_variant_new_float (g_value_get_float (gvalue)));
++          break;
++
+         case G_VARIANT_CLASS_DOUBLE:
+           ret = g_variant_ref_sink (g_variant_new_double (g_value_get_double (gvalue)));
+           break;
+diff --git a/gio/gioenums.h b/gio/gioenums.h
+index c820cd3..c8b9eb1 100644
+--- a/gio/gioenums.h
++++ b/gio/gioenums.h
+@@ -999,6 +999,66 @@ typedef enum
+ /* When adding new flags, their numeric values must currently match those
+  * used in the D-Bus Specification. */
++/**
++ * GBusRequestNameReplyFlags:
++ * @G_BUS_REQUEST_NAME_FLAGS_ERROR: Error flag.
++ * @G_BUS_REQUEST_NAME_FLAGS_PRIMARY_OWNER: Caller is now the primary owner of the name, replacing
++ * any previous owner.
++ * @G_BUS_REQUEST_NAME_FLAGS_IN_QUEUE: The name already had an owner, the application will be
++ * placed in a queue.
++ * @G_BUS_REQUEST_NAME_FLAGS_EXISTS: The name already has an owner.
++ * @G_BUS_REQUEST_NAME_FLAGS_ALREADY_OWNER: The application trying to request ownership of a name
++ * is already the owner of it.
++ *
++ * Flags used in g_dbus_request_name().
++ *
++ * Since: 2.44
++ */
++typedef enum
++{
++  G_BUS_REQUEST_NAME_FLAGS_ERROR = 0,
++  G_BUS_REQUEST_NAME_FLAGS_PRIMARY_OWNER = 1,
++  G_BUS_REQUEST_NAME_FLAGS_IN_QUEUE = 2,
++  G_BUS_REQUEST_NAME_FLAGS_EXISTS = 3,
++  G_BUS_REQUEST_NAME_FLAGS_ALREADY_OWNER = 4
++} GBusRequestNameReplyFlags;
++
++/**
++ * GBusReleaseNameReplyFlags:
++ * @G_BUS_RELEASE_NAME_FLAGS_ERROR: Error flag.
++ * @G_BUS_RELEASE_NAME_FLAGS_RELEASED: The caller has released his claim on the given name.
++ * @G_BUS_RELEASE_NAME_FLAGS_NON_EXISTENT: The given name does not exist on this bus.
++ * @G_BUS_RELEASE_NAME_FLAGS_NOT_OWNER: The caller not waiting in the queue to own this name.
++ *
++ * Flags used in g_dbus_release_name().
++ *
++ * Since: 2.44
++ */
++typedef enum
++{
++  G_BUS_RELEASE_NAME_FLAGS_ERROR = 0,
++  G_BUS_RELEASE_NAME_FLAGS_RELEASED = 1,
++  G_BUS_RELEASE_NAME_FLAGS_NON_EXISTENT = 2,
++  G_BUS_RELEASE_NAME_FLAGS_NOT_OWNER = 3
++} GBusReleaseNameReplyFlags;
++
++/**
++ * GBusStartServiceReplyFlags:
++ * @G_BUS_START_SERVICE_REPLY_ERROR: Error flag.
++ * @G_BUS_START_SERVICE_REPLY_SUCCESS: The service was successfully started.
++ * @G_BUS_START_SERVICE_REPLY_ALREADY_RUNNING: A connection already owns the given name.
++ *
++ * Flags used in g_dbus_start_service_by_name().
++ *
++ * Since: 2.44
++ */
++typedef enum
++{
++  G_BUS_START_SERVICE_REPLY_ERROR = 0,
++  G_BUS_START_SERVICE_REPLY_SUCCESS = 1,
++  G_BUS_START_SERVICE_REPLY_ALREADY_RUNNING = 2
++} GBusStartServiceReplyFlags;
++
+ /**
+  * GBusNameWatcherFlags:
+  * @G_BUS_NAME_WATCHER_FLAGS_NONE: No flags set.
+diff --git a/gio/gkdbus.c b/gio/gkdbus.c
+new file mode 100755
+index 0000000..aeb4408
+--- /dev/null
++++ b/gio/gkdbus.c
+@@ -0,0 +1,4356 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright (C) 2015 Samsung Electronics
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Author: Lukasz Skalski       <l.skalski@samsung.com>
++ * Author: Adrian Szyndela      <adrian.s@samsung.com>
++ * Author: Michal Eljasiewicz   <m.eljasiewic@samsung.com>
++ */
++
++#include "config.h"
++#include "gdbusconnection.h" /* DBUS_DEFAULT_TIMEOUT_MSEC */
++#include "gkdbus.h"
++#include "glib-unix.h"
++#include "glib-linux.h"
++#include "glibintl.h"
++#include "gvariant-serialiser.h"
++#include <linux/kdbus.h>
++
++#include <gio/gio.h>
++#include <errno.h>
++#include <string.h>
++#include <sys/mman.h>
++#include <sys/ioctl.h>
++#include <stdio.h>
++#include <stdint.h>
++
++#ifdef HAVE_SYS_FILIO_H
++#include <sys/filio.h>
++#endif
++
++#ifdef HAVE_SYS_UIO_H
++#include <sys/uio.h>
++#endif
++
++#ifdef LIBDBUSPOLICY
++#include <dbuspolicy/libdbuspolicy1.h>
++#endif
++
++#define DBUS_DAEMON_EMULATION
++#ifdef DBUS_DAEMON_EMULATION
++#include "gkdbusfakedaemon.h"
++#endif
++
++#include <glib/gstdio.h>
++#include <glib/glib-private.h>
++#include <gio/gio.h>
++#include <gio/gunixfdlist.h>
++
++#include "glibintl.h"
++#include "gunixfdmessage.h"
++
++#define KDBUS_MSG_MAX_SIZE          8192
++#define KDBUS_INFINITE_TIMEOUT_NS   0x3fffffffffffffffLLU
++
++#define RECEIVE_POOL_SIZE_DEFAULT_SIZE    (16 * 1024LU * 1024LU)
++#define RECEIVE_POOL_SIZE_ENV_VAR_NAME    "KDBUS_MEMORY_POOL_SIZE"
++#define RECEIVE_POOL_SIZE_MAX_MBYTES      64
++#define RECEIVE_POOL_SIZE_MIN_KBYTES      16
++
++#define KDBUS_MEMFD_THRESHOLD      (512 * 1024LU)
++#define KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE  0x00200000      /* maximum size of message header and items */
++
++#define KDBUS_ALIGN8(l)            (((l) + 7) & ~7)
++#define KDBUS_ALIGN8_PTR(p)        ((void*) (uintptr_t)(p))
++
++#define KDBUS_ITEM_HEADER_SIZE G_STRUCT_OFFSET(struct kdbus_item, data)
++#define KDBUS_ITEM_SIZE(s) KDBUS_ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE)
++
++#define KDBUS_ITEM_NEXT(item) \
++        (typeof(item))(((guint8 *)item) + KDBUS_ALIGN8((item)->size))
++#define KDBUS_ITEM_FOREACH(item, head, first)                    \
++        for (item = (head)->first;                               \
++             (guint8 *)(item) < (guint8 *)(head) + (head)->size; \
++             item = KDBUS_ITEM_NEXT(item))
++#define KDBUS_FOREACH(iter, first, _size)                             \
++        for (iter = (first);                                          \
++             ((guint8 *)(iter) < (guint8 *)(first) + (_size)) &&      \
++               ((guint8 *)(iter) >= (guint8 *)(first));               \
++             iter = (void*)(((guint8 *)iter) + KDBUS_ALIGN8((iter)->size)))
++
++#define g_alloca0(x) memset(g_alloca(x), '\0', (x))
++
++struct dbus_fixed_header {
++  guint8  endian;
++  guint8  type;
++  guint8  flags;
++  guint8  version;
++  guint32 reserved;
++  guint64 serial;
++};
++
++#define DBUS_FIXED_HEADER_TYPE     ((const GVariantType *) "(yyyyut)")
++#define DBUS_EXTENDED_HEADER_TYPE  ((const GVariantType *) "a{tv}")
++#define DBUS_MESSAGE_TYPE          ((const GVariantType *) "((yyyyut)a{tv}v)")
++
++/*
++ * Macros for SipHash algorithm
++ */
++
++#define ROTL(x,b) (guint64)( ((x) << (b)) | ( (x) >> (64 - (b))) )
++
++#define U32TO8_LE(p, v)         \
++    (p)[0] = (guint8)((v)      ); (p)[1] = (guint8)((v) >>  8); \
++    (p)[2] = (guint8)((v) >> 16); (p)[3] = (guint8)((v) >> 24);
++
++#define U64TO8_LE(p, v)         \
++  U32TO8_LE((p),     (guint32)((v)      ));   \
++  U32TO8_LE((p) + 4, (guint32)((v) >> 32));
++
++#define U8TO64_LE(p) \
++  (((guint64)((p)[0])      ) | \
++   ((guint64)((p)[1]) <<  8) | \
++   ((guint64)((p)[2]) << 16) | \
++   ((guint64)((p)[3]) << 24) | \
++   ((guint64)((p)[4]) << 32) | \
++   ((guint64)((p)[5]) << 40) | \
++   ((guint64)((p)[6]) << 48) | \
++   ((guint64)((p)[7]) << 56))
++
++#define SIPROUND            \
++  do {              \
++    v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
++    v2 += v3; v3=ROTL(v3,16); v3 ^= v2;     \
++    v0 += v3; v3=ROTL(v3,21); v3 ^= v0;     \
++    v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
++  } while(0)
++
++typedef GObjectClass GKDBusWorkerClass;
++
++struct _GKDBusWorker
++{
++  GObject            parent_instance;
++
++  const gchar       *address;
++  gint               fd;
++
++  GMainContext      *context;
++  GMainLoop         *loop;
++  GThread           *thread;
++  GSource           *source;
++
++  gchar             *kdbus_buffer;
++  guint64           receive_pool_size;
++  gchar             *unique_name;
++  guint64            unique_id;
++
++  guint64            flags;
++  guint64            attach_flags_send;
++  guint64            attach_flags_recv;
++
++  gsize              bloom_size;
++  guint              bloom_n_hash;
++
++  guint              closed : 1;
++  guint              inited : 1;
++  guint              timeout;
++  guint              timed_out : 1;
++
++  guchar             bus_id[16];
++
++  GMutex             matches_mutex;
++  GList             *matches;
++
++#ifdef LIBDBUSPOLICY
++  void              *dbuspolicy;
++#endif
++
++  GDBusCapabilityFlags                     capabilities;
++  GDBusWorkerMessageReceivedCallback       message_received_callback;
++  GDBusWorkerMessageAboutToBeSentCallback  message_about_to_be_sent_callback;
++  GDBusWorkerDisconnectedCallback          disconnected_callback;
++  gpointer                                 user_data;
++};
++
++static gboolean  _g_kdbus_send    (GKDBusWorker  *worker,
++                                   GDBusMessage  *message,
++                                   GDBusMessage **reply,
++                                   gint           timeout_msec,
++                                   GCancellable  *cancellable,
++                                   GError       **error);
++
++static void      _g_kdbus_receive (GKDBusWorker  *worker,
++                                   GError       **error);
++
++G_DEFINE_TYPE (GKDBusWorker, g_kdbus_worker, G_TYPE_OBJECT)
++
++/* Hash keys for bloom filters*/
++const guint8 hash_keys[8][16] =
++{
++  {0xb9,0x66,0x0b,0xf0,0x46,0x70,0x47,0xc1,0x88,0x75,0xc4,0x9c,0x54,0xb9,0xbd,0x15},
++  {0xaa,0xa1,0x54,0xa2,0xe0,0x71,0x4b,0x39,0xbf,0xe1,0xdd,0x2e,0x9f,0xc5,0x4a,0x3b},
++  {0x63,0xfd,0xae,0xbe,0xcd,0x82,0x48,0x12,0xa1,0x6e,0x41,0x26,0xcb,0xfa,0xa0,0xc8},
++  {0x23,0xbe,0x45,0x29,0x32,0xd2,0x46,0x2d,0x82,0x03,0x52,0x28,0xfe,0x37,0x17,0xf5},
++  {0x56,0x3b,0xbf,0xee,0x5a,0x4f,0x43,0x39,0xaf,0xaa,0x94,0x08,0xdf,0xf0,0xfc,0x10},
++  {0x31,0x80,0xc8,0x73,0xc7,0xea,0x46,0xd3,0xaa,0x25,0x75,0x0f,0x9e,0x4c,0x09,0x29},
++  {0x7d,0xf7,0x18,0x4b,0x7b,0xa4,0x44,0xd5,0x85,0x3c,0x06,0xe0,0x65,0x53,0x96,0x6d},
++  {0xf2,0x77,0xe9,0x6f,0x93,0xb5,0x4e,0x71,0x9a,0x0c,0x34,0x88,0x39,0x25,0xbf,0x35}
++};
++
++enum {
++  MATCH_ELEMENT_TYPE,
++  MATCH_ELEMENT_SENDER,
++  MATCH_ELEMENT_INTERFACE,
++  MATCH_ELEMENT_MEMBER,
++  MATCH_ELEMENT_PATH,
++  MATCH_ELEMENT_PATH_NAMESPACE,
++  MATCH_ELEMENT_DESTINATION,
++  MATCH_ELEMENT_ARG0NAMESPACE,
++  MATCH_ELEMENT_EAVESDROP,
++  MATCH_ELEMENT_ARGN,
++  MATCH_ELEMENT_ARGNPATH,
++};
++
++static guint64 _global_match_cookie = 1;
++
++/* MatchElement struct */
++typedef struct {
++  guint16 type;
++  guint16 arg;
++  char *value;
++} MatchElement;
++
++/* Match struct */
++typedef struct {
++  int n_elements;
++  MatchElement *elements;
++  guint64 cookie;
++} Match;
++
++
++/*
++ * SipHash algorithm
++ */
++static void
++_g_siphash24 (guint8         out[8],
++              const void    *_in,
++              gsize          inlen,
++              const guint8   k[16])
++{
++  /* "somepseudorandomlygeneratedbytes" */
++  guint64 v0 = 0x736f6d6570736575ULL;
++  guint64 v1 = 0x646f72616e646f6dULL;
++  guint64 v2 = 0x6c7967656e657261ULL;
++  guint64 v3 = 0x7465646279746573ULL;
++  guint64 b;
++  guint64 k0 = U8TO64_LE (k);
++  guint64 k1 = U8TO64_LE (k + 8);
++  guint64 m;
++  const guint8 *in = _in;
++  const guint8 *end = in + inlen - (inlen % sizeof(guint64));
++  const int left = inlen & 7;
++  b = ((guint64) inlen) << 56;
++  v3 ^= k1;
++  v2 ^= k0;
++  v1 ^= k1;
++  v0 ^= k0;
++
++  for (; in != end; in += 8)
++    {
++      m = U8TO64_LE (in);
++      v3 ^= m;
++      SIPROUND;
++      SIPROUND;
++      v0 ^= m;
++    }
++
++  switch (left)
++    {
++      case 7: b |= ((guint64) in[6]) << 48;
++      case 6: b |= ((guint64) in[5]) << 40;
++      case 5: b |= ((guint64) in[4]) << 32;
++      case 4: b |= ((guint64) in[3]) << 24;
++      case 3: b |= ((guint64) in[2]) << 16;
++      case 2: b |= ((guint64) in[1]) <<  8;
++      case 1: b |= ((guint64) in[0]); break;
++      case 0: break;
++    }
++
++  v3 ^= b;
++  SIPROUND;
++  SIPROUND;
++  v0 ^= b;
++
++  v2 ^= 0xff;
++  SIPROUND;
++  SIPROUND;
++  SIPROUND;
++  SIPROUND;
++  b = v0 ^ v1 ^ v2  ^ v3;
++  U64TO8_LE (out, b);
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++static gboolean
++is_key (const char *key_start, const char *key_end, char *value)
++{
++  gsize len = strlen (value);
++
++  if (len != key_end - key_start)
++    return FALSE;
++
++  return strncmp (key_start, value, len) == 0;
++}
++
++static gboolean
++parse_key (MatchElement *element, const char *key_start, const char *key_end)
++{
++  gboolean res = TRUE;
++
++  if (is_key (key_start, key_end, "type"))
++    {
++      element->type = MATCH_ELEMENT_TYPE;
++    }
++  else if (is_key (key_start, key_end, "sender"))
++    {
++      element->type = MATCH_ELEMENT_SENDER;
++    }
++  else if (is_key (key_start, key_end, "interface"))
++    {
++      element->type = MATCH_ELEMENT_INTERFACE;
++    }
++  else if (is_key (key_start, key_end, "member"))
++    {
++      element->type = MATCH_ELEMENT_MEMBER;
++    }
++  else if (is_key (key_start, key_end, "path"))
++    {
++      element->type = MATCH_ELEMENT_PATH;
++    }
++  else if (is_key (key_start, key_end, "path_namespace"))
++    {
++      element->type = MATCH_ELEMENT_PATH_NAMESPACE;
++    }
++  else if (is_key (key_start, key_end, "destination"))
++    {
++      element->type = MATCH_ELEMENT_DESTINATION;
++    }
++  else if (is_key (key_start, key_end, "arg0namespace"))
++    {
++      element->type = MATCH_ELEMENT_ARG0NAMESPACE;
++    }
++  else if (is_key (key_start, key_end, "eavesdrop"))
++    {
++      element->type = MATCH_ELEMENT_EAVESDROP;
++    }
++  else if (key_end - key_start > 3 && is_key (key_start, key_start + 3, "arg"))
++    {
++      const char *digits = key_start + 3;
++      const char *end_digits = digits;
++
++      while (end_digits < key_end && g_ascii_isdigit (*end_digits))
++        end_digits++;
++
++      if (end_digits == key_end) /* argN */
++        {
++          element->type = MATCH_ELEMENT_ARGN;
++          element->arg = atoi (digits);
++        }
++      else if (is_key (end_digits, key_end, "path")) /* argNpath */
++        {
++          element->type = MATCH_ELEMENT_ARGNPATH;
++          element->arg = atoi (digits);
++        }
++      else
++        res = FALSE;
++    }
++  else
++    res = FALSE;
++
++  return res;
++}
++
++static const char *
++parse_value (MatchElement *element, const char *s)
++{
++  char quote_char;
++  GString *value;
++
++  value = g_string_new ("");
++
++  quote_char = 0;
++
++  for (;*s; s++)
++    {
++      if (quote_char == 0)
++        {
++          switch (*s)
++            {
++            case '\'':
++              quote_char = '\'';
++              break;
++
++            case ',':
++              s++;
++              goto out;
++
++            case '\\':
++              quote_char = '\\';
++              break;
++
++            default:
++              g_string_append_c (value, *s);
++              break;
++            }
++        }
++      else if (quote_char == '\\')
++        {
++          /* \ only counts as an escape if escaping a quote mark */
++          if (*s != '\'')
++            g_string_append_c (value, '\\');
++
++          g_string_append_c (value, *s);
++          quote_char = 0;
++        }
++      else /* quote_char == ' */
++        {
++          if (*s == '\'')
++            quote_char = 0;
++          else
++            g_string_append_c (value, *s);
++        }
++    }
++
++ out:
++  if (quote_char == '\\')
++    g_string_append_c (value, '\\');
++  else if (quote_char == '\'')
++    {
++      g_string_free (value, TRUE);
++      return NULL;
++    }
++
++  element->value = g_string_free (value, FALSE);
++  return s;
++}
++
++static Match *
++match_new (const char *str)
++{
++  Match *match;
++  GArray *elements;
++  const char *p;
++  const char *key_start;
++  const char *key_end;
++  MatchElement element;
++  int i;
++
++  elements = g_array_new (TRUE, TRUE, sizeof (MatchElement));
++
++  p = str;
++
++  while (*p != 0)
++    {
++      memset (&element, 0, sizeof (element));
++
++      /* Skip initial whitespace */
++      while (*p && g_ascii_isspace (*p))
++        p++;
++
++      key_start = p;
++
++      /* Read non-whitespace non-equals chars */
++      while (*p && *p != '=' && !g_ascii_isspace (*p))
++        p++;
++
++      key_end = p;
++
++      /* Skip any whitespace after key */
++      while (*p && g_ascii_isspace (*p))
++        p++;
++
++      if (key_start == key_end)
++        continue; /* Allow trailing whitespace */
++      if (*p != '=')
++        goto error;
++
++      ++p;
++
++      if (!parse_key (&element, key_start, key_end))
++        goto error;
++
++      p = parse_value (&element, p);
++      if (p == NULL)
++        goto error;
++
++      g_array_append_val (elements, element);
++    }
++
++  match = g_new0 (Match, 1);
++  match->n_elements = elements->len;
++  match->elements = (MatchElement *)g_array_free (elements, FALSE);
++  match->cookie = _global_match_cookie++;
++
++  return match;
++
++ error:
++  for (i = 0; i < elements->len; i++)
++    g_free (g_array_index (elements, MatchElement, i).value);
++  g_array_free (elements, TRUE);
++  return NULL;
++}
++
++static gboolean
++match_equal (Match *a, Match *b)
++{
++  int i;
++
++  if (a->n_elements != b->n_elements)
++    return FALSE;
++
++  for (i = 0; i < a->n_elements; i++)
++    {
++      if (a->elements[i].type != b->elements[i].type ||
++          a->elements[i].arg != b->elements[i].arg ||
++          strcmp (a->elements[i].value, b->elements[i].value) != 0)
++        return FALSE;
++    }
++
++  return TRUE;
++}
++
++static void
++match_free (Match *match)
++{
++  int i;
++
++  for (i = 0; i < match->n_elements; i++)
++    g_free (match->elements[i].value);
++
++  g_free (match->elements);
++  g_free (match);
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++/*
++ * _g_kdbus_open
++ */
++gboolean
++_g_kdbus_open (GKDBusWorker  *worker,
++               const gchar   *address,
++               GError       **error)
++{
++  g_return_val_if_fail (G_IS_KDBUS_WORKER (worker), FALSE);
++  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
++
++  worker->fd = g_open(address, O_RDWR|O_NOCTTY|O_CLOEXEC, 0);
++  if (worker->fd<0)
++    {
++      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot open kdbus endpoint"));
++      return FALSE;
++    }
++
++  worker->address = g_strdup(address);
++  worker->closed = FALSE;
++  return TRUE;
++}
++
++static gboolean
++_g_kdbus_quit_loop (gpointer loop)
++{
++  g_main_loop_quit ((GMainLoop*)loop);
++  return FALSE;
++}
++
++gboolean
++_g_kdbus_can_connect (GKDBusWorker *worker,
++                      GError       **error)
++{
++#ifdef LIBDBUSPOLICY
++  worker->dbuspolicy = dbuspolicy1_init_shared (worker->address, worker->fd);
++  if (worker->dbuspolicy == NULL)
++    {
++      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot load dbus policy for kdbus transport or access to bus denied by security policy"));
++      return FALSE;
++    }
++#endif
++  return TRUE;
++}
++
++/*
++ * _g_kdbus_close
++ */
++gboolean
++_g_kdbus_close (GKDBusWorker *worker)
++{
++  g_return_val_if_fail (G_IS_KDBUS_WORKER (worker), FALSE);
++
++  if (worker->closed)
++    return TRUE;
++
++  g_main_context_invoke (worker->context, _g_kdbus_quit_loop, worker->loop);
++
++  g_main_context_unref (worker->context);
++  worker->context = NULL;
++
++  g_main_loop_unref (worker->loop);
++
++  g_thread_unref(worker->thread);
++
++  worker->thread = NULL;
++
++  g_free ((char*)worker->address);
++
++  close (worker->fd);
++  worker->fd = -1;
++
++  worker->closed = TRUE;
++  return TRUE;
++}
++
++/*
++ * _g_kdbus_is_closed
++ */
++gboolean
++_g_kdbus_is_closed (GKDBusWorker *worker)
++{
++  g_return_val_if_fail (G_IS_KDBUS_WORKER (worker), FALSE);
++
++  return worker->closed;
++}
++
++static void
++g_kdbus_free_data (GKDBusWorker  *worker,
++                   guint64        offset)
++{
++  struct kdbus_cmd_free cmd = {
++    .size = sizeof(cmd),
++    .offset = offset,
++    .flags = 0
++  };
++
++  if (ioctl (worker->fd, KDBUS_CMD_FREE, &cmd) < 0)
++    {
++      g_error ("kdbus: invalid KDBUS_CMD_FREE ioctl : %" G_GUINT64_FORMAT " offset\n", offset);
++    }
++}
++
++static void
++g_kdbus_close_msg (GKDBusWorker      *worker,
++                   struct kdbus_msg  *msg)
++{
++  guint64 offset;
++
++  offset = (guint8 *)msg - (guint8 *)worker->kdbus_buffer;
++  g_kdbus_free_data (worker, offset);
++}
++
++
++/*
++ * g_kdbus_translate_nameowner_flags
++ */
++static void
++g_kdbus_translate_nameowner_flags (GBusNameOwnerFlags   flags,
++                                   guint64             *kdbus_flags)
++{
++  guint64 new_flags;
++
++  new_flags = 0;
++
++  if (flags & G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT)
++    new_flags |= KDBUS_NAME_ALLOW_REPLACEMENT;
++
++  if (flags & G_BUS_NAME_OWNER_FLAGS_REPLACE)
++    new_flags |= KDBUS_NAME_REPLACE_EXISTING;
++
++  if (!(flags & G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE))
++    new_flags |= KDBUS_NAME_QUEUE;
++
++  *kdbus_flags = new_flags;
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++/* < internal >
++ *
++ * _g_kdbus_Hello:
++ *
++ * Gets the unique name.
++ *
++ * Returns: the unique name or NULL. Do not free this string,
++ * it is owned by GKDBusWorker.
++ */
++const gchar *
++_g_kdbus_Hello (GKDBusWorker  *worker,
++                GError       **error)
++{
++  struct kdbus_cmd_hello *cmd;
++  struct kdbus_bloom_parameter *bloom;
++  struct kdbus_item *item, *items;
++
++  gchar *conn_name;
++  size_t size, conn_name_size;
++
++  const gchar *env_pool;
++  guint64 receive_pool_size = RECEIVE_POOL_SIZE_DEFAULT_SIZE;
++
++  conn_name = "gdbus-kdbus";
++  conn_name_size = strlen (conn_name);
++
++  size = KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_cmd_hello, items)) +
++         KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, str) + conn_name_size + 1);
++
++  cmd = g_alloca0 (size);
++  cmd->flags = worker->flags;
++  cmd->attach_flags_send = worker->attach_flags_send;
++  cmd->attach_flags_recv = worker->attach_flags_recv;
++  cmd->size = size;
++
++  env_pool = getenv (RECEIVE_POOL_SIZE_ENV_VAR_NAME);
++  if(env_pool)
++    {
++      guint64 size;
++      guint32 multiply = 1;
++      gint64 page_size;
++
++      page_size = sysconf(_SC_PAGESIZE);
++      if(page_size == -1)
++        {
++          size = 0;
++          goto finish;
++        }
++
++      errno = 0;
++      size = strtoul(env_pool, (char**)&env_pool, 10);
++      if(errno == EINVAL || size == 0)
++        {
++          size = 0;
++          goto finish;
++        }
++
++      if(*env_pool == 'k')
++        {
++          multiply = 1024;
++          env_pool++;
++        }
++      else if (*env_pool == 'M')
++        {
++          multiply = 1024 * 1024;
++          env_pool++;
++        }
++
++      if(*env_pool != '\0')
++        {
++          size = 0;
++          goto finish;
++        }
++
++      receive_pool_size = size * multiply;
++
++      if((receive_pool_size > RECEIVE_POOL_SIZE_MAX_MBYTES * 1024 * 1024) ||
++         (receive_pool_size < RECEIVE_POOL_SIZE_MIN_KBYTES * 1024) ||
++         ((receive_pool_size & (page_size - 1)) != 0))
++        size = 0;
++
++    finish:
++      if(size == 0)
++        {
++          g_warning ("%s value is invalid, default value %luB will be used.", RECEIVE_POOL_SIZE_ENV_VAR_NAME,
++                     RECEIVE_POOL_SIZE_DEFAULT_SIZE);
++          g_warning ("Correct value must be between %ukB and %uMB and must be aligned to page size: %" G_GINT64_FORMAT "B.",
++                     RECEIVE_POOL_SIZE_MIN_KBYTES, RECEIVE_POOL_SIZE_MAX_MBYTES, page_size);
++
++          receive_pool_size = RECEIVE_POOL_SIZE_DEFAULT_SIZE;
++        }
++    }
++
++  g_debug ("[KDBUS] receive pool size set to %" G_GUINT64_FORMAT "\n", receive_pool_size);
++  worker->receive_pool_size = receive_pool_size;
++  cmd->pool_size = worker->receive_pool_size;
++
++  item = cmd->items;
++  item->size = (guint64) G_STRUCT_OFFSET (struct kdbus_item, str) + conn_name_size + 1;
++  item->type = KDBUS_ITEM_CONN_DESCRIPTION;
++  memcpy (item->str, conn_name, conn_name_size+1);
++  item = KDBUS_ITEM_NEXT (item);
++
++  if (worker->unique_id != -1)
++    {
++      g_set_error (error, G_DBUS_ERROR,
++                   G_DBUS_ERROR_FAILED,
++                   "Already handled an Hello message");
++      return NULL;
++    }
++
++  if (ioctl(worker->fd, KDBUS_CMD_HELLO, cmd))
++    {
++      g_set_error (error, G_IO_ERROR,
++                   g_io_error_from_errno (errno),
++                   _("Failed to send HELLO: %s"),
++                   g_strerror (errno));
++      return NULL;
++    }
++
++  worker->kdbus_buffer = mmap(NULL, worker->receive_pool_size, PROT_READ, MAP_SHARED, worker->fd, 0);
++  if (worker->kdbus_buffer == MAP_FAILED)
++    {
++      g_set_error (error, G_IO_ERROR,
++                   g_io_error_from_errno (errno),
++                   _("mmap error: %s"),
++                   g_strerror (errno));
++      return NULL;
++    }
++
++  if (cmd->bus_flags > 0xFFFFFFFFULL)
++    {
++      g_set_error_literal (error,
++                           G_IO_ERROR,
++                           G_IO_ERROR_FAILED,
++                           _("Incompatible HELLO flags"));
++      return NULL;
++    }
++
++  memcpy (worker->bus_id, cmd->id128, 16);
++
++  worker->unique_id = cmd->id;
++  if (asprintf (&worker->unique_name, ":1.%llu", (unsigned long long) cmd->id) < 0)
++    {
++      g_set_error (error,
++                   G_IO_ERROR,
++                   G_IO_ERROR_UNKNOWN,
++                   "asprintf error: %s",
++                   g_strerror(errno));
++      return NULL;
++    }
++
++  /* read bloom filters parameters */
++  bloom = NULL;
++  items = (void*)(worker->kdbus_buffer + cmd->offset);
++  KDBUS_FOREACH(item, items, cmd->items_size)
++    {
++      switch (item->type)
++        {
++          case KDBUS_ITEM_BLOOM_PARAMETER:
++            bloom = &item->bloom_parameter;
++          break;
++        }
++    }
++
++  if (bloom != NULL)
++    {
++      worker->bloom_size = (gsize) bloom->size;
++      worker->bloom_n_hash = (guint) bloom->n_hash;
++    }
++  else
++    {
++      g_set_error_literal (error,
++                           G_IO_ERROR,
++                           G_IO_ERROR_FAILED,
++                           _("Can't read bloom filter parameters"));
++      return NULL;
++    }
++
++  g_kdbus_free_data (worker, cmd->offset);
++
++#ifdef LIBDBUSPOLICY
++  dbuspolicy1_init_set_pool (worker->dbuspolicy, worker->kdbus_buffer);
++#endif
++
++  return worker->unique_name;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_RequestName:
++ *
++ * Synchronously acquires name on the bus and returns status code
++ * from the GBusRequestNameReplyFlags enumeration.
++ *
++ * Returns: status code or G_BUS_REQUEST_NAME_FLAGS_ERROR
++ * if error is set.
++ */
++GBusRequestNameReplyFlags
++_g_kdbus_RequestName (GKDBusWorker        *worker,
++                      const gchar         *name,
++                      GBusNameOwnerFlags   flags,
++                      GError             **error)
++{
++  GBusRequestNameReplyFlags status;
++  struct kdbus_cmd *cmd;
++  guint64 kdbus_flags;
++  gsize len, size;
++  gint ret;
++
++  status = G_BUS_REQUEST_NAME_FLAGS_PRIMARY_OWNER;
++
++  if (!g_dbus_is_name (name))
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_INVALID_ARGS,
++                   "Given bus name \"%s\" is not valid", name);
++      return G_BUS_REQUEST_NAME_FLAGS_ERROR;
++    }
++
++  if (g_strcmp0 (name, "org.freedesktop.DBus") == 0)
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_INVALID_ARGS,
++                   "Cannot acquire a service named '%s', because that is reserved", name);
++      return G_BUS_REQUEST_NAME_FLAGS_ERROR;
++    }
++
++  if (*name == ':')
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_INVALID_ARGS,
++                   "Cannot acquire a service starting with ':' such as \"%s\"", name);
++      return G_BUS_REQUEST_NAME_FLAGS_ERROR;
++    }
++
++#ifdef LIBDBUSPOLICY
++  if (worker->dbuspolicy != NULL)
++    {
++      if (dbuspolicy1_can_own (worker->dbuspolicy, name) != 1)
++        {
++          g_set_error (error,
++                       G_DBUS_ERROR,
++                       G_DBUS_ERROR_ACCESS_DENIED,
++                       "Connection \"%s\" is not allowed to own the "
++                       "service \"%s\" due to security policies", worker->unique_name, name);
++          return G_BUS_REQUEST_NAME_FLAGS_ERROR;
++        }
++    }
++#endif
++
++  g_kdbus_translate_nameowner_flags (flags, &kdbus_flags);
++
++  len = strlen(name) + 1;
++  size = (gulong) G_STRUCT_OFFSET (struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(len);
++  cmd = g_alloca0 (size);
++  cmd->size = size;
++  cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + len;
++  cmd->items[0].type = KDBUS_ITEM_NAME;
++  cmd->flags = kdbus_flags;
++  memcpy (cmd->items[0].str, name, len);
++
++  ret = ioctl(worker->fd, KDBUS_CMD_NAME_ACQUIRE, cmd);
++  if (ret < 0)
++    {
++      if (errno == EEXIST)
++        status = G_BUS_REQUEST_NAME_FLAGS_EXISTS;
++      else if (errno == EALREADY)
++        status = G_BUS_REQUEST_NAME_FLAGS_ALREADY_OWNER;
++      else
++        {
++          g_set_error (error, G_IO_ERROR,
++                       g_io_error_from_errno (errno),
++                       _("Error while acquiring name: %s"),
++                       g_strerror (errno));
++          return G_BUS_REQUEST_NAME_FLAGS_ERROR;
++        }
++    }
++  else if ((cmd->return_flags & KDBUS_NAME_PRIMARY)
++      && !(cmd->return_flags & KDBUS_NAME_ACQUIRED))
++    status = G_BUS_REQUEST_NAME_FLAGS_ALREADY_OWNER;
++
++  if (cmd->return_flags & KDBUS_NAME_IN_QUEUE)
++    status = G_BUS_REQUEST_NAME_FLAGS_IN_QUEUE;
++
++  return status;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_ReleaseName:
++ *
++ * Synchronously releases name on the bus and returns status code
++ * from the GBusReleaseNameReplyFlags enumeration.
++ *
++ * Returns: status code or G_BUS_RELEASE_NAME_FLAGS_ERROR
++ * if error is set.
++ */
++GBusReleaseNameReplyFlags
++_g_kdbus_ReleaseName (GKDBusWorker  *worker,
++                      const gchar   *name,
++                      GError       **error)
++{
++  GBusReleaseNameReplyFlags status;
++  struct kdbus_cmd *cmd;
++  gsize len, size;
++  gint ret;
++
++  status = G_BUS_RELEASE_NAME_FLAGS_RELEASED;
++
++  if (!g_dbus_is_name (name))
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_INVALID_ARGS,
++                   "Given bus name \"%s\" is not valid", name);
++      return G_BUS_RELEASE_NAME_FLAGS_ERROR;
++    }
++
++  if (g_strcmp0 (name, "org.freedesktop.DBus") == 0)
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_INVALID_ARGS,
++                   "Cannot release a service named '%s', because that is owned by the bus", name);
++      return G_BUS_RELEASE_NAME_FLAGS_ERROR;
++    }
++
++  if (*name == ':')
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_INVALID_ARGS,
++                   "Cannot release a service starting with ':' such as \"%s\"", name);
++      return G_BUS_RELEASE_NAME_FLAGS_ERROR;
++    }
++
++  len = strlen(name) + 1;
++  size = (gulong) G_STRUCT_OFFSET (struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(len);
++  cmd = g_alloca0 (size);
++  cmd->size = size;
++  cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + len;
++  cmd->items[0].type = KDBUS_ITEM_NAME;
++  memcpy (cmd->items[0].str, name, len);
++
++  ret = ioctl(worker->fd, KDBUS_CMD_NAME_RELEASE, cmd);
++  if (ret < 0)
++    {
++      if (errno == ESRCH)
++        status = G_BUS_RELEASE_NAME_FLAGS_NON_EXISTENT;
++      else if (errno == EADDRINUSE)
++        status = G_BUS_RELEASE_NAME_FLAGS_NOT_OWNER;
++      else
++        {
++          g_set_error (error, G_IO_ERROR,
++                       g_io_error_from_errno (errno),
++                       _("Error while releasing name: %s"),
++                       g_strerror (errno));
++          return G_BUS_RELEASE_NAME_FLAGS_ERROR;
++        }
++    }
++
++  return status;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_GetBusId:
++ *
++ * Synchronously returns the unique ID of the bus.
++ *
++ * Returns: the unique ID of the bus or NULL if error is set.
++ * Free with g_free().
++ */
++gchar *
++_g_kdbus_GetBusId (GKDBusWorker  *worker,
++                   GError       **error)
++{
++  GString  *result;
++  guint     cnt;
++
++  result = g_string_new (NULL);
++
++  for (cnt=0; cnt<16; cnt++)
++    g_string_append_printf (result, "%02x", worker->bus_id[cnt]);
++
++  return g_string_free (result, FALSE);
++}
++
++
++static void
++expand_strv (gchar  ***strv_ptr,
++             gchar    *value)
++{
++  gchar **strv;
++  guint strv_len;
++
++  if (!value)
++    return;
++
++  strv = *strv_ptr;
++  strv_len = g_strv_length (strv);
++
++  strv = g_renew (gchar *, strv, strv_len + 2);
++  strv[strv_len] = value;
++  strv[strv_len + 1] = NULL;
++
++  *strv_ptr = strv;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_GetListNames:
++ *
++ * Synchronously returns a list of:
++ *   - all currently-owned names on the bus (activatable = FALSE),
++ *   - all names that can be activated on the bus (activatable = TRUE),
++ *
++ * or NULL if error is set. Free with g_strfreev().
++ */
++gchar **
++_g_kdbus_GetListNames (GKDBusWorker  *worker,
++                       gboolean       activatable,
++                       GError       **error)
++{
++  struct kdbus_info *name_list, *name;
++  struct kdbus_cmd_list cmd = {
++    .size = sizeof(cmd)
++  };
++
++  gchar **listnames;
++  guint64 prev_id;
++  gint ret;
++
++  prev_id = 0;
++
++  if (activatable)
++    cmd.flags = KDBUS_LIST_ACTIVATORS;                /* ListActivatableNames */
++  else
++    cmd.flags = KDBUS_LIST_UNIQUE | KDBUS_LIST_NAMES; /* ListNames */
++
++  ret = ioctl(worker->fd, KDBUS_CMD_LIST, &cmd);
++  if (ret < 0)
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_FAILED,
++                   _("Error listing names"));
++      return NULL;
++    }
++
++  listnames = g_new0 (gchar *, 1);
++  name_list = (struct kdbus_info *) ((guint8 *) worker->kdbus_buffer + cmd.offset);
++
++  KDBUS_FOREACH (name, name_list, cmd.list_size)
++    {
++      struct kdbus_item *item;
++
++      if ((cmd.flags & KDBUS_LIST_UNIQUE) && name->id != prev_id)
++        {
++          gchar *unique_name;
++
++          if (asprintf (&unique_name, ":1.%llu", name->id) < 0)
++            {
++              g_set_error (error,
++                           G_IO_ERROR,
++                           G_IO_ERROR_UNKNOWN,
++                           "asprintf error: %s",
++                           g_strerror(errno));
++              goto error;
++            }
++
++          expand_strv (&listnames, unique_name);
++          prev_id = name->id;
++        }
++
++       KDBUS_ITEM_FOREACH (item, name, items)
++         {
++           if (item->type == KDBUS_ITEM_OWNED_NAME)
++             {
++               if (g_dbus_is_name (item->name.name))
++                 expand_strv (&listnames, g_strdup (item->name.name));
++             }
++         }
++    }
++
++  /* org.freedesktop.DBus.ListNames */
++  if (!activatable)
++    expand_strv (&listnames, g_strdup ("org.freedesktop.DBus"));
++
++  g_kdbus_free_data (worker, cmd.offset);
++
++  return listnames;
++
++error:
++  g_strfreev(listnames);
++  return NULL;
++}
++
++
++static gboolean
++g_kdbus_NameHasOwner_internal (GKDBusWorker  *worker,
++                               const gchar   *name)
++{
++  struct kdbus_cmd_info *cmd;
++  struct kdbus_info *conn_info;
++  gsize size, len;
++  gint ret;
++
++  if (g_dbus_is_unique_name(name))
++    {
++       size = (gulong) G_STRUCT_OFFSET (struct kdbus_cmd_info, items);
++       cmd = g_alloca0 (size);
++       cmd->id = g_ascii_strtoull (name+3, NULL, 10);
++    }
++  else
++    {
++       len = strlen(name) + 1;
++       size = (gulong) G_STRUCT_OFFSET (struct kdbus_cmd_info, items) + KDBUS_ITEM_SIZE(len);
++       cmd = g_alloca0 (size);
++       cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + len;
++       cmd->items[0].type = KDBUS_ITEM_NAME;
++       memcpy (cmd->items[0].str, name, len);
++    }
++  cmd->size = size;
++
++  ret = ioctl(worker->fd, KDBUS_CMD_CONN_INFO, cmd);
++  if (ret < 0)
++    return FALSE;
++
++  conn_info = (struct kdbus_info *) ((guint8 *) worker->kdbus_buffer + cmd->offset);
++
++  if (conn_info->flags & KDBUS_HELLO_ACTIVATOR)
++    ret = -1;
++
++  g_kdbus_free_data (worker, cmd->offset);
++
++  if (ret < 0)
++    return FALSE;
++  else
++    return TRUE;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_GetListQueuedOwners:
++ *
++ * Synchronously returns the unique bus names of connections currently
++ * queued for the name.
++ *
++ * Returns: the unique bus names of connections currently queued for the
++ * 'name' or NULL if error is set. Free with g_strfreev().
++ */
++gchar **
++_g_kdbus_GetListQueuedOwners (GKDBusWorker  *worker,
++                              const gchar   *name,
++                              GError       **error)
++{
++  struct kdbus_info *name_list, *kname;
++  struct kdbus_cmd_list cmd = {
++    .size = sizeof(cmd),
++    .flags = KDBUS_LIST_QUEUED
++  };
++
++  gchar **queued_owners;
++  gint ret;
++
++  if (!g_dbus_is_name (name))
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_INVALID_ARGS,
++                   "Given bus name \"%s\" is not valid", name);
++      return NULL;
++    }
++
++  if (!g_kdbus_NameHasOwner_internal (worker, name))
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_NAME_HAS_NO_OWNER,
++                   "Could not get owner of name '%s': no such name", name);
++      return NULL;
++    }
++
++  ret = ioctl(worker->fd, KDBUS_CMD_LIST, &cmd);
++  if (ret < 0)
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_FAILED,
++                   _("Error listing names"));
++      return NULL;
++    }
++
++  queued_owners = g_new0 (gchar *, 1);
++  name_list = (struct kdbus_info *) ((guint8 *) worker->kdbus_buffer + cmd.offset);
++
++  KDBUS_FOREACH(kname, name_list, cmd.list_size)
++    {
++      struct kdbus_item *item;
++
++      KDBUS_ITEM_FOREACH(item, kname, items)
++        {
++          if (item->type == KDBUS_ITEM_OWNED_NAME)
++            {
++              gchar *unique_name;
++
++              if (strcmp(item->name.name, name))
++                continue;
++
++              if (asprintf (&unique_name, ":1.%llu", kname->id) != -1)
++                expand_strv (&queued_owners, unique_name);
++            }
++        }
++    }
++
++  g_kdbus_free_data (worker, cmd.offset);
++
++  return queued_owners;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_NameHasOwner:
++ *
++ * Checks if the specified name exists (currently has an owner).
++ *
++ * Returns: TRUE if the name exists and FALSE when name doesn't exists
++ * or error is set.
++ */
++gboolean
++_g_kdbus_NameHasOwner (GKDBusWorker  *worker,
++                       const gchar   *name,
++                       GError       **error)
++{
++  if (!g_dbus_is_name (name))
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_INVALID_ARGS,
++                   "Given bus name \"%s\" is not valid", name);
++      return FALSE;
++    }
++
++  if (g_strcmp0 (name, "org.freedesktop.DBus") == 0)
++    return  TRUE;
++
++  if (!g_kdbus_NameHasOwner_internal (worker, name))
++    return FALSE; /* Don't make g_set_error, otherwise NameHasOwner will fail. */
++  else
++    return TRUE;
++}
++
++
++GDBusCredentials *
++_g_kdbus_GetConnInfo (GKDBusWorker  *worker,
++                      const gchar   *name,
++                      guint          flags,
++                      GError       **error)
++{
++  GDBusCredentials *creds;
++  struct kdbus_cmd_info *cmd;
++  struct kdbus_info *conn_info;
++  struct kdbus_item *item;
++  gsize size, len;
++  gint ret;
++
++  creds = g_new0 (GDBusCredentials, 1);
++
++  if (!g_dbus_is_name (name))
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_INVALID_ARGS,
++                   "Given bus name \"%s\" is not valid", name);
++      goto error;
++    }
++
++  if (!g_kdbus_NameHasOwner_internal (worker, name))
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_NAME_HAS_NO_OWNER,
++                   "Could not get owner of name '%s': no such name", name);
++      goto error;
++    }
++
++  if (g_dbus_is_unique_name(name))
++    {
++       size = (gulong) G_STRUCT_OFFSET (struct kdbus_cmd_info, items);
++       cmd = g_alloca0 (size);
++       cmd->id = g_ascii_strtoull (name+3, NULL, 10);
++    }
++  else
++    {
++       len = strlen(name) + 1;
++       size = (gulong) G_STRUCT_OFFSET (struct kdbus_cmd_info, items) + KDBUS_ITEM_SIZE(len);
++       cmd = g_alloca0 (size);
++       cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + len;
++       cmd->items[0].type = KDBUS_ITEM_NAME;
++       memcpy (cmd->items[0].str, name, len);
++    }
++
++  cmd->attach_flags = _KDBUS_ATTACH_ALL;
++  cmd->size = size;
++
++  ret = ioctl(worker->fd, KDBUS_CMD_CONN_INFO, cmd);
++  if (ret < 0)
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_FAILED,
++                   _("Could not get connection info"));
++      goto error;
++    }
++
++  conn_info = (struct kdbus_info *) ((guint8 *) worker->kdbus_buffer + cmd->offset);
++
++  if (flags & G_DBUS_CREDS_UNIQUE_NAME)
++    {
++       if (asprintf (&creds->unique_name, ":1.%llu", (unsigned long long) conn_info->id) < 0)
++         {
++           g_set_error (error,
++                        G_DBUS_ERROR,
++                        G_DBUS_ERROR_FAILED,
++                        "asprintf error: %s",
++                        g_strerror(errno));
++           goto error;
++         }
++    }
++
++  KDBUS_ITEM_FOREACH(item, conn_info, items)
++   {
++      switch (item->type)
++        {
++          case KDBUS_ITEM_PIDS:
++            if (flags & G_DBUS_CREDS_PID)
++              creds->pid = item->pids.pid;
++          break;
++
++          case KDBUS_ITEM_CREDS:
++            if (flags & G_DBUS_CREDS_UID)
++              creds->uid = item->creds.uid;
++          break;
++
++          case KDBUS_ITEM_SECLABEL:
++            if (flags & G_DBUS_CREDS_SEC_LABEL)
++              creds->sec_label = g_strdup (item->str);
++          break;
++
++          case KDBUS_ITEM_PID_COMM:
++          case KDBUS_ITEM_TID_COMM:
++          case KDBUS_ITEM_EXE:
++          case KDBUS_ITEM_CMDLINE:
++          case KDBUS_ITEM_CGROUP:
++          case KDBUS_ITEM_CAPS:
++          case KDBUS_ITEM_AUDIT:
++          case KDBUS_ITEM_CONN_DESCRIPTION:
++          case KDBUS_ITEM_AUXGROUPS:
++          case KDBUS_ITEM_OWNED_NAME:
++            break;
++        }
++   }
++
++  g_kdbus_free_data (worker, cmd->offset);
++  return creds;
++
++error:
++  g_free (creds->unique_name);
++  g_free (creds->sec_label);
++  g_free (creds);
++  return NULL;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_GetNameOwner:
++ *
++ * Synchronously returns the unique connection name of the primary owner of
++ * the name given. If the requested name doesn't have an owner, an error is
++ * returned.
++ *
++ * Returns: the unique connection name of the primary owner of the
++ * name given. If the requested name doesn't have an owner, function
++ * returns NULL and error is set. Free with g_free().
++ */
++gchar *
++_g_kdbus_GetNameOwner (GKDBusWorker  *worker,
++                       const gchar   *name,
++                       GError       **error)
++{
++  GDBusCredentials *creds;
++  gchar *unique_name;
++  guint flags;
++
++  creds = NULL;
++  unique_name = NULL;
++
++  if (g_strcmp0 (name, "org.freedesktop.DBus") == 0)
++    return g_strdup (name);
++
++  flags = G_DBUS_CREDS_UNIQUE_NAME;
++  creds = _g_kdbus_GetConnInfo (worker,
++                                name,
++                                flags,
++                                error);
++  if (creds != NULL)
++    {
++      unique_name = creds->unique_name;
++      g_free (creds);
++    }
++
++  return unique_name;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_GetConnectionUnixProcessID:
++ *
++ * Synchronously returns the Unix process ID of the process connected to the
++ * bus. If unable to determine it, an error is returned.
++ *
++ * If name contains a value not compatible with the D-Bus syntax and naming
++ * conventions for bus names, the operation returns -1 and error is set.
++ *
++ * Returns: the Unix process ID of the process connected to the bus or -1
++ * if error is set.
++ */
++pid_t
++_g_kdbus_GetConnectionUnixProcessID (GKDBusWorker  *worker,
++                                     const gchar   *name,
++                                     GError       **error)
++{
++  GDBusCredentials *creds;
++  guint flags;
++  pid_t pid;
++
++  creds = NULL;
++  pid = -1;
++
++  flags = G_DBUS_CREDS_PID;
++  creds = _g_kdbus_GetConnInfo (worker,
++                                name,
++                                flags,
++                                error);
++  if (creds != NULL)
++    {
++      pid = creds->pid;
++      g_free (creds);
++    }
++
++  return pid;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_GetConnectionUnixUser:
++ *
++ * Synchronously returns the Unix user ID of the process connected to the
++ * bus. If unable to determine it, an error is returned.
++ *
++ * If name contains a value not compatible with the D-Bus syntax and naming
++ * conventions for bus names, the operation returns -1 and error is set.
++ *
++ * Returns: the Unix user ID of the process connected to the bus or -1
++ * if error is set.
++ */
++uid_t
++_g_kdbus_GetConnectionUnixUser (GKDBusWorker  *worker,
++                                const gchar   *name,
++                                GError       **error)
++{
++  GDBusCredentials *creds;
++  guint flags;
++  uid_t uid;
++
++  creds = NULL;
++  uid = -1;
++
++  flags = G_DBUS_CREDS_UID;
++  creds = _g_kdbus_GetConnInfo (worker,
++                                name,
++                                flags,
++                                error);
++  if (creds != NULL)
++    {
++      uid = creds->uid;
++      g_free (creds);
++    }
++
++  return uid;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_GetConnectionSecurityLabel:
++ *
++ * Synchronously returns security label of the process connected to the bus.
++ *
++ * If name contains a value not compatible with the D-Bus syntax and naming
++ * conventions for bus names, the operation returns -1 and error is set.
++ *
++ * Returns: security label of the process connected to the bus or -1
++ */
++gchar *
++_g_kdbus_GetConnectionSecurityLabel (GKDBusWorker  *worker,
++                                     const gchar   *name,
++                                     GError       **error)
++{
++  GDBusCredentials *creds;
++  gchar *sec_label;
++  guint flags;
++
++  creds = NULL;
++  sec_label = NULL;
++
++  flags = G_DBUS_CREDS_SEC_LABEL;
++  creds = _g_kdbus_GetConnInfo (worker,
++                                name,
++                                flags,
++                                error);
++  if (creds != NULL)
++    {
++      sec_label = creds->sec_label;
++      g_free (creds);
++    }
++
++  return sec_label;
++}
++
++typedef struct
++{
++  GKDBusWorker  *worker;
++  GDBusMessage  *message;
++} SyntheticReplyData;
++
++static gboolean
++deliver_synthetic_reply (gpointer user_data)
++{
++  SyntheticReplyData *data;
++  GKDBusWorker *worker;
++  GDBusMessage *message;
++
++  data = user_data;
++  worker = data->worker;
++  message = data->message;
++
++  (* worker->message_received_callback) (message, worker->user_data);
++
++  g_object_unref (message);
++  g_free (data);
++
++  return FALSE;
++}
++
++static void
++send_synthetic_message (GKDBusWorker *worker,
++                    GDBusMessage *message)
++{
++  SyntheticReplyData *reply_data;
++  reply_data = g_new0 (SyntheticReplyData, 1);
++  reply_data->worker = worker;
++  reply_data->message = message;
++  g_main_context_invoke (worker->context, deliver_synthetic_reply, reply_data);
++}
++
++static GBusStartServiceReplyFlags
++_g_kdbus_send_Ping (GKDBusWorker *worker,
++                    const gchar  *name,
++                    GError      **error)
++{
++  GDBusMessage *ping_message;
++  GDBusMessage *reply = NULL;
++  gboolean ret;
++
++  ping_message = g_dbus_message_new_method_call (name, "/", "org.freedesktop.DBus.Peer", "Ping");
++  g_dbus_message_set_serial (ping_message, -1);
++
++  ret = _g_kdbus_send (worker, ping_message, &reply, 25000, NULL, NULL);
++  g_object_unref (ping_message);
++  if (reply)
++    g_object_unref (reply);
++
++  if (!ret)
++    {
++      g_set_error (error,
++                  G_DBUS_ERROR,
++                  G_DBUS_ERROR_SERVICE_UNKNOWN,
++                  "The name %s was not provided by any .service files", name);
++      return G_BUS_START_SERVICE_REPLY_ERROR;
++    }
++  return G_BUS_START_SERVICE_REPLY_SUCCESS;
++}
++
++static void
++_g_kdbus_send_Ping_thread (GTask        *task,
++                           gpointer      source_object,
++                           gpointer      task_data,
++                           GCancellable *cancellable)
++{
++  GKDBusWorker *worker = source_object;
++  GDBusMessage *message = task_data;
++  const gchar *name = NULL;
++  GError *error = NULL;
++
++  GBusStartServiceReplyFlags status;
++
++  g_variant_get (g_dbus_message_get_body (message), "(su)", &name, NULL);
++
++  status = _g_kdbus_send_Ping (worker, name, &error);
++  g_free ((gchar*) name);
++
++  if (status == G_BUS_START_SERVICE_REPLY_SUCCESS)
++    g_task_return_int (task, G_BUS_START_SERVICE_REPLY_SUCCESS);
++  else
++    g_task_return_error (task, error);
++
++}
++
++static void
++_g_kdbus_send_Ping_finish (GKDBusWorker *worker,
++                           GAsyncResult *result,
++                           gpointer user_data)
++{
++  GDBusMessage *message = user_data;
++  GDBusMessage *synthetic_reply = NULL;
++  GError *error = NULL;
++  gssize status = g_task_propagate_int (G_TASK (result), &error);
++  if (status != -1)
++    {
++      synthetic_reply = g_dbus_message_new_method_reply (message);
++      g_dbus_message_set_body (synthetic_reply, g_variant_new ("(u)", status));
++    }
++  else
++    {
++      gchar *dbus_error_name = g_dbus_error_encode_gerror (error);
++      synthetic_reply = g_dbus_message_new_method_error (message, dbus_error_name, "%s", error->message);
++      g_free (dbus_error_name);
++      g_error_free (error);
++    }
++  g_object_unref (message);
++  g_dbus_message_set_serial (synthetic_reply, -1);
++  send_synthetic_message (worker, synthetic_reply);
++}
++
++/* < internal >
++ *
++ * _g_kdbus_StartServiceByName:
++ *
++ * Synchronously tries to launch the executable associated
++ * with a name.
++ *
++ * Returns: status code or G_BUS_START_SERVICE_REPLY_ERROR
++   if error is set.
++ */
++GBusStartServiceReplyFlags
++_g_kdbus_StartServiceByName (GKDBusWorker  *worker,
++                             const gchar   *name,
++                             guint32        flags,
++                             GDBusMessage  *message,
++                             GError       **error)
++{
++  if (!g_dbus_is_name (name))
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_INVALID_ARGS,
++                   "Given bus name \"%s\" is not valid", name);
++      return G_BUS_START_SERVICE_REPLY_ERROR;
++    }
++
++  if (g_strcmp0 (name, "org.freedesktop.DBus") == 0)
++    return G_BUS_START_SERVICE_REPLY_ALREADY_RUNNING;
++
++  if (!g_kdbus_NameHasOwner_internal (worker, name))
++    {
++      if (!message)
++        {
++            return _g_kdbus_send_Ping(worker, name, error);
++        }
++      else
++        {
++          GTask *task;
++          task = g_task_new (worker,
++                             NULL,
++                             (GAsyncReadyCallback) _g_kdbus_send_Ping_finish,
++                             g_object_ref(message));
++          g_task_set_task_data (task, message, NULL);
++          g_task_run_in_thread (task, _g_kdbus_send_Ping_thread);
++          g_object_unref (task);
++          return G_BUS_START_SERVICE_REPLY_SUCCESS;
++        }
++    }
++  else
++    {
++      return G_BUS_START_SERVICE_REPLY_ALREADY_RUNNING;
++    }
++}
++
++
++/*
++ * g_kdbus_bloom_add_data:
++ * Based on bus-bloom.c from systemd
++ * http://cgit.freedesktop.org/systemd/systemd/tree/src/libsystemd/sd-bus/bus-bloom.c
++ */
++static void
++g_kdbus_bloom_add_data (GKDBusWorker  *worker,
++                        guint64        bloom_data[],
++                        const void    *data,
++                        gsize          n)
++{
++  guint8 hash[8];
++  guint64 bit_num;
++  guint bytes_num = 0;
++  guint cnt_1, cnt_2;
++  guint hash_index = 0;
++
++  guint c = 0;
++
++  bit_num = (guint64)worker->bloom_size * 8;
++
++  if (bit_num > 1)
++    bytes_num = ((__builtin_clzll(bit_num) ^ 63U) + 7) / 8;
++
++  for (cnt_1 = 0, hash_index = 0; cnt_1 < (worker->bloom_n_hash); cnt_1++)
++    {
++      guint64 p = 0;
++      for (cnt_2 = 0; cnt_2 < bytes_num; cnt_2++)
++        {
++          if (c <= 0)
++            {
++              _g_siphash24(hash, data, n, hash_keys[hash_index++]);
++              c += 8;
++            }
++
++          p = (p << 8ULL) | (guint64) hash[8 - c];
++          c--;
++        }
++
++      p &= bit_num - 1;
++      bloom_data[p >> 6] |= 1ULL << (p & 63);
++    }
++}
++
++static void
++g_kdbus_bloom_add_pair (GKDBusWorker  *worker,
++                        guint64        bloom_data[],
++                        const gchar   *parameter,
++                        const gchar   *value)
++{
++  gchar buf[1024];
++  gsize size;
++
++  size = strlen(parameter) + strlen(value) + 1;
++  if (size >= 1024)
++    return;
++
++  strcpy(stpcpy(stpcpy(buf, parameter), ":"), value);
++  g_kdbus_bloom_add_data(worker, bloom_data, buf, size);
++}
++
++static void
++g_kdbus_bloom_add_prefixes (GKDBusWorker  *worker,
++                            guint64        bloom_data[],
++                            const gchar   *parameter,
++                            const gchar   *value,
++                            gchar          separator)
++{
++  gchar buf[1024];
++  gsize size;
++
++  size = strlen(parameter) + strlen(value) + 1;
++  if (size >= 1024)
++    return;
++
++  strcpy(stpcpy(stpcpy(buf, parameter), ":"), value);
++
++  for (;;)
++    {
++      gchar *last_sep;
++      last_sep = strrchr(buf, separator);
++      if (!last_sep || last_sep == buf)
++        break;
++
++      *last_sep = 0;
++      g_kdbus_bloom_add_data(worker, bloom_data, buf, last_sep-buf);
++    }
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_AddMatch:
++ *
++ * Synchronously adds a match rule to match messages.
++ *
++ * Returns: TRUE if the operation succeeded, FALSE
++ * if error is set.
++ *
++ */
++gboolean
++_g_kdbus_AddMatch (GKDBusWorker  *worker,
++                   const gchar   *match_rule,
++                   GError       **error)
++{
++  Match *match;
++  MatchElement *element;
++  const gchar *sender_name;
++  gsize sender_len, size;
++  struct kdbus_cmd_match *cmd;
++  struct kdbus_item *item;
++  guint64 *bloom;
++  guint64 src_id;
++  gchar *type;
++  gint cnt, ret;
++
++  if (match_rule[0] == '-')
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_MATCH_RULE_INVALID,
++                   "Invalid rule: %s", match_rule);
++      return FALSE;
++    }
++
++  match = match_new (match_rule);
++  if (!match)
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_MATCH_RULE_INVALID,
++                   "Invalid rule: %s", match_rule);
++      return FALSE;
++    }
++
++  sender_name = NULL;
++  src_id = KDBUS_MATCH_ID_ANY;
++
++  bloom = g_alloca0 (worker->bloom_size);
++  size = KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_cmd_match, items));
++
++  for (cnt = 0; cnt < match->n_elements; cnt++)
++    {
++      element = &match->elements[cnt];
++      switch (element->type)
++        {
++          case MATCH_ELEMENT_SENDER:
++            if (g_dbus_is_unique_name(element->value))
++              {
++                src_id = g_ascii_strtoull ((element->value)+3, NULL, 10);
++                size += KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, id) + sizeof(src_id));
++              }
++            else if (g_dbus_is_name (element->value))
++              {
++                sender_name = element->value;
++                sender_len = strlen(element->value) + 1;
++                size += KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, str) + sender_len);
++              }
++            else
++              {
++                g_set_error (error,
++                             G_DBUS_ERROR,
++                             G_DBUS_ERROR_MATCH_RULE_INVALID,
++                             "Invalid rule: %s", match_rule);
++                match_free (match);
++                return FALSE;
++              }
++            break;
++
++          case MATCH_ELEMENT_TYPE:
++            g_kdbus_bloom_add_pair (worker, bloom, "message-type", element->value);
++            break;
++
++          case MATCH_ELEMENT_INTERFACE:
++            g_kdbus_bloom_add_pair (worker, bloom, "interface", element->value);
++            break;
++
++          case MATCH_ELEMENT_MEMBER:
++            g_kdbus_bloom_add_pair (worker, bloom, "member", element->value);
++            break;
++
++          case MATCH_ELEMENT_PATH:
++            g_kdbus_bloom_add_pair (worker, bloom, "path", element->value);
++            break;
++
++          case MATCH_ELEMENT_PATH_NAMESPACE:
++            if (g_strcmp0 (element->value, "/"))
++              g_kdbus_bloom_add_pair (worker, bloom, "path-slash-prefix", element->value);
++            break;
++
++          case MATCH_ELEMENT_ARGN:
++            if (asprintf (&type, "arg%u", element->arg) < 0)
++              {
++                 g_set_error (error,
++                              G_DBUS_ERROR,
++                              G_DBUS_ERROR_FAILED,
++                              "asprintf failed: %s", g_strerror(errno));
++                 match_free (match);
++                 return FALSE;
++              }
++            else
++              {
++                g_kdbus_bloom_add_pair (worker, bloom, type, element->value);
++                free (type);
++              }
++            break;
++
++          case MATCH_ELEMENT_ARGNPATH:
++            if (asprintf (&type, "arg%u-slash-prefix", element->arg) < 0)
++              {
++                 g_set_error (error,
++                              G_DBUS_ERROR,
++                              G_DBUS_ERROR_FAILED,
++                              "asprintf failed: %s", g_strerror(errno));
++                 match_free (match);
++                 return FALSE;
++              }
++            else
++              {
++                g_kdbus_bloom_add_pair (worker, bloom, type, element->value);
++                free (type);
++              }
++            break;
++
++          case MATCH_ELEMENT_ARG0NAMESPACE:
++            g_kdbus_bloom_add_pair (worker, bloom, "arg0-dot-prefix", element->value);
++            break;
++
++          case MATCH_ELEMENT_DESTINATION:
++          case MATCH_ELEMENT_EAVESDROP:
++            break;
++        }
++    }
++
++  size += KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, data64) + worker->bloom_size);
++  cmd = g_alloca0 (size);
++  cmd->size = size;
++  cmd->cookie = match->cookie;
++
++  item = cmd->items;
++  item->size = (guint64) G_STRUCT_OFFSET(struct kdbus_item, data64) + worker->bloom_size;
++  item->type = KDBUS_ITEM_BLOOM_MASK;
++  memcpy(item->data64, bloom, worker->bloom_size);
++  item = KDBUS_ITEM_NEXT(item);
++
++  if (src_id != KDBUS_MATCH_ID_ANY)
++    {
++      item->size = (guint64) G_STRUCT_OFFSET (struct kdbus_item, id) + sizeof(src_id);
++      item->type = KDBUS_ITEM_ID;
++      item->id = src_id;
++      item = KDBUS_ITEM_NEXT(item);
++    }
++
++  if (sender_name)
++    {
++      item->size = (guint64) G_STRUCT_OFFSET (struct kdbus_item, str) + sender_len;
++      item->type = KDBUS_ITEM_NAME;
++      memcpy (item->str, sender_name, sender_len);
++    }
++
++  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
++  if (ret < 0)
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_FAILED,
++                   "Error while adding a match");
++      match_free (match);
++      return FALSE;
++    }
++
++  g_mutex_lock (&worker->matches_mutex);
++  worker->matches = g_list_prepend (worker->matches, match);
++  g_mutex_unlock (&worker->matches_mutex);
++  return TRUE;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_RemoveMatch:
++ *
++ * Synchronously removes the first rule that matches.
++ *
++ * Returns: TRUE if the operation succeeded, FALSE
++ * if error is set.
++ */
++gboolean
++_g_kdbus_RemoveMatch (GKDBusWorker  *worker,
++                      const gchar   *match_rule,
++                      GError       **error)
++{
++  Match *match, *other_match;
++  GList *matches;
++  guint64 cookie;
++
++  if (match_rule[0] == '-')
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_MATCH_RULE_INVALID,
++                   "Invalid rule: %s", match_rule);
++      return FALSE;
++    }
++
++  match = match_new (match_rule);
++  if (!match)
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_MATCH_RULE_INVALID,
++                   "Invalid rule: %s", match_rule);
++      return FALSE;
++    }
++
++  g_mutex_lock (&worker->matches_mutex);
++  for (matches = worker->matches; matches != NULL; matches = matches->next)
++    {
++      other_match = matches->data;
++      if (match_equal (match, other_match))
++        {
++          cookie = other_match->cookie;
++          match_free (other_match);
++          worker->matches = g_list_delete_link (worker->matches, matches);
++          break;
++        }
++    }
++  g_mutex_unlock (&worker->matches_mutex);
++
++  if (matches == NULL)
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_MATCH_RULE_NOT_FOUND,
++                   "The given match rule wasn't found and can't be removed");
++      match_free (match);
++      return FALSE;
++    }
++  else
++    {
++      struct kdbus_cmd_match cmd = {
++        .size = sizeof(cmd),
++        .cookie = cookie
++      };
++      gint ret;
++
++      ret = ioctl(worker->fd, KDBUS_CMD_MATCH_REMOVE, &cmd);
++      if (ret < 0)
++        {
++          if (errno == EBADSLT)
++            {
++              g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_MATCH_RULE_NOT_FOUND,
++                           "A match entry with the given cookie could not be found");
++            }
++          else
++            {
++              g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
++                           "Error while removing a match");
++            }
++          match_free (match);
++          return FALSE;
++        }
++    }
++
++  match_free (match);
++  return TRUE;
++}
++
++
++/*
++ * _g_kdbus_subscribe_name_owner_changed_internal
++ */
++static gboolean
++_g_kdbus_subscribe_name_owner_changed_internal (GKDBusWorker  *worker,
++                                                const gchar   *name,
++                                                const gchar   *old_name,
++                                                const gchar   *new_name,
++                                                guint64        cookie,
++                                                GError       **error)
++{
++  struct kdbus_item *item;
++  struct kdbus_cmd_match *cmd;
++  gsize size, len;
++  gint ret;
++  guint64 old_id = 0;
++  guint64 new_id = KDBUS_MATCH_ID_ANY;
++
++  if (name)
++    len = strlen(name) + 1;
++  else
++    len = 0;
++
++  size = KDBUS_ALIGN8(G_STRUCT_OFFSET (struct kdbus_cmd_match, items) +
++                      G_STRUCT_OFFSET (struct kdbus_item, name_change) +
++                      G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len);
++
++  cmd = g_alloca0 (size);
++  cmd->size = size;
++  cmd->cookie = cookie;
++  item = cmd->items;
++
++  if (old_name == NULL)
++    {
++      old_id = KDBUS_MATCH_ID_ANY;
++    }
++  else
++    {
++      if (g_dbus_is_unique_name(old_name))
++        old_id = strtoull (old_name + 3, NULL, 10);
++      else
++        return TRUE;
++    }
++
++  if (new_name == NULL)
++    {
++      new_id = KDBUS_MATCH_ID_ANY;
++    }
++  else
++    {
++      if (g_dbus_is_unique_name(new_name))
++        new_id = strtoull (new_name + 3, NULL, 10);
++      else
++        return TRUE;
++    }
++
++  cmd = g_alloca0 (size);
++  cmd->size = size;
++  cmd->cookie = cookie;
++  item = cmd->items;
++
++  item->type = KDBUS_ITEM_NAME_CHANGE;
++  item->name_change.old_id.id = old_id;
++  item->name_change.new_id.id = new_id;
++
++  if (name)
++    memcpy(item->name_change.name, name, len);
++
++  item->size = (guint64) G_STRUCT_OFFSET (struct kdbus_item, name_change) +
++               (guint64) G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len;
++  item = KDBUS_ITEM_NEXT(item);
++
++  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
++  if (ret < 0)
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_FAILED,
++                   "Error while adding a match");
++      return FALSE;
++    }
++
++  return TRUE;
++}
++
++
++/*
++ * _g_kdbus_subscribe_name_acquired
++ */
++gboolean
++_g_kdbus_subscribe_name_acquired (GKDBusWorker  *worker,
++                                  const gchar   *match_rule,
++                                  const gchar   *name,
++                                  GError       **error)
++{
++  Match *match;
++  struct kdbus_item *item;
++  struct kdbus_cmd_match *cmd;
++  gsize size, len;
++  gint ret;
++
++  if (match_rule[0] == '-')
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_MATCH_RULE_INVALID,
++                   "Invalid rule: %s", match_rule);
++      return FALSE;
++    }
++
++  if (name != NULL)
++    {
++      if (!g_dbus_is_name (name))
++        {
++          g_set_error (error,
++                       G_DBUS_ERROR,
++                       G_DBUS_ERROR_MATCH_RULE_INVALID,
++                       "Invalid rule: %s", match_rule);
++          return FALSE;
++        }
++    }
++
++  match = match_new (match_rule);
++  if (!match)
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_MATCH_RULE_INVALID,
++                   "Invalid rule: %s", match_rule);
++      return FALSE;
++    }
++
++  if (name)
++    len = strlen(name) + 1;
++  else
++    len = 0;
++
++  size = KDBUS_ALIGN8(G_STRUCT_OFFSET (struct kdbus_cmd_match, items) +
++                      G_STRUCT_OFFSET (struct kdbus_item, name_change) +
++                      G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len);
++
++  cmd = g_alloca0 (size);
++  cmd->size = size;
++  cmd->cookie = match->cookie;
++  item = cmd->items;
++
++  item->type = KDBUS_ITEM_NAME_ADD;
++  item->name_change.old_id.id = KDBUS_MATCH_ID_ANY;
++  item->name_change.new_id.id = worker->unique_id;
++
++  if (name)
++    memcpy(item->name_change.name, name, len);
++
++  item->size = (guint64) G_STRUCT_OFFSET (struct kdbus_item, name_change) +
++               (guint64) G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len;
++  item = KDBUS_ITEM_NEXT(item);
++
++  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
++  if (ret < 0)
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_FAILED,
++                   "Error while adding a match");
++      match_free (match);
++      return FALSE;
++    }
++
++  if (!_g_kdbus_subscribe_name_owner_changed_internal (worker, name, NULL, worker->unique_name, match->cookie, error))
++    {
++      match_free (match);
++      return FALSE;
++    }
++
++  g_mutex_lock (&worker->matches_mutex);
++  worker->matches = g_list_prepend (worker->matches, match);
++  g_mutex_unlock (&worker->matches_mutex);
++  return TRUE;
++}
++
++
++/*
++ * _g_kdbus_subscribe_name_lost
++ */
++gboolean
++_g_kdbus_subscribe_name_lost (GKDBusWorker  *worker,
++                              const gchar   *match_rule,
++                              const gchar   *name,
++                              GError       **error)
++{
++  Match *match;
++  struct kdbus_item *item;
++  struct kdbus_cmd_match *cmd;
++  gsize size, len;
++  gint ret;
++
++  if (match_rule[0] == '-')
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_MATCH_RULE_INVALID,
++                   "Invalid rule: %s", match_rule);
++      return FALSE;
++    }
++
++  if (name != NULL)
++    {
++      if (!g_dbus_is_name (name))
++        {
++          g_set_error (error,
++                       G_DBUS_ERROR,
++                       G_DBUS_ERROR_MATCH_RULE_INVALID,
++                       "Invalid rule: %s", match_rule);
++          return FALSE;
++        }
++    }
++
++  match = match_new (match_rule);
++  if (!match)
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_MATCH_RULE_INVALID,
++                   "Invalid rule: %s", match_rule);
++      return FALSE;
++    }
++
++  if (name)
++    len = strlen(name) + 1;
++  else
++    len = 0;
++
++  size = KDBUS_ALIGN8(G_STRUCT_OFFSET (struct kdbus_cmd_match, items) +
++                      G_STRUCT_OFFSET (struct kdbus_item, name_change) +
++                      G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len);
++
++  cmd = g_alloca0 (size);
++  cmd->size = size;
++  cmd->cookie = match->cookie;
++  item = cmd->items;
++
++  item->type = KDBUS_ITEM_NAME_REMOVE;
++  item->name_change.old_id.id = worker->unique_id;
++  item->name_change.new_id.id = KDBUS_MATCH_ID_ANY;
++
++  if (name)
++    memcpy(item->name_change.name, name, len);
++
++  item->size = (guint64) G_STRUCT_OFFSET (struct kdbus_item, name_change) +
++               (guint64) G_STRUCT_OFFSET(struct kdbus_notify_name_change, name) + len;
++  item = KDBUS_ITEM_NEXT(item);
++
++  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
++  if (ret < 0)
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_FAILED,
++                   "Error while adding a match");
++      match_free (match);
++      return FALSE;
++    }
++
++  if (!_g_kdbus_subscribe_name_owner_changed_internal (worker, name, worker->unique_name, NULL, match->cookie, error))
++    {
++      match_free (match);
++      return FALSE;
++    }
++
++  g_mutex_lock (&worker->matches_mutex);
++  worker->matches = g_list_prepend (worker->matches, match);
++  g_mutex_unlock (&worker->matches_mutex);
++  return TRUE;
++}
++
++
++/*
++ * _g_kdbus_subscribe_name_owner_changed
++ */
++gboolean
++_g_kdbus_subscribe_name_owner_changed (GKDBusWorker  *worker,
++                                       const gchar   *match_rule,
++                                       const gchar   *name,
++                                       GError       **error)
++{
++  Match *match;
++  struct kdbus_item *item;
++  struct kdbus_cmd_match *cmd;
++  gsize size, len;
++  gint ret;
++
++  if (match_rule[0] == '-')
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_MATCH_RULE_INVALID,
++                   "Invalid rule: %s", match_rule);
++      return FALSE;
++    }
++
++  if (name != NULL)
++  {
++    if (!g_dbus_is_name (name))
++      {
++        g_set_error (error,
++                      G_DBUS_ERROR,
++                      G_DBUS_ERROR_INVALID_ARGS,
++                      "Invalid dbus name: %s", name);
++        return FALSE;
++      }
++  }
++
++  match = match_new (match_rule);
++  if (!match)
++    {
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_MATCH_RULE_INVALID,
++                   "Invalid rule: %s", match_rule);
++      return FALSE;
++    }
++
++  /* 'name' argument is missing or is a unique name */
++  if (name == NULL || g_dbus_is_unique_name (name))
++    {
++      size = KDBUS_ALIGN8(G_STRUCT_OFFSET (struct kdbus_cmd_match, items) +
++                          G_STRUCT_OFFSET (struct kdbus_item, id_change) +
++                          sizeof (struct kdbus_notify_id_change));
++
++      cmd = g_alloca0 (size);
++      cmd->size = size;
++      cmd->cookie = match->cookie;
++      item = cmd->items;
++
++      if (name)
++        item->id_change.id = strtoull (name + 3, NULL, 10);
++      else
++        item->id_change.id = KDBUS_MATCH_ID_ANY;
++
++      item->size = (guint64) G_STRUCT_OFFSET (struct kdbus_item, id_change) +
++                   sizeof (struct kdbus_notify_id_change);
++
++      item->type = KDBUS_ITEM_ID_ADD;
++      ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
++      if (ret < 0)
++        {
++          g_set_error (error,
++                       G_DBUS_ERROR,
++                       G_DBUS_ERROR_FAILED,
++                       "Error while adding a match");
++          match_free (match);
++          return FALSE;
++        }
++
++      item->type = KDBUS_ITEM_ID_REMOVE;
++      ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
++      if (ret < 0)
++        {
++          g_set_error (error,
++                       G_DBUS_ERROR,
++                       G_DBUS_ERROR_FAILED,
++                       "Error while adding a match");
++          match_free (match);
++          return FALSE;
++        }
++    }
++
++  /* 'name' argument is missing or is a well-known name */
++  if (name == NULL || !g_dbus_is_unique_name (name))
++    {
++      if (name)
++        len = strlen(name) + 1;
++      else
++        len = 0;
++
++      size = KDBUS_ALIGN8(G_STRUCT_OFFSET (struct kdbus_cmd_match, items) +
++                          G_STRUCT_OFFSET (struct kdbus_item, name_change) +
++                          G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len);
++
++      cmd = g_alloca0 (size);
++      cmd->size = size;
++      cmd->cookie = match->cookie;
++      item = cmd->items;
++
++      item->name_change.old_id.id = KDBUS_MATCH_ID_ANY;
++      item->name_change.new_id.id = KDBUS_MATCH_ID_ANY;
++      item->size = (guint64) G_STRUCT_OFFSET (struct kdbus_item, name_change) +
++                   (guint64) G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len;
++
++      if (name)
++        memcpy(item->name_change.name, name, len);
++
++      item->type = KDBUS_ITEM_NAME_ADD;
++      ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
++      if (ret < 0)
++        {
++          g_set_error (error,
++                       G_DBUS_ERROR,
++                       G_DBUS_ERROR_FAILED,
++                       "Error while adding a match");
++          match_free (match);
++          return FALSE;
++        }
++
++      item->type = KDBUS_ITEM_NAME_REMOVE;
++      ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
++      if (ret < 0)
++        {
++          g_set_error (error,
++                       G_DBUS_ERROR,
++                       G_DBUS_ERROR_FAILED,
++                       "Error while adding a match");
++          match_free (match);
++          return FALSE;
++        }
++
++      item->type = KDBUS_ITEM_NAME_CHANGE;
++      ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
++      if (ret < 0)
++        {
++          g_set_error (error,
++                       G_DBUS_ERROR,
++                       G_DBUS_ERROR_FAILED,
++                       "Error while adding a match");
++          match_free (match);
++          return FALSE;
++        }
++    }
++
++  g_mutex_lock (&worker->matches_mutex);
++  worker->matches = g_list_prepend (worker->matches, match);
++  g_mutex_unlock (&worker->matches_mutex);
++  return TRUE;
++}
++
++
++/*
++ * g_kdbus_setup_bloom:
++ * Based on bus-bloom.c from systemd
++ * http://cgit.freedesktop.org/systemd/systemd/tree/src/libsystemd/sd-bus/bus-bloom.c
++ */
++static void
++g_kdbus_setup_bloom (GKDBusWorker               *worker,
++                     GDBusMessage               *dbus_msg,
++                     struct kdbus_bloom_filter  *bloom_filter)
++{
++  GVariant *body;
++  gchar *message_type;
++  const gchar *interface;
++  const gchar *member;
++  const gchar *path;
++  void *bloom_data;
++
++  body = g_dbus_message_get_body (dbus_msg);
++  message_type = _g_dbus_enum_to_string (G_TYPE_DBUS_MESSAGE_TYPE, g_dbus_message_get_message_type (dbus_msg));
++  interface = g_dbus_message_get_interface (dbus_msg);
++  member = g_dbus_message_get_member (dbus_msg);
++  path = g_dbus_message_get_path (dbus_msg);
++
++  bloom_data = bloom_filter->data;
++  memset (bloom_data, 0, worker->bloom_size);
++  bloom_filter->generation = 0;
++
++  g_kdbus_bloom_add_pair(worker, bloom_data, "message-type", message_type);
++
++  if (interface)
++    g_kdbus_bloom_add_pair(worker, bloom_data, "interface", interface);
++
++  if (member)
++    g_kdbus_bloom_add_pair(worker, bloom_data, "member", member);
++
++  if (path)
++    {
++      g_kdbus_bloom_add_pair(worker, bloom_data, "path", path);
++      g_kdbus_bloom_add_pair(worker, bloom_data, "path-slash-prefix", path);
++      g_kdbus_bloom_add_prefixes(worker, bloom_data, "path-slash-prefix", path, '/');
++    }
++
++  if (body != NULL)
++    {
++      const GVariantType *body_type;
++      const GVariantType *arg_type;
++      guint cnt;
++
++      body_type = g_variant_get_type (body);
++
++      for (arg_type = g_variant_type_first (body_type), cnt = 0;
++           arg_type;
++           arg_type = g_variant_type_next (arg_type), cnt++)
++        {
++          gchar type_char = g_variant_type_peek_string (arg_type)[0];
++          gchar buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")];
++          const gchar *str;
++          GVariant *child;
++          gchar *e;
++
++          if (type_char != 's' && type_char != 'o')
++            /* XXX: kdbus docs say "stop after first non-string" but I
++             * think they're wrong (vs. dbus-1 compat)...
++             */
++            continue;
++
++          child = g_variant_get_child_value (body, cnt);
++          str = g_variant_get_string (child, NULL);
++
++          e = stpcpy(buf, "arg");
++          if (cnt < 10)
++            *(e++) = '0' + (char) cnt;
++          else
++            {
++              *(e++) = '0' + (char) (cnt / 10);
++              *(e++) = '0' + (char) (cnt % 10);
++            }
++
++          /* We add this one for both strings and object paths */
++          strcpy(e, "-slash-prefix");
++          g_kdbus_bloom_add_prefixes(worker, bloom_data, buf, str, '/');
++
++          /* But the others are only for strings */
++          if (type_char == 's')
++            {
++              strcpy(e, "-dot-prefix");
++              g_kdbus_bloom_add_prefixes(worker, bloom_data, buf, str, '.');
++
++              *e = 0;
++              g_kdbus_bloom_add_pair(worker, bloom_data, buf, str);
++            }
++
++          g_variant_unref (child);
++        }
++    }
++  g_free (message_type);
++}
++
++
++/*
++ * g_kdbus_translate_id_change
++ */
++static void
++g_kdbus_translate_id_change (GKDBusWorker       *worker,
++                             struct kdbus_item  *item)
++{
++  GDBusMessage *signal_message;
++  gchar *name;
++
++  signal_message = g_dbus_message_new_signal ("/org/freedesktop/DBus",
++                                              "org.freedesktop.DBus",
++                                              "NameOwnerChanged");
++
++  name = g_strdup_printf (":1.%"G_GUINT64_FORMAT, (guint64) item->id_change.id);
++
++  g_dbus_message_set_sender (signal_message, "org.freedesktop.DBus");
++  g_dbus_message_set_body (signal_message,
++                           g_variant_new ("(sss)",
++                                          name,
++                                          item->type == KDBUS_ITEM_ID_ADD ? "" : name,
++                                          item->type == KDBUS_ITEM_ID_ADD ? name : ""));
++
++  (* worker->message_received_callback) (signal_message, worker->user_data);
++
++  g_free (name);
++  g_object_unref (signal_message);
++}
++
++
++/*
++ * g_kdbus_translate_name_change
++ */
++static void
++g_kdbus_translate_name_change (GKDBusWorker       *worker,
++                               struct kdbus_item  *item)
++{
++  GDBusMessage *signal_message;
++
++  signal_message = NULL;
++
++  /* NameAcquired */
++  if ((item->type == KDBUS_ITEM_NAME_ADD) ||
++      (item->type == KDBUS_ITEM_NAME_CHANGE && ((guint64)item->name_change.new_id.id == worker->unique_id)))
++    {
++      signal_message = g_dbus_message_new_signal ("/org/freedesktop/DBus",
++                                                  "org.freedesktop.DBus",
++                                                  "NameAcquired");
++
++      g_dbus_message_set_sender (signal_message, "org.freedesktop.DBus");
++      g_dbus_message_set_body (signal_message,
++                               g_variant_new ("(s)", item->name_change.name));
++
++      (* worker->message_received_callback) (signal_message, worker->user_data);
++      g_object_unref (signal_message);
++    }
++
++  /* NameLost */
++  if ((item->type == KDBUS_ITEM_NAME_REMOVE) ||
++      (item->type == KDBUS_ITEM_NAME_CHANGE && ((guint64)item->name_change.old_id.id == worker->unique_id)))
++    {
++      signal_message = g_dbus_message_new_signal ("/org/freedesktop/DBus",
++                                                  "org.freedesktop.DBus",
++                                                  "NameLost");
++
++      g_dbus_message_set_sender (signal_message, "org.freedesktop.DBus");
++      g_dbus_message_set_body (signal_message,
++                               g_variant_new ("(s)", item->name_change.name));
++
++      (* worker->message_received_callback) (signal_message, worker->user_data);
++      g_object_unref (signal_message);
++    }
++
++  /* NameOwnerChanged */
++  if (1)
++    {
++      gchar *old_name;
++      gchar *new_name;
++
++      old_name = NULL;
++      new_name = NULL;
++
++      /* old_name */
++      if (!(item->type == KDBUS_ITEM_NAME_ADD || (item->name_change.old_id.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR))))
++        old_name = g_strdup_printf (":1.%"G_GUINT64_FORMAT, (guint64) item->name_change.old_id.id);
++
++      /* new_name */
++      if (!(item->type == KDBUS_ITEM_NAME_REMOVE || (item->name_change.new_id.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR))))
++        new_name = g_strdup_printf (":1.%"G_GUINT64_FORMAT, (guint64) item->name_change.new_id.id);
++      else
++        if (old_name == NULL)
++          return;
++
++      /* send signal */
++      signal_message = g_dbus_message_new_signal ("/org/freedesktop/DBus",
++                                                  "org.freedesktop.DBus",
++                                                  "NameOwnerChanged");
++      g_dbus_message_set_sender (signal_message, "org.freedesktop.DBus");
++      g_dbus_message_set_body (signal_message,
++                               g_variant_new ("(sss)",
++                                              item->name_change.name,
++                                              old_name ? old_name : "",
++                                              new_name ? new_name : ""));
++
++      (* worker->message_received_callback) (signal_message, worker->user_data);
++
++      g_free (old_name);
++      g_free (new_name);
++      g_object_unref (signal_message);
++    }
++}
++
++
++/*
++ * g_kdbus_translate_kernel_reply
++ */
++static void
++g_kdbus_translate_kernel_reply (GKDBusWorker       *worker,
++                                struct kdbus_msg   *msg,
++                                struct kdbus_item  *item)
++{
++  GDBusMessage *message;
++
++  message = g_dbus_message_new ();
++
++  g_dbus_message_set_message_type (message, G_DBUS_MESSAGE_TYPE_ERROR);
++  g_dbus_message_set_flags (message, G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED);
++  g_dbus_message_set_reply_serial (message, (guint32) msg->cookie_reply);
++
++  g_dbus_message_set_sender (message, "org.freedesktop.DBus");
++  g_dbus_message_set_destination (message, worker->unique_name);
++
++  g_dbus_message_set_error_name (message, "org.freedesktop.DBus.Error.NoReply");
++
++  if (item->type == KDBUS_ITEM_REPLY_TIMEOUT)
++    g_dbus_message_set_body (message, g_variant_new ("(s)", "Method call timed out"));
++  else
++    g_dbus_message_set_body (message, g_variant_new ("(s)", "Method call peer died"));
++
++  (* worker->message_received_callback) (message, worker->user_data);
++
++  g_object_unref (message);
++}
++
++
++/*
++ * g_kdbus_decode_kernel_msg
++ */
++static void
++g_kdbus_decode_kernel_msg (GKDBusWorker      *worker,
++                           struct kdbus_msg  *msg)
++{
++  struct kdbus_item *item = NULL;
++
++  KDBUS_ITEM_FOREACH(item, msg, items)
++    {
++     switch (item->type)
++        {
++          case KDBUS_ITEM_ID_ADD:
++          case KDBUS_ITEM_ID_REMOVE:
++            g_kdbus_translate_id_change (worker, item);
++            break;
++
++          case KDBUS_ITEM_NAME_ADD:
++          case KDBUS_ITEM_NAME_REMOVE:
++          case KDBUS_ITEM_NAME_CHANGE:
++            g_kdbus_translate_name_change (worker, item);
++            break;
++
++          case KDBUS_ITEM_REPLY_TIMEOUT:
++          case KDBUS_ITEM_REPLY_DEAD:
++            g_kdbus_translate_kernel_reply (worker, msg, item);
++            break;
++
++          case KDBUS_ITEM_TIMESTAMP:
++            break;
++
++          default:
++            g_warning ("kdbus: unknown field in kernel message - %lld", item->type);
++       }
++    }
++}
++
++#define DEFINE_READ_FUNCTION(name, type) \
++  static inline type \
++  read_ ## name (GVariantSerialised data) \
++  { \
++    if (data.size < sizeof(type)) \
++      return 0; \
++    return *(type *)data.data; \
++  }
++
++DEFINE_READ_FUNCTION (byte, guchar)
++DEFINE_READ_FUNCTION (uint64, guint64)
++DEFINE_READ_FUNCTION (uint32, guint32)
++
++static const gchar *
++check_str (GVariantSerialised data)
++{
++  const gchar *str = (gchar *)data.data;
++  if (str[data.size] == '\0')
++    return str;
++  return "";
++}
++
++/*
++ * g_kdbus_decode_dbus_msg
++ */
++static GKDBusMessage *
++g_kdbus_decode_dbus_msg (GKDBusWorker      *worker,
++                         struct kdbus_msg  *msg)
++{
++  GKDBusMessage *kmsg;
++  struct kdbus_item *item;
++  gssize data_size = 0;
++  GArray *body_vectors;
++  gsize body_size;
++  GVariant *body;
++  gchar *sender;
++  guint i;
++  GVariant *parts[2];
++  GString *owned_name;
++  guint8 type = 0, flags = 0;
++  guint64 serial = 0;
++  GVariantSerialised gvs = { 0, };
++
++  kmsg = g_new0 (GKDBusMessage, 1);
++  kmsg->message = g_dbus_message_new();
++  kmsg->sender_euid = (uid_t) -1;
++  kmsg->sender_egid = (gid_t) -1;
++  kmsg->sender_seclabel = NULL;
++  kmsg->sender_names = NULL;
++
++  body_vectors = g_array_new (FALSE, FALSE, sizeof (GVariantVector));
++
++  item = msg->items;
++  body_size = 0;
++  owned_name = NULL;
++
++  KDBUS_ITEM_FOREACH(item, msg, items)
++    {
++      if (item->size < KDBUS_ITEM_HEADER_SIZE)
++        g_error("kdbus: %llu bytes - invalid data record", item->size);
++
++      data_size = item->size - KDBUS_ITEM_HEADER_SIZE;
++
++      switch (item->type)
++        {
++         case KDBUS_ITEM_DST_NAME:
++           /* Classic D-Bus doesn't make this known to the receiver, so
++            * we don't do it here either (for compatibility with the
++            * fallback case).
++            */
++           break;
++
++        case KDBUS_ITEM_PAYLOAD_OFF:
++          {
++            GVariantVector vector;
++            gsize flavour;
++
++            /* We want to make sure the bytes are aligned the same as
++             * they would be if they appeared in a contiguously
++             * allocated chunk of aligned memory.
++             *
++             * We decide what the alignment 'should' be by consulting
++             * body_size, which has been tracking the total size of the
++             * message up to this point.
++             *
++             * We then play around with the pointer by removing as many
++             * bytes as required to get it to the proper alignment (and
++             * copy extra bytes accordingly).  This means that we will
++             * grab some extra data in the 'bytes', but it won't be
++             * shared with GVariant (which means there is no chance of
++             * it being accidentally retransmitted).
++             *
++             * The kernel does the same thing, so make sure we get the
++             * expected result.  Because of the kernel doing the same,
++             * the result is that we will always be rounding-down to a
++             * multiple of 8 for the pointer, which means that the
++             * pointer will always be valid, assuming the original
++             * address was.
++             *
++             * We could fix this with a new GBytes constructor that took
++             * 'flavour' as a parameter, but it's not worth it...
++             */
++            flavour = body_size & 7;
++            g_assert ((item->vec.offset & 7) == flavour);
++
++            vector.gbytes = g_bytes_new (((guchar *) msg) + item->vec.offset - flavour, item->vec.size + flavour);
++            vector.data.pointer = g_bytes_get_data (vector.gbytes, NULL);
++            vector.data.pointer += flavour;
++            vector.size = item->vec.size;
++
++            g_array_append_val (body_vectors, vector);
++            body_size += vector.size;
++          }
++          break;
++
++        case KDBUS_ITEM_PAYLOAD_MEMFD:
++          {
++            GVariantVector vector;
++            const guchar *data;
++            gsize size;
++
++            vector.gbytes = g_bytes_new_take_zero_copy_fd_size (item->memfd.fd, item->memfd.size);
++            data = g_bytes_get_data (vector.gbytes, &size);
++
++            g_assert (item->memfd.size == size);
++
++            vector.data.pointer = data + item->memfd.start;
++            vector.size = item->memfd.size;
++
++            g_array_append_val (body_vectors, vector);
++            body_size += vector.size;
++          }
++          break;
++
++        case KDBUS_ITEM_FDS:
++          {
++            GUnixFDList *fd_list;
++
++            fd_list = g_unix_fd_list_new_from_array (item->fds, data_size / sizeof (int));
++            g_dbus_message_set_unix_fd_list (kmsg->message, fd_list);
++            g_object_unref (fd_list);
++          }
++          break;
++
++        /* [libdbuspolicy] read euid and egid values */
++        case KDBUS_ITEM_CREDS:
++
++          if ((uid_t) item->creds.euid != (uid_t) -1)
++            kmsg->sender_euid = (uid_t) item->creds.euid;
++
++          if ((gid_t) item->creds.egid != (gid_t) -1)
++            kmsg->sender_egid = (gid_t) item->creds.egid;
++
++          break;
++
++        /* [libdbuspolicy] read security label value */
++        case KDBUS_ITEM_SECLABEL:
++
++          /*if (item->str != NULL)*/
++            kmsg->sender_seclabel = g_strdup (item->str);
++
++          break;
++
++        /* [libdbuspolicy] read all owned well-known names */
++        case KDBUS_ITEM_OWNED_NAME:
++
++          if (g_dbus_is_name (item->name.name))
++            {
++              if (owned_name == NULL)
++                owned_name = g_string_new (item->name.name);
++              else
++                g_string_append_printf (owned_name, " %s", item->name.name);
++            }
++
++          break;
++
++        case KDBUS_ITEM_TIMESTAMP:
++        case KDBUS_ITEM_PIDS:
++        case KDBUS_ITEM_PID_COMM:
++        case KDBUS_ITEM_TID_COMM:
++        case KDBUS_ITEM_EXE:
++        case KDBUS_ITEM_CMDLINE:
++        case KDBUS_ITEM_CGROUP:
++        case KDBUS_ITEM_AUDIT:
++        case KDBUS_ITEM_CAPS:
++        case KDBUS_ITEM_CONN_DESCRIPTION:
++        case KDBUS_ITEM_AUXGROUPS:
++        case KDBUS_ITEM_NAME:
++        case KDBUS_ITEM_DST_ID:
++        case KDBUS_ITEM_BLOOM_FILTER:
++          break;
++
++        default:
++          g_warning ("kdbus: unknown filed - %lld", item->type);
++          break;
++        }
++    }
++
++  body = GLIB_PRIVATE_CALL(g_variant_from_vectors) (G_VARIANT_TYPE ("((yyyyuta{tv})v)"),
++                                                    (GVariantVector *) body_vectors->data,
++                                                    body_vectors->len, body_size, FALSE);
++  g_assert (body);
++
++  parts[0] = g_variant_get_child_value (body, 0);
++  gvs.type_info = g_variant_type_info_get (G_VARIANT_TYPE ("(yyyyuta{tv})"));
++  gvs.data = (guchar *)g_variant_get_data (parts[0]);
++  gvs.size = g_variant_get_size (parts[0]);
++  gvs.depth = 0;
++  gvs.ordered_offsets_up_to = 0;
++  gvs.checked_offsets_up_to = 0;
++
++  type = read_byte (g_variant_serialised_get_child(gvs, 1));
++  flags = read_byte (g_variant_serialised_get_child(gvs, 2));
++  serial = read_uint64 (g_variant_serialised_get_child(gvs, 5));
++
++  {
++    guint8 i = 0;
++    gsize n_children;
++    GVariantSerialised gvsc;
++
++    gvsc = g_variant_serialised_get_child(gvs, 6);
++    n_children = g_variant_serialised_n_children(gvsc);
++
++    for (i = 0; i < n_children; i++) {
++      GVariantSerialised item;
++      GVariantSerialised variant;
++      guint64 key;
++      GVariantSerialised variant_value;
++
++      item = g_variant_serialised_get_child (gvsc, i);
++      key = read_uint64 (g_variant_serialised_get_child (item, 0));
++      variant = g_variant_serialised_get_child (item, 1);
++      variant_value = g_variant_serialised_get_child (variant, 0);  /* strip variant */
++
++      switch (key) {
++        case G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL:
++          {
++            guint64 val = read_uint64 (variant_value);
++            g_dbus_message_set_reply_serial (kmsg->message, (guint32)val);
++          }
++          break;
++        case G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS:
++          g_dbus_message_set_header (kmsg->message, key, g_variant_new_uint32 (read_uint32 (variant_value)));
++          break;
++        case G_DBUS_MESSAGE_HEADER_FIELD_PATH:
++          g_dbus_message_set_header (kmsg->message, key, g_variant_new_object_path (check_str (variant_value)));
++          break;
++        case G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE:
++          g_dbus_message_set_header (kmsg->message, key, g_variant_new_signature (check_str (variant_value)));
++          break;
++        default:
++          g_dbus_message_set_header (kmsg->message, key, g_variant_new_string (check_str (variant_value)));
++          break;
++      }
++    }
++  }
++
++  parts[1] = g_variant_get_child_value (body, 1);
++  g_variant_unref (body);
++
++  g_variant_unref (parts[0]);
++
++  for (i = 0; i < body_vectors->len; i++)
++    g_bytes_unref (g_array_index (body_vectors, GVariantVector, i).gbytes);
++
++  g_array_free (body_vectors, TRUE);
++
++  g_dbus_message_set_flags (kmsg->message, flags);
++  g_dbus_message_set_serial (kmsg->message, serial);
++  g_dbus_message_set_message_type (kmsg->message, type);
++
++  body = g_variant_get_variant (parts[1]);
++  if (!g_variant_is_of_type (body, G_VARIANT_TYPE ("()")))
++    g_dbus_message_set_body (kmsg->message, body);
++  else
++    g_dbus_message_set_body (kmsg->message, NULL);
++
++  g_variant_unref (body);
++  g_variant_unref (parts[1]);
++
++  /* set 'sender' field */
++  sender = g_strdup_printf (":1.%"G_GUINT64_FORMAT, (guint64) msg->src_id);
++  g_dbus_message_set_sender (kmsg->message, sender);
++  g_free (sender);
++
++  /* owned name */
++  if (owned_name != NULL)
++    kmsg->sender_names = g_string_free (owned_name, FALSE);
++
++  return kmsg;
++}
++
++
++/*
++ * _g_kdbus_receive
++ */
++static void
++_g_kdbus_receive (GKDBusWorker  *worker,
++                  GError       **error)
++{
++  struct kdbus_cmd_recv recv;
++  struct kdbus_msg *msg;
++  gboolean can_receive;
++  gint ret = 0;
++
++  can_receive = TRUE;
++  memset (&recv, 0, sizeof recv);
++  recv.size = sizeof (recv);
++
++again:
++    ret = ioctl (worker->fd, KDBUS_CMD_RECV, &recv);
++    if (ret < 0)
++          ret = errno;
++
++    if (recv.return_flags & KDBUS_RECV_RETURN_DROPPED_MSGS)
++      g_warning ("kdbus: %lld dropped broadcast messages", recv.dropped_msgs);
++
++    if (ret != 0)
++      {
++        if (ret == EINTR)
++          goto again;
++
++        if (ret == EAGAIN)
++          return;
++
++        g_set_error (error, G_IO_ERROR,
++                     g_io_error_from_errno (ret),
++                     _("Error while receiving message: %s"),
++                     g_strerror (ret));
++        return;
++      }
++
++    msg = (struct kdbus_msg *)((guint8 *)worker->kdbus_buffer + recv.msg.offset);
++
++    if (msg->payload_type == KDBUS_PAYLOAD_DBUS)
++      {
++        GKDBusMessage *kmsg;
++
++        kmsg = g_kdbus_decode_dbus_msg (worker, msg);
++
++#ifdef LIBDBUSPOLICY
++    if (worker->dbuspolicy != NULL)
++      {
++        if (g_dbus_message_get_message_type (kmsg->message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL ||
++            g_dbus_message_get_message_type (kmsg->message) == G_DBUS_MESSAGE_TYPE_SIGNAL)
++          {
++            if ((kmsg->sender_euid != (uid_t) -1) && (kmsg->sender_egid != (gid_t) -1) &&
++                (kmsg->sender_seclabel != NULL))
++              {
++                gint check;
++                const gchar *destination = g_dbus_message_get_destination (kmsg->message);
++                if (!destination)
++                  destination = worker->unique_name;
++
++                check = dbuspolicy1_check_in (worker->dbuspolicy,
++                                              destination,
++                                              kmsg->sender_names,
++                                              kmsg->sender_seclabel,
++                                              kmsg->sender_euid,
++                                              kmsg->sender_egid,
++                                              g_dbus_message_get_path (kmsg->message),
++                                              g_dbus_message_get_interface (kmsg->message),
++                                              g_dbus_message_get_member (kmsg->message),
++                                              g_dbus_message_get_message_type (kmsg->message),
++                                              NULL, 0, 0);
++                if (check != 1)
++                  {
++                    can_receive = FALSE;
++                  }
++              }
++            else
++              {
++                can_receive = FALSE;
++              }
++          }
++      }
++#endif
++
++       if (can_receive)
++         (* worker->message_received_callback) (kmsg->message, worker->user_data);
++
++       if (kmsg->sender_seclabel != NULL)
++         g_free (kmsg->sender_seclabel);
++
++       if (kmsg->sender_names != NULL)
++         g_free (kmsg->sender_names);
++
++       g_object_unref (kmsg->message);
++       g_free (kmsg);
++     }
++   else if (msg->payload_type == KDBUS_PAYLOAD_KERNEL)
++     g_kdbus_decode_kernel_msg (worker, msg);
++   else
++     {
++       g_set_error (error,
++                    G_DBUS_ERROR,
++                    G_DBUS_ERROR_FAILED,
++                    _("Received unknown payload type"));
++     }
++
++  g_kdbus_close_msg (worker, msg);
++  return;
++}
++
++static gboolean
++g_kdbus_msg_append_item (struct kdbus_msg  *msg,
++                         gsize              type,
++                         gconstpointer      data,
++                         gsize              size)
++{
++  struct kdbus_item *item;
++  gsize item_size;
++
++  item_size = size + G_STRUCT_OFFSET(struct kdbus_item, data);
++
++  msg->size += (-msg->size) & 7;
++  item = (struct kdbus_item *) ((guchar *) msg + msg->size);
++  item->type = type;
++  item->size = item_size;
++  memcpy (item->data, data, size);
++
++  msg->size += item_size;
++
++  return TRUE;
++}
++
++static gboolean
++g_kdbus_msg_append_payload_vec (struct kdbus_msg  *msg,
++                                gconstpointer      data,
++                                gsize              size)
++{
++  gboolean ok = TRUE;
++  do {
++    struct kdbus_vec vec = {
++      .size = size <= KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE ? size : KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE,
++      .address = (gsize) data
++    };
++    ok = g_kdbus_msg_append_item (msg, KDBUS_ITEM_PAYLOAD_VEC, &vec, sizeof vec);
++    data = (char*)data + vec.size;
++    size -= vec.size;
++  } while (size > 0 && ok);
++  return ok;
++}
++
++static gboolean
++g_kdbus_msg_append_payload_memfd (struct kdbus_msg  *msg,
++                                  gint               fd,
++                                  gsize              offset,
++                                  gsize              size)
++{
++  struct kdbus_memfd mfd = {
++   .start = offset,
++   .size = size,
++   .fd = fd,
++  };
++
++  return g_kdbus_msg_append_item (msg, KDBUS_ITEM_PAYLOAD_MEMFD, &mfd, sizeof mfd);
++}
++
++static struct kdbus_bloom_filter *
++g_kdbus_msg_append_bloom (struct kdbus_msg  *msg,
++                          gsize              size)
++{
++  struct kdbus_item *bloom_item;
++  gsize bloom_item_size;
++
++  bloom_item_size = (gulong) G_STRUCT_OFFSET (struct kdbus_item, bloom_filter) +
++                    (gulong) G_STRUCT_OFFSET (struct kdbus_bloom_filter, data) +
++                    size;
++
++  msg->size += (-msg->size) & 7;
++  bloom_item = (struct kdbus_item *) ((guchar *) msg + msg->size);
++
++  bloom_item->size = bloom_item_size;
++  bloom_item->type = KDBUS_ITEM_BLOOM_FILTER;
++
++  msg->size += bloom_item->size;
++  return &bloom_item->bloom_filter;
++}
++
++/*
++ * Returns header size.
++ */
++static gsize
++prepare_body_vectors (GKDBusWorker    *worker,
++                      GDBusMessage    *message,
++                      GVariantVectors *body_vectors,
++                      gboolean        *lg_h_field_exist)
++{
++  struct dbus_fixed_header fh;
++  GHashTableIter header_iter;
++  GVariantBuilder builder;
++  gpointer key, value;
++  GVariant *parts[3];
++  GVariant *body;
++  gsize header_size;
++
++  *lg_h_field_exist = FALSE;
++
++  fh.endian = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? 'l': 'B';
++  fh.type = g_dbus_message_get_message_type (message);
++  fh.flags = g_dbus_message_get_flags (message);
++  fh.version = 2;
++  fh.reserved = 0;
++  fh.serial = g_dbus_message_get_serial (message);
++  parts[0] = g_variant_new_from_data (DBUS_FIXED_HEADER_TYPE, &fh, sizeof fh, TRUE, NULL, NULL);
++  g_assert_nonnull (parts[0]);
++
++  g_dbus_message_init_header_iter (message, &header_iter);
++  g_variant_builder_init (&builder, DBUS_EXTENDED_HEADER_TYPE);
++
++  /* We set the sender field to the correct value for ourselves */
++  g_variant_builder_add (&builder, "{tv}",
++                         (guint64) G_DBUS_MESSAGE_HEADER_FIELD_SENDER,
++                         g_variant_new_printf (":1.%"G_GUINT64_FORMAT, worker->unique_id));
++
++  while (g_hash_table_iter_next (&header_iter, &key, &value))
++    {
++      guint64 key_int = (gsize) key;
++
++      switch (key_int)
++        {
++          /* These are the normal header fields that get passed
++           * straight through.
++           */
++          case G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL:
++            g_variant_builder_add (&builder, "{tv}", key_int, g_variant_new_uint64 (g_dbus_message_get_reply_serial(message)));
++            continue;
++
++
++          case G_DBUS_MESSAGE_HEADER_FIELD_PATH:
++          case G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE:
++          case G_DBUS_MESSAGE_HEADER_FIELD_MEMBER:
++          case G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME:
++          case G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION:
++          case G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS:
++            g_variant_builder_add (&builder, "{tv}", key_int, value);
++            /* This is a little bit gross.
++             *
++             * We must send the header part of the message in a single
++             * vector as per kdbus rules, but the GVariant serialiser
++             * code will split any item >= 128 bytes into its own
++             * vector to save the copy.
++             *
++             * Anyway, kdbus does not check it, and libraries handle it...
++             */
++
++            if (g_variant_get_size (value) >= 128)
++              *lg_h_field_exist = TRUE;
++
++            continue;
++
++          /* We send this one unconditionally, but set it ourselves */
++          case G_DBUS_MESSAGE_HEADER_FIELD_SENDER:
++            continue;
++
++          /* We don't send these at all in GVariant format */
++          case G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE:
++            continue;
++
++          default:
++            g_assert_not_reached ();
++        }
++    }
++  parts[1] = g_variant_builder_end (&builder);
++  g_assert_nonnull (parts[1]);
++
++  body = g_dbus_message_get_body (message);
++  if (!body)
++    body = g_variant_new ("()");
++  parts[2] = g_variant_new_variant (body);
++  g_assert_nonnull (parts[2]);
++
++  header_size = g_variant_get_size (parts[0]) + g_variant_get_size (parts[1]);
++
++  body = g_variant_ref_sink (g_variant_new_tuple (parts, G_N_ELEMENTS (parts)));
++  GLIB_PRIVATE_CALL(g_variant_to_vectors) (body, body_vectors);
++
++  /* Sanity check to make sure the header is really contiguous:
++   *
++   *  - we must have at least one vector in the output
++   *  - the first vector must completely contain at least the header; this
++   *    condition must be assured later, during composing kdbus vectors.
++   */
++  g_assert_cmpint (body_vectors->vectors->len, >, 0);
++
++  g_variant_unref (body);
++
++  return header_size;
++}
++
++static void
++make_single_header_vector (GVariantVectors *body_vectors,
++                           gsize            header_size,
++                           gboolean         lg_h_field_exist)
++{
++  guint i = 0;
++  gsize added_vectors_size = 0;
++  GVariantVector *first_vector;
++
++  /* Merge all header vectors into single vector */
++  first_vector = &g_array_index (body_vectors->vectors, GVariantVector, 0);
++  if (first_vector->size < header_size)
++    {
++      /* There are multiple header vectors, we have to join them together into a single one */
++      GByteArray *header = g_byte_array_sized_new (header_size);
++      gsize dummy_size;
++
++      g_assert_nonnull (header);
++      g_assert_true (lg_h_field_exist);
++
++      while (added_vectors_size < header_size && i < body_vectors->vectors->len)
++        {
++          GVariantVector *vector = &g_array_index (body_vectors->vectors, GVariantVector, i);
++
++          if (vector->gbytes)
++            {
++              g_byte_array_append (header, vector->data.pointer, vector->size);
++              g_bytes_unref (vector->gbytes);
++            }
++          else
++            {
++              g_byte_array_append (header, body_vectors->extra_bytes->data + vector->data.offset, vector->size);
++            }
++
++          added_vectors_size += vector->size;
++          i++;
++        }
++
++      /* Sanity check if the first vector contains at least the complete header */
++      g_assert_cmpint (added_vectors_size, >=, header_size);
++
++      first_vector->gbytes = g_byte_array_free_to_bytes (header);
++      first_vector->data.pointer = g_bytes_get_data (first_vector->gbytes, &dummy_size);
++      first_vector->size = added_vectors_size;
++
++      g_array_remove_range (body_vectors->vectors, 1, i-1);
++    }
++  /* else: If there is only a single header vector, then just go */
++}
++
++static gboolean
++add_message_destination_item (struct kdbus_msg *msg,
++                              GDBusMessage     *message,
++                              GError          **error)
++{
++  const gchar *dst_name;
++
++  dst_name = g_dbus_message_get_destination (message);
++  if (dst_name != NULL)
++    {
++      if (g_dbus_is_unique_name (dst_name))
++        {
++          if (dst_name[1] != '1' || dst_name[2] != '.')
++            {
++              g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER,
++                           "Invalid unique D-Bus name '%s'", dst_name);
++              return FALSE;
++            }
++
++          /* We already know that it passes the checks for unique
++           * names, so no need to perform error checking on strtoull.
++           */
++          msg->dst_id = strtoull (dst_name + 3, NULL, 10);
++        }
++      else
++        {
++          g_kdbus_msg_append_item (msg, KDBUS_ITEM_DST_NAME, dst_name, strlen (dst_name) + 1);
++          msg->dst_id = KDBUS_DST_ID_NAME;
++        }
++    }
++  else
++    msg->dst_id = KDBUS_DST_ID_BROADCAST;
++
++  return TRUE;
++}
++
++static void
++add_file_descriptors_item (struct kdbus_msg *msg,
++                           GDBusMessage     *message)
++{
++  GUnixFDList *fd_list;
++
++  fd_list = g_dbus_message_get_unix_fd_list (message);
++  if (fd_list != NULL)
++    {
++      const gint *fds;
++      gint n_fds;
++
++      fds = g_unix_fd_list_peek_fds (fd_list, &n_fds);
++
++      if (n_fds)
++        g_kdbus_msg_append_item (msg, KDBUS_ITEM_FDS, fds, sizeof (gint) * n_fds);
++    }
++}
++
++static gboolean
++add_body_vectors (struct kdbus_msg *msg,
++                  GVariantVectors  *body_vectors,
++                  gint             *memfd_fd)
++{
++  guint i = 0;
++
++  /* Add all the vectors to the kdbus message */
++  for (; i < body_vectors->vectors->len; i++)
++    {
++      GVariantVector vector = g_array_index (body_vectors->vectors, GVariantVector, i);
++
++      if (vector.gbytes)
++        {
++          const guchar *bytes_data;
++          gboolean use_memfd;
++          gsize bytes_size;
++
++          use_memfd = FALSE;
++          bytes_data = g_bytes_get_data (vector.gbytes, &bytes_size);
++
++          /* check whether we can and should use memfd */
++          if ((msg->dst_id != KDBUS_DST_ID_BROADCAST) && (bytes_size > KDBUS_MEMFD_THRESHOLD))
++            use_memfd = TRUE;
++
++          if (use_memfd)
++            {
++              const guchar *bytes_data_wr;
++              gint64 wr;
++
++              /* create memfd object */
++              *memfd_fd = glib_linux_memfd_create ("glib-kdbus-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING);
++              if (*memfd_fd == -1)
++                {
++                  g_warning ("kdbus: missing kernel memfd support");
++                  use_memfd = FALSE; /* send as PAYLOAD_VEC item */
++                }
++              else
++                {
++                  /* write data to memfd */
++                  bytes_data_wr = bytes_data;
++                  while (bytes_size)
++                    {
++                      wr = write (*memfd_fd, bytes_data_wr, bytes_size);
++                      if (wr < 0)
++                        g_warning ("kdbus: writing to memfd failed: (%d) %m", errno);
++
++                      bytes_size -= wr;
++                      bytes_data_wr += wr;
++                    }
++
++                  /* seal memfd */
++                  if (!g_unix_fd_ensure_zero_copy_safe (*memfd_fd))
++                    {
++                      g_warning ("kdbus: memfd sealing failed");
++                      use_memfd = FALSE; /* send as PAYLOAD_VEC item */
++                    }
++                  else
++                    {
++                      /* attach memfd item */
++                      if (!g_kdbus_msg_append_payload_memfd (msg, *memfd_fd, vector.data.pointer - bytes_data, vector.size))
++                        return FALSE;
++                    }
++                } /* *memfd_fd == -1 */
++            } /* use_memfd */
++
++          if (!use_memfd)
++            if (!g_kdbus_msg_append_payload_vec (msg, vector.data.pointer, vector.size))
++              return FALSE;
++        }
++      else
++        if (!g_kdbus_msg_append_payload_vec (msg, body_vectors->extra_bytes->data + vector.data.offset, vector.size))
++          return FALSE;
++    }
++  return TRUE;
++}
++
++static gboolean
++add_bloom_item (struct kdbus_msg *msg,
++                GDBusMessage     *message,
++                GKDBusWorker     *worker)
++{
++  struct kdbus_bloom_filter *bloom_filter;
++
++  if (g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_SIGNAL)
++    {
++      msg->flags |= KDBUS_MSG_SIGNAL;
++      bloom_filter = g_kdbus_msg_append_bloom (msg, worker->bloom_size);
++      if (bloom_filter == NULL)
++        return FALSE;
++      g_kdbus_setup_bloom (worker, message, bloom_filter);
++    }
++  return TRUE;
++}
++
++static gsize
++compute_msg_size (GDBusMessage    *message,
++                  GKDBusWorker    *worker,
++                  GVariantVectors *body_vectors)
++{
++  gsize items_size = 0;
++  GUnixFDList *fd_list;
++  const gchar *dst_name;
++  guint i;
++  GDBusMessageType msg_type = g_dbus_message_get_message_type (message);
++
++  /* payload - check which vectors will become memfds */
++  for (i = 0; i < body_vectors->vectors->len; i++)
++    {
++      GVariantVector vector = g_array_index (body_vectors->vectors, GVariantVector, i);
++      gsize payload_size;
++      gsize kdbus_num_vectors;
++
++      if (vector.gbytes)
++        {
++          payload_size = g_bytes_get_size (vector.gbytes);
++          if (msg_type != G_DBUS_MESSAGE_TYPE_SIGNAL && payload_size > KDBUS_MEMFD_THRESHOLD)
++            {
++              /* We can do this because we know that struct kdbus_memfd is larger or equal than
++                 struct kdbus_vec. struct kdbus_vec is fallback option if memfd does not work */
++              items_size += KDBUS_ITEM_SIZE (sizeof (struct kdbus_memfd));
++              continue;
++            }
++        }
++      else
++        {
++          payload_size = vector.size;
++        }
++
++      kdbus_num_vectors = (payload_size + KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE - 1) / KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE;
++      items_size += KDBUS_ITEM_SIZE (sizeof (struct kdbus_vec)) * kdbus_num_vectors;
++    }
++
++  /* file descriptors */
++  fd_list = g_dbus_message_get_unix_fd_list (message);
++  if (fd_list != NULL)
++    items_size += KDBUS_ITEM_SIZE (g_unix_fd_list_get_length (fd_list) * sizeof(gint));
++
++  /* destination */
++  dst_name = g_dbus_message_get_destination (message);
++  if (dst_name != NULL)
++    items_size += KDBUS_ITEM_SIZE (strlen (dst_name) + 1);
++
++  /* bloom filters */
++  if (msg_type == G_DBUS_MESSAGE_TYPE_SIGNAL)
++    items_size += KDBUS_ITEM_SIZE (sizeof (struct kdbus_bloom_filter) + worker->bloom_size);
++
++  return sizeof (struct kdbus_msg) + items_size;
++}
++
++#ifdef LIBDBUSPOLICY
++static GDBusMessage*
++create_access_error_reply (GDBusMessage *message,
++                           const GQuark  domain,
++                           const gint    code,
++                           const gchar  *format,
++                           ...)
++{
++  gchar *error_text;
++  GError *access_error;
++  gchar *dbus_error_name;
++  GDBusMessage *error_reply;
++  va_list args;
++
++  va_start (args, format);
++  error_text = g_strdup_vprintf (format, args);
++  va_end (args);
++
++  access_error = NULL;
++  g_set_error (&access_error, domain, code, "%s", error_text);
++  dbus_error_name = g_dbus_error_encode_gerror (access_error);
++  g_error_free (access_error);
++
++  error_reply = g_dbus_message_new_method_error (message, dbus_error_name, "%s", error_text);
++  g_free (dbus_error_name);
++  g_free (error_text);
++  return error_reply;
++}
++#endif
++
++#ifdef LIBDBUSPOLICY
++static GDBusMessage*
++check_result_to_error_reply (GDBusMessage *message,
++                             const gint    check_result)
++{
++  switch (check_result)
++    {
++      case DBUSPOLICY_RESULT_DENY:
++        return create_access_error_reply (message, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
++                     "Cannot send message - message rejected due to XML security policies");
++
++      case DBUSPOLICY_RESULT_DEST_NOT_AVAILABLE:
++        if (g_dbus_message_get_flags (message) & G_DBUS_MESSAGE_FLAGS_NO_AUTO_START)
++          return create_access_error_reply (message, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER,
++                       "Name \"%s\" does not exist", g_dbus_message_get_destination (message));
++        else
++          return create_access_error_reply (message, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN,
++                       "Cannot send message - destination '%s' not known", g_dbus_message_get_destination (message));
++
++      case DBUSPOLICY_RESULT_KDBUS_ERROR:
++        return create_access_error_reply (message, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
++                     "Cannot send message - message rejected due to internal libdbuspolicy error (kdbus)");
++
++      case DBUSPOLICY_RESULT_CYNARA_ERROR:
++        return create_access_error_reply (message, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
++                     "Cannot send message - message rejected due to internal libdbuspolicy error (Cynara)");
++
++      default:
++        return create_access_error_reply (message, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
++                     "Cannot send message - unknown libdbuspolicy error");
++    }
++}
++#endif
++
++/*
++ * _g_kdbus_send
++ */
++static gboolean
++_g_kdbus_send (GKDBusWorker  *worker,
++               GDBusMessage  *message,
++               GDBusMessage **out_reply,
++               gint           timeout_msec,
++               GCancellable  *cancellable,
++               GError       **error)
++{
++  struct kdbus_msg *msg;
++  GVariantVectors body_vectors;
++  struct kdbus_cmd_send *send;
++  gsize send_size;
++  gboolean result = FALSE;
++  gboolean lg_h_field_exist = FALSE;
++
++  gint memfd_fd;
++  gint cancel_fd;
++
++  gsize header_size;
++  gsize msg_size;
++
++  g_return_val_if_fail (G_IS_KDBUS_WORKER (worker), FALSE);
++
++  send = NULL;
++  send_size = sizeof(*send);
++
++  /* Prepare message body - it is needed for kdbus msg size computation */
++  header_size = prepare_body_vectors (worker, message, &body_vectors, &lg_h_field_exist);
++  make_single_header_vector (&body_vectors, header_size, lg_h_field_exist);
++
++  /* We precompute needed size for the message to allocate exact space instead
++     of some arbitrary amount */
++  msg_size = compute_msg_size (message, worker, &body_vectors);
++  if (msg_size <= KDBUS_MSG_MAX_SIZE)
++    {
++      msg = g_alloca (msg_size);
++      memset (msg, 0, msg_size);
++    }
++  else
++    {
++      msg = g_malloc0 (msg_size);
++    }
++  g_assert_nonnull (msg);
++
++  memfd_fd = -1;
++  cancel_fd = -1;
++
++  /* fill in as we go... */
++  msg->size = sizeof (struct kdbus_msg);
++  msg->payload_type = KDBUS_PAYLOAD_DBUS;
++  msg->cookie = g_dbus_message_get_serial(message);
++
++  /* Message destination */
++  if (!add_message_destination_item (msg, message, error))
++    goto out;
++
++  /* File descriptors */
++  add_file_descriptors_item (msg, message);
++
++  if (!add_body_vectors (msg, &body_vectors, &memfd_fd))
++    {
++      g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
++                   "message serialisation error: body vectors");
++      g_warning ("kdbus: message serialisation error: body vectors");
++      goto out;
++    }
++
++  /*
++   * set message flags
++   */
++  msg->flags = ((g_dbus_message_get_flags (message) & G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_EXPECT_REPLY) |
++                ((g_dbus_message_get_flags (message) & G_DBUS_MESSAGE_FLAGS_NO_AUTO_START) ? KDBUS_MSG_NO_AUTO_START : 0);
++
++  if ((msg->flags) & KDBUS_MSG_EXPECT_REPLY)
++    msg->timeout_ns = 1U | ( /* ensure nonzero */
++                        1000U * g_get_monotonic_time() + (
++                          timeout_msec == -1 ? DBUS_DEFAULT_TIMEOUT_MSEC * 1000000LLU :
++                          timeout_msec == G_MAXINT ? KDBUS_INFINITE_TIMEOUT_NS :
++                          (guint64)timeout_msec * 1000000U
++                        )
++                      );
++  else
++    msg->cookie_reply = g_dbus_message_get_reply_serial(message);
++
++  /*
++   * append bloom filter item for broadcast signals
++   */
++  if (!add_bloom_item (msg, message, worker))
++    {
++      g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
++                   "message serialisation error: bloom filters");
++      g_warning ("kdbus: message serialisation error: bloom filters");
++      goto out;
++    }
++
++  if (out_reply != NULL && cancellable)
++    {
++      cancel_fd = g_cancellable_get_fd (cancellable);
++      if (cancel_fd != -1)
++        send_size += KDBUS_ITEM_SIZE (sizeof(cancel_fd));
++    }
++
++  g_assert_cmpuint (msg->size, <=, msg_size);
++
++  send = g_alloca0 (send_size);
++  send->size = send_size;
++  send->msg_address = (gsize) msg;
++
++  if (out_reply != NULL)
++    {
++      /* synchronous call */
++      send->flags = KDBUS_SEND_SYNC_REPLY;
++
++      if (cancel_fd != -1)
++        {
++          struct kdbus_item *item;
++
++          item = send->items;
++          item->type = KDBUS_ITEM_CANCEL_FD;
++          item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(cancel_fd);
++          item->fds[0] = cancel_fd;
++        }
++    }
++  else
++    {
++      /* asynchronous call */
++      send->flags = 0;
++    }
++
++  /*
++   * show debug
++   */
++  if (G_UNLIKELY (_g_dbus_debug_message ()))
++    {
++      gchar *s;
++      _g_dbus_debug_print_lock ();
++      g_print ("========================================================================\n"
++               "GDBus-debug:Message:\n"
++               "  >>>> SENT D-Bus/kdbus message\n");
++      s = g_dbus_message_print (message, 2);
++      g_print ("%s", s);
++      g_free (s);
++      _g_dbus_debug_print_unlock ();
++    }
++
++  /*
++   * check policy
++   */
++#ifdef LIBDBUSPOLICY
++  if (worker->dbuspolicy != NULL)
++    {
++      if (g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL ||
++         (g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_SIGNAL &&
++           g_dbus_message_get_destination (message) != NULL))
++        {
++          gint check;
++
++          check = dbuspolicy1_check_out (worker->dbuspolicy,
++                                         g_dbus_message_get_destination (message),
++                                         worker->unique_name,
++                                         g_dbus_message_get_path (message),
++                                         g_dbus_message_get_interface (message),
++                                         g_dbus_message_get_member (message),
++                                         g_dbus_message_get_message_type (message),
++                                         NULL, 0, 0);
++          if (check != DBUSPOLICY_RESULT_ALLOW)
++            {
++              GDBusMessage *access_error_message;
++              access_error_message = check_result_to_error_reply (message, check);
++              result = TRUE;
++              if (out_reply != NULL)
++                {
++                  *out_reply = access_error_message;
++                }
++              else
++                {
++                  send_synthetic_message (worker, access_error_message);
++                }
++
++              goto out;
++            }
++        }
++    }
++#endif
++
++  /*
++   * send message
++   */
++  if (ioctl(worker->fd, KDBUS_CMD_SEND, send))
++    {
++      int ret = errno;
++      gchar *info;
++      if (asprintf (&info, "sender=%s destination=%s path=%s interface=%s member=%s type=%d",
++                       g_dbus_message_get_sender (message),
++                       g_dbus_message_get_destination (message),
++                       g_dbus_message_get_path (message),
++                       g_dbus_message_get_interface (message),
++                       g_dbus_message_get_member (message),
++                       g_dbus_message_get_message_type (message)) < 0)
++        {
++        g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "%s", g_strerror(errno));
++        /* If asprintf fails, we lose the ioctl errno,
++         * but without info, we can't give a useful error message.
++         * In practice though, asprintf failure is exceedingly rare.
++         */
++        }
++      else
++        {
++          errno = ret;
++          if (errno == ENXIO || errno == ESRCH)
++            {
++              if (g_dbus_message_get_flags (message) & G_DBUS_MESSAGE_FLAGS_NO_AUTO_START)
++                g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER,
++                             "Name \"%s\" does not exist, %s", g_dbus_message_get_destination (message), info);
++              else
++                g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN,
++                             "Destination '%s' not known, %s", g_dbus_message_get_destination (message), info);
++            }
++          else if (errno == EADDRNOTAVAIL)
++            {
++              g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN,
++                           "No support for activation for name: %s, %s", g_dbus_message_get_destination (message), info);
++            }
++          else if (errno == EXFULL)
++            {
++              g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_LIMITS_EXCEEDED,
++                           "The memory pool of the receiver is full, %s", info);
++            }
++          else if (errno == ENOBUFS)
++            {
++              g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_LIMITS_EXCEEDED,
++                           "Too many pending messages on the receiver side, %s", info);
++            }
++            else if (errno == EMSGSIZE)
++            {
++              g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_LIMITS_EXCEEDED,
++                           "The size of the message is excessive, %s", info);
++            }
++          else if (errno == EMLINK)
++            {
++              g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_LIMITS_EXCEEDED,
++                           "The maximum number of pending replies per connection has been reached, %s", info);
++            }
++          else if (errno == ECANCELED)
++            {
++              g_set_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED,
++                           "Operation was cancelled, %s", info);
++            }
++          else if (errno == ETIMEDOUT)
++            {
++              g_set_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
++                           "Timeout was reached, %s", info);
++            }
++          else if (errno == EPERM)
++            {
++              g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
++                           "Permission denied, %s", info);
++            }
++          else
++            {
++              g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "%s, %s", g_strerror(errno), info);
++              g_warning ("kdbus: %s, %s", g_strerror(errno), info);
++            }
++          free(info);
++        }
++    }
++  else
++    {
++      result = TRUE;
++      if (out_reply != NULL)
++        {
++          GKDBusMessage *kmsg;
++          struct kdbus_msg *kdbus_msg;
++
++          kdbus_msg = (struct kdbus_msg *)((guint8 *)worker->kdbus_buffer + send->reply.offset);
++
++          kmsg = g_kdbus_decode_dbus_msg (worker, kdbus_msg);
++          g_kdbus_close_msg (worker, kdbus_msg);
++
++          *out_reply = kmsg->message;
++
++          if (kmsg->sender_seclabel != NULL)
++            g_free (kmsg->sender_seclabel);
++
++          if (kmsg->sender_names != NULL)
++            g_free (kmsg->sender_names);
++
++          g_free (kmsg);
++
++          if (G_UNLIKELY (_g_dbus_debug_message ()))
++            {
++              gchar *s;
++              _g_dbus_debug_print_lock ();
++              g_print ("========================================================================\n"
++                       "GDBus-debug:Message:\n"
++                       "  <<<< RECEIVED D-Bus message\n");
++              s = g_dbus_message_print (*out_reply, 2);
++              g_print ("%s", s);
++              g_free (s);
++              _g_dbus_debug_print_unlock ();
++            }
++        }
++    }
++
++out:
++  if (msg_size > KDBUS_MSG_MAX_SIZE)
++    g_free (msg);
++
++  if (cancel_fd != -1)
++    g_cancellable_release_fd (cancellable);
++
++  if (memfd_fd != -1)
++    close (memfd_fd);
++
++  GLIB_PRIVATE_CALL(g_variant_vectors_deinit) (&body_vectors);
++
++  return result;
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++static void
++g_kdbus_worker_finalize (GObject *object)
++{
++  GKDBusWorker *worker;
++  GList *match;
++
++  worker = G_KDBUS_WORKER (object);
++
++  if (worker->kdbus_buffer != NULL)
++    {
++      munmap (worker->kdbus_buffer, worker->receive_pool_size);
++      worker->kdbus_buffer = NULL;
++      worker->receive_pool_size = 0;
++    }
++
++  if (worker->unique_name != NULL)
++    g_free (worker->unique_name);
++  worker->unique_name = NULL;
++
++  for (match = worker->matches; match != NULL; match = match->next)
++    match_free (match->data);
++  g_list_free (worker->matches);
++  g_mutex_clear (&worker->matches_mutex);
++
++#ifdef LIBDBUSPOLICY
++  if (worker->dbuspolicy != NULL)
++    dbuspolicy1_free (worker->dbuspolicy);
++#endif
++
++  if (worker->fd != -1 && !worker->closed)
++    _g_kdbus_close (worker);
++
++  G_OBJECT_CLASS (g_kdbus_worker_parent_class)->finalize (object);
++}
++
++static void
++g_kdbus_worker_class_init (GKDBusWorkerClass *class)
++{
++  class->finalize = g_kdbus_worker_finalize;
++}
++
++static void
++g_kdbus_worker_init (GKDBusWorker *worker)
++{
++  worker->fd = -1;
++
++  worker->context = NULL;
++  worker->loop = NULL;
++  worker->thread = NULL;
++  worker->source = 0;
++
++  worker->kdbus_buffer = NULL;
++  worker->receive_pool_size = 0;
++  worker->unique_name = NULL;
++  worker->unique_id = -1;
++
++  worker->flags = KDBUS_HELLO_ACCEPT_FD;
++  worker->attach_flags_send = _KDBUS_ATTACH_ALL;
++  /* Attach items we really use only. Each collected item brings unneccessary overhead.
++   *
++   * The flags are used by libdbuspolicy library which uses these to apply dbus policy
++   * to each received message.
++   */
++  worker->attach_flags_recv = KDBUS_ATTACH_PIDS | KDBUS_ATTACH_CREDS | KDBUS_ATTACH_SECLABEL;
++
++  worker->bloom_size = 0;
++  worker->bloom_n_hash = 0;
++  worker->matches = NULL;
++  g_mutex_init (&worker->matches_mutex);
++
++#ifdef LIBDBUSPOLICY
++  worker->dbuspolicy = NULL;
++#endif
++}
++
++static gpointer
++_g_kdbus_worker_thread (gpointer _data)
++{
++  GMainLoop *loop = (GMainLoop *) _data;
++
++  g_main_loop_run (loop);
++
++  g_main_loop_unref (loop);
++
++  return NULL;
++}
++
++GKDBusWorker *
++_g_kdbus_worker_new (const gchar  *address,
++                     GError      **error)
++{
++  GKDBusWorker *worker;
++
++  worker = g_object_new (G_TYPE_KDBUS_WORKER, NULL);
++  if (!_g_kdbus_open (worker, address, error))
++    {
++      g_object_unref (worker);
++      return NULL;
++    }
++
++  worker->context = g_main_context_new ();
++  worker->loop = g_main_loop_new (worker->context, FALSE);
++  worker->thread = g_thread_new ("gkdbus", _g_kdbus_worker_thread, g_main_loop_ref(worker->loop));
++
++  return worker;
++}
++
++void
++_g_kdbus_worker_associate (GKDBusWorker                            *worker,
++                           GDBusCapabilityFlags                     capabilities,
++                           GDBusWorkerMessageReceivedCallback       message_received_callback,
++                           GDBusWorkerMessageAboutToBeSentCallback  message_about_to_be_sent_callback,
++                           GDBusWorkerDisconnectedCallback          disconnected_callback,
++                           gpointer                                 user_data)
++{
++  worker->capabilities = capabilities;
++  worker->message_received_callback = message_received_callback;
++  worker->message_about_to_be_sent_callback = message_about_to_be_sent_callback;
++  worker->disconnected_callback = disconnected_callback;
++  worker->user_data = user_data;
++}
++
++static gboolean
++g_kdbus_ready (gint           fd,
++               GIOCondition   condition,
++               gpointer       user_data)
++{
++  GKDBusWorker *worker;
++  GError *error;
++
++  worker = user_data;
++  error = NULL;
++
++  _g_kdbus_receive (worker, &error);
++  g_assert_no_error (error);
++
++  return G_SOURCE_CONTINUE;
++}
++
++void
++_g_kdbus_worker_unfreeze (GKDBusWorker *worker)
++{
++  gchar *name;
++
++  if (worker->source != NULL)
++    return;
++
++  worker->source = g_unix_fd_source_new (worker->fd, G_IO_IN);
++
++  g_source_set_callback (worker->source, (GSourceFunc) g_kdbus_ready,
++                         g_object_ref (worker), g_object_unref);
++  name = g_strdup_printf ("kdbus worker");
++  g_source_set_name (worker->source, name);
++  g_free (name);
++
++  g_source_attach (worker->source, worker->context);
++}
++
++gboolean
++_g_kdbus_worker_send_message (GKDBusWorker  *worker,
++                              GDBusMessage  *message,
++                              gint           timeout_msec,
++                              GError       **error)
++{
++#ifdef DBUS_DAEMON_EMULATION
++  if (_is_message_to_dbus_daemon (message))
++    {
++      GDBusMessage *reply = NULL;
++      reply = _dbus_daemon_synthetic_reply (worker, message, FALSE);
++
++      if (reply)
++        {
++          SyntheticReplyData *data;
++
++          data = g_new0 (SyntheticReplyData, 1);
++
++          data->worker = worker;
++          data->message = reply;
++
++          g_main_context_invoke (worker->context, deliver_synthetic_reply, data);
++        }
++
++      return TRUE;
++    }
++#endif /* DBUS_DAEMON_EMULATION */
++
++  return _g_kdbus_send (worker, message, NULL, timeout_msec, NULL, error);
++}
++
++gboolean
++_g_kdbus_worker_send_message_sync (GKDBusWorker  *worker,
++                                   GDBusMessage  *message,
++                                   GDBusMessage **out_reply,
++                                   gint           timeout_msec,
++                                   GCancellable  *cancellable,
++                                   GError       **error)
++{
++#ifdef DBUS_DAEMON_EMULATION
++  if (_is_message_to_dbus_daemon (message))
++    {
++      *out_reply = _dbus_daemon_synthetic_reply (worker, message, TRUE);
++      return TRUE;
++    }
++#endif /* DBUS_DAEMON_EMULATION */
++
++  return _g_kdbus_send (worker, message, out_reply, timeout_msec, cancellable, error);
++}
++
++gboolean
++_g_kdbus_worker_flush_sync (GKDBusWorker *worker)
++{
++  return TRUE;
++}
++
++void
++_g_kdbus_worker_stop (GKDBusWorker *worker)
++{
++  g_source_destroy (worker->source);
++  g_source_unref (worker->source);
++  worker->source = 0;
++
++  g_object_unref (worker);
++}
++
++void
++_g_kdbus_worker_close (GKDBusWorker       *worker,
++                       GTask              *task)
++{
++  worker->disconnected_callback (FALSE, NULL, worker->user_data);
++  g_task_return_boolean (task, TRUE);
++}
+diff --git a/gio/gkdbus.h b/gio/gkdbus.h
+new file mode 100644
+index 0000000..acbc0d5
+--- /dev/null
++++ b/gio/gkdbus.h
+@@ -0,0 +1,203 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright (C) 2015 Samsung Electronics
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Author: Lukasz Skalski       <l.skalski@samsung.com>
++ * Author: Michal Eljasiewicz   <m.eljasiewic@samsung.com>
++ */
++
++#ifndef __G_KDBUS_H__
++#define __G_KDBUS_H__
++
++#if !defined (GIO_COMPILATION)
++#error "gkdbus.h is a private header file."
++#endif
++
++#include <gio/giotypes.h>
++#include "gdbusprivate.h"
++
++#define G_TYPE_KDBUS_WORKER                                (g_kdbus_worker_get_type ())
++#define G_KDBUS_WORKER(inst)                               (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
++                                                            G_TYPE_KDBUS_WORKER, GKDBusWorker))
++#define G_KDBUS_WORKER_CLASS(class)                        (G_TYPE_CHECK_CLASS_CAST ((class),                       \
++                                                            G_TYPE_KDBUS_WORKER, GKDBusWorkerClass))
++#define G_IS_KDBUS_WORKER(inst)                            (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
++                                                            G_TYPE_KDBUS_WORKER))
++#define G_IS_KDBUS_WORKER_CLASS(class)                     (G_TYPE_CHECK_CLASS_TYPE ((class),                       \
++                                                            G_TYPE_KDBUS_WORKER))
++#define G_KDBUS_WORKER_GET_CLASS(inst)                     (G_TYPE_INSTANCE_GET_CLASS ((inst),                      \
++                                                            G_TYPE_KDBUS_WORKER, GKDBusWorkerClass))
++typedef enum {
++  G_DBUS_CREDS_NONE = 0,
++  G_DBUS_CREDS_PID = (1<<0),
++  G_DBUS_CREDS_UID = (1<<1),
++  G_DBUS_CREDS_UNIQUE_NAME = (1<<2),
++  G_DBUS_CREDS_SEC_LABEL = (1<<3)
++} GDBusCredentialsFlags;
++
++typedef struct
++{
++  guint   pid;
++  guint   uid;
++  gchar  *unique_name;
++  gchar  *sec_label;
++} GDBusCredentials;
++
++typedef struct
++{
++  GDBusMessage  *message;
++  uid_t          sender_euid;
++  gid_t          sender_egid;
++  gchar         *sender_seclabel;
++  gchar         *sender_names;
++} GKDBusMessage;
++
++typedef struct _GKDBusWorker                                  GKDBusWorker;
++
++void                  _g_kdbus_worker_associate              (GKDBusWorker                             *worker,
++                                                              GDBusCapabilityFlags                      capabilities,
++                                                              GDBusWorkerMessageReceivedCallback        message_received_callback,
++                                                              GDBusWorkerMessageAboutToBeSentCallback   message_about_to_be_sent_callback,
++                                                              GDBusWorkerDisconnectedCallback           disconnected_callback,
++                                                              gpointer                                  user_data);
++
++GType                  g_kdbus_worker_get_type               (void);
++
++GKDBusWorker *        _g_kdbus_worker_new                    (const gchar         *address,
++                                                              GError             **error);
++
++void                  _g_kdbus_worker_unfreeze               (GKDBusWorker        *worker);
++
++gboolean              _g_kdbus_worker_send_message           (GKDBusWorker        *worker,
++                                                              GDBusMessage        *message,
++                                                              gint                 timeout_msec,
++                                                              GError             **error);
++
++gboolean              _g_kdbus_worker_send_message_sync      (GKDBusWorker        *worker,
++                                                              GDBusMessage        *message,
++                                                              GDBusMessage       **out_reply,
++                                                              gint                 timeout_msec,
++                                                              GCancellable        *cancellable,
++                                                              GError             **error);
++
++void                  _g_kdbus_worker_stop                   (GKDBusWorker        *worker);
++
++gboolean              _g_kdbus_worker_flush_sync             (GKDBusWorker        *worker);
++
++void                  _g_kdbus_worker_close                  (GKDBusWorker        *worker,
++                                                              GTask               *task);
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++gboolean              _g_kdbus_open                          (GKDBusWorker        *worker,
++                                                              const gchar         *address,
++                                                              GError             **error);
++
++gboolean              _g_kdbus_can_connect                   (GKDBusWorker        *worker,
++                                                              GError             **error);
++
++gboolean              _g_kdbus_close                         (GKDBusWorker        *worker);
++
++gboolean              _g_kdbus_is_closed                     (GKDBusWorker        *worker);
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++const gchar *               _g_kdbus_Hello                           (GKDBusWorker        *worker,
++                                                                      GError             **error);
++
++gchar *                     _g_kdbus_GetBusId                        (GKDBusWorker        *worker,
++                                                                      GError             **error);
++
++GBusRequestNameReplyFlags   _g_kdbus_RequestName                     (GKDBusWorker        *worker,
++                                                                      const gchar         *name,
++                                                                      GBusNameOwnerFlags   flags,
++                                                                      GError             **error);
++
++GBusReleaseNameReplyFlags   _g_kdbus_ReleaseName                     (GKDBusWorker     *worker,
++                                                                      const gchar      *name,
++                                                                      GError          **error);
++
++gchar **                    _g_kdbus_GetListNames                    (GKDBusWorker     *worker,
++                                                                      gboolean          activatable,
++                                                                      GError          **error);
++
++gchar **                    _g_kdbus_GetListQueuedOwners             (GKDBusWorker     *worker,
++                                                                      const gchar      *name,
++                                                                      GError          **error);
++
++gboolean                    _g_kdbus_NameHasOwner                    (GKDBusWorker     *connection,
++                                                                      const gchar      *name,
++                                                                      GError          **error);
++
++gchar *                     _g_kdbus_GetNameOwner                    (GKDBusWorker     *worker,
++                                                                      const gchar      *name,
++                                                                      GError          **error);
++
++pid_t                       _g_kdbus_GetConnectionUnixProcessID      (GKDBusWorker     *worker,
++                                                                      const gchar      *name,
++                                                                      GError          **error);
++
++uid_t                       _g_kdbus_GetConnectionUnixUser           (GKDBusWorker     *worker,
++                                                                      const gchar      *name,
++                                                                      GError          **error);
++
++gchar *                     _g_kdbus_GetConnectionSecurityLabel      (GKDBusWorker     *worker,
++                                                                      const gchar      *name,
++                                                                      GError          **error);
++
++GBusStartServiceReplyFlags  _g_kdbus_StartServiceByName              (GKDBusWorker     *worker,
++                                                                      const gchar      *name,
++                                                                      guint32           flags,
++                                                                      GDBusMessage     *message,
++                                                                      GError          **error);
++
++gboolean                    _g_kdbus_AddMatch                        (GKDBusWorker     *worker,
++                                                                      const gchar      *match_rule,
++                                                                      GError          **error);
++
++gboolean                    _g_kdbus_RemoveMatch                     (GKDBusWorker     *worker,
++                                                                      const gchar      *match_rule,
++                                                                      GError          **error);
++
++GDBusCredentials *          _g_kdbus_GetConnInfo                     (GKDBusWorker     *worker,
++                                                                      const gchar      *name,
++                                                                      guint             flags,
++                                                                      GError          **error);
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++gboolean              _g_kdbus_subscribe_name_acquired               (GKDBusWorker     *worker,
++                                                                      const gchar      *match_rule,
++                                                                      const gchar      *name,
++                                                                      GError          **error);
++
++gboolean              _g_kdbus_subscribe_name_lost                   (GKDBusWorker     *worker,
++                                                                      const gchar      *match_rule,
++                                                                      const gchar      *name,
++                                                                      GError          **error);
++
++gboolean              _g_kdbus_subscribe_name_owner_changed          (GKDBusWorker     *worker,
++                                                                      const gchar      *match_rule,
++                                                                      const gchar      *name,
++                                                                      GError          **error);
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++G_END_DECLS
++
++#endif /* __G_KDBUS_H__ */
+diff --git a/gio/gkdbusfakedaemon.c b/gio/gkdbusfakedaemon.c
+new file mode 100644
+index 0000000..04272f3
+--- /dev/null
++++ b/gio/gkdbusfakedaemon.c
+@@ -0,0 +1,694 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright (C) 2015 Samsung Electronics
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Author: Lukasz Skalski <l.skalski@samsung.com>
++ */
++
++#include "config.h"
++#include "gkdbus.h"
++#include "gkdbusfakedaemon.h"
++
++#include <gio/gio.h>
++#include <string.h>
++
++static gchar *introspect =
++  "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
++  "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
++  "<node>\n"
++  " <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
++  "  <method name=\"Introspect\">\n"
++  "   <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
++  "  </method>\n"
++  " </interface>\n"
++  " <interface name=\"org.freedesktop.DBus\">\n"
++  "  <method name=\"AddMatch\">\n"
++  "   <arg type=\"s\" direction=\"in\"/>\n"
++  "  </method>\n"
++  "  <method name=\"RemoveMatch\">\n"
++  "   <arg type=\"s\" direction=\"in\"/>\n"
++  "  </method>\n"
++  "  <method name=\"GetConnectionCredentials\">\n"
++  "   <arg type=\"s\" direction=\"in\"/>\n"
++  "   <arg type=\"a{sv}\" direction=\"out\"/>\n"
++  "  </method>\n"
++  "  <method name=\"GetConnectionSELinuxSecurityContext\">\n"
++  "   <arg type=\"s\" direction=\"in\"/>\n"
++  "   <arg type=\"ay\" direction=\"out\"/>\n"
++  "  </method>\n"
++  "  <method name=\"GetConnectionUnixProcessID\">\n"
++  "   <arg type=\"s\" direction=\"in\"/>\n"
++  "   <arg type=\"u\" direction=\"out\"/>\n"
++  "  </method>\n"
++  "  <method name=\"GetConnectionUnixUser\">\n"
++  "   <arg type=\"s\" direction=\"in\"/>\n"
++  "   <arg type=\"u\" direction=\"out\"/>\n"
++  "  </method>\n"
++  "  <method name=\"GetId\">\n"
++  "   <arg type=\"s\" direction=\"out\"/>\n"
++  "  </method>\n"
++  "  <method name=\"GetNameOwner\">\n"
++  "   <arg type=\"s\" direction=\"in\"/>\n"
++  "   <arg type=\"s\" direction=\"out\"/>\n"
++  "  </method>\n"
++  "  <method name=\"Hello\">\n"
++  "   <arg type=\"s\" direction=\"out\"/>\n"
++  "  </method>\n"
++  "  <method name=\"ListActivatableNames\">\n"
++  "   <arg type=\"as\" direction=\"out\"/>\n"
++  "  </method>\n"
++  "  <method name=\"ListNames\">\n"
++  "   <arg type=\"as\" direction=\"out\"/>\n"
++  "  </method>\n"
++  "  <method name=\"ListQueuedOwners\">\n"
++  "   <arg type=\"s\" direction=\"in\"/>\n"
++  "   <arg type=\"as\" direction=\"out\"/>\n"
++  "  </method>\n"
++  "  <method name=\"NameHasOwner\">\n"
++  "   <arg type=\"s\" direction=\"in\"/>\n"
++  "   <arg type=\"b\" direction=\"out\"/>\n"
++  "  </method>\n"
++  "  <method name=\"ReleaseName\">\n"
++  "   <arg type=\"s\" direction=\"in\"/>\n"
++  "   <arg type=\"u\" direction=\"out\"/>\n"
++  "  </method>\n"
++  "  <method name=\"ReloadConfig\">\n"
++  "  </method>\n"
++  "  <method name=\"RequestName\">\n"
++  "   <arg type=\"s\" direction=\"in\"/>\n"
++  "   <arg type=\"u\" direction=\"in\"/>\n"
++  "   <arg type=\"u\" direction=\"out\"/>\n"
++  "  </method>\n"
++  "  <method name=\"StartServiceByName\">\n"
++  "   <arg type=\"s\" direction=\"in\"/>\n"
++  "   <arg type=\"u\" direction=\"in\"/>\n"
++  "   <arg type=\"u\" direction=\"out\"/>\n"
++  "  </method>\n"
++  "  <method name=\"UpdateActivationEnvironment\">\n"
++  "   <arg type=\"a{ss}\" direction=\"in\"/>\n"
++  "  </method>\n"
++  "  <signal name=\"NameAcquired\">\n"
++  "   <arg type=\"s\"/>\n"
++  "  </signal>\n"
++  "  <signal name=\"NameLost\">\n"
++  "   <arg type=\"s\"/>\n"
++  "  </signal>\n"
++  "  <signal name=\"NameOwnerChanged\">\n"
++  "   <arg type=\"s\"/>\n"
++  "   <arg type=\"s\"/>\n"
++  "   <arg type=\"s\"/>\n"
++  "  </signal>\n"
++  " </interface>\n"
++  "</node>\n";
++
++static gboolean
++_mac_smack_use (void)
++{
++  static int cached_use = -1;
++
++  if (cached_use < 0)
++    cached_use = access("/sys/fs/smackfs/", F_OK) >= 0;
++
++  return cached_use;
++}
++
++/**
++ * _is_message_to_dbus_daemon()
++ */
++gboolean
++_is_message_to_dbus_daemon (GDBusMessage  *message)
++{
++  return g_strcmp0 (g_dbus_message_get_destination (message), "org.freedesktop.DBus") == 0 &&
++         (g_strcmp0 (g_dbus_message_get_interface (message), "org.freedesktop.DBus") == 0 ||
++          g_strcmp0 (g_dbus_message_get_interface (message), "org.freedesktop.DBus.Introspectable") == 0) &&
++         (g_strcmp0 (g_dbus_message_get_path (message), "/org/freedesktop/DBus") == 0 ||
++          g_strcmp0 (g_dbus_message_get_path (message), "/") == 0);
++}
++
++
++/**
++ * _dbus_daemon_synthetic_reply()
++ */
++GDBusMessage *
++_dbus_daemon_synthetic_reply (GKDBusWorker  *worker,
++                              GDBusMessage  *message,
++                              gboolean sync)
++{
++  GDBusMessage *reply;
++  GVariant     *reply_body;
++  GVariant     *body;
++  GError       *local_error;
++  const gchar  *member;
++
++  reply = NULL;
++  reply_body = NULL;
++  local_error = NULL;
++
++  member = g_dbus_message_get_member (message);
++  body = g_dbus_message_get_body (message);
++
++  /* show debug */
++  if (G_UNLIKELY (_g_dbus_debug_message ()))
++    {
++      gchar *s;
++      _g_dbus_debug_print_lock ();
++      g_print ("========================================================================\n"
++               "GDBus-debug:Message:\n"
++               "  >>>> SENT D-Bus/kdbus message\n");
++      s = g_dbus_message_print (message, 2);
++      g_print ("%s", s);
++      g_free (s);
++      _g_dbus_debug_print_unlock ();
++    }
++
++  /*
++   * Introspect
++   */
++  if (!g_strcmp0 (member, "Introspect"))
++    {
++      reply_body = g_variant_new ("(s)", introspect);
++    }
++
++  /*
++   * AddMatch
++   */
++  else if (!g_strcmp0 (member, "AddMatch"))
++    {
++      if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++        {
++          gchar *rule;
++
++          g_variant_get (body, "(&s)", &rule);
++
++          _g_kdbus_AddMatch (worker, rule, &local_error);
++          if (local_error == NULL)
++            reply_body = g_variant_new ("()", NULL);
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'AddMatch' has wrong args (expected s)");
++    }
++
++  /*
++   * RemoveMatch
++   */
++  else if (!g_strcmp0 (member, "RemoveMatch"))
++    {
++      if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++        {
++          gchar *rule;
++
++          g_variant_get (body, "(&s)", &rule);
++
++          _g_kdbus_RemoveMatch (worker, rule, &local_error);
++          if (local_error == NULL)
++            reply_body = g_variant_new ("()", NULL);
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'RemoveMatch' has wrong args (expected s)");
++    }
++
++  /*
++   * GetConnectionCredentials
++   */
++  else if (!g_strcmp0 (member, "GetConnectionCredentials"))
++    {
++      if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++        {
++          GDBusCredentials *creds;
++          gchar *name;
++          guint flags;
++
++          creds = NULL;
++          flags = G_DBUS_CREDS_PID | G_DBUS_CREDS_UID | G_DBUS_CREDS_SEC_LABEL;
++
++          g_variant_get (body, "(&s)", &name);
++
++          creds = _g_kdbus_GetConnInfo (worker,
++                                        name,
++                                        flags,
++                                        &local_error);
++          if (local_error == NULL)
++            {
++              GVariantBuilder builder;
++
++              g_variant_builder_init (&builder, G_VARIANT_TYPE ("(a{sv})"));
++              g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}"));
++
++              g_variant_builder_add (&builder, "{sv}", "UnixUserID", g_variant_new_uint32 (creds->uid));
++              g_variant_builder_add (&builder, "{sv}", "ProcessID", g_variant_new_uint32 (creds->pid));
++
++              if (creds->sec_label != NULL)
++                {
++                  GVariantBuilder *label_builder;
++                  gint counter;
++                  gint label_size;
++
++                  label_size = strlen (creds->sec_label);
++                  label_builder = g_variant_builder_new (G_VARIANT_TYPE ("ay"));
++                  for (counter = 0 ; counter < label_size + 1; counter++)   /* label_size + 1 : include the \0 in the payload, for zero-copy reading. From dbus/bus/driver.c */
++                    g_variant_builder_add (label_builder, "y", creds->sec_label[counter]);
++
++                  g_variant_builder_add (&builder, "{sv}", "LinuxSecurityLabel", g_variant_new ("ay", label_builder));
++
++                  g_variant_builder_unref (label_builder);
++                  g_free (creds->sec_label);
++                }
++
++              g_variant_builder_close (&builder);
++              reply_body = g_variant_builder_end (&builder);
++              g_free (creds);
++            }
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'GetConnectionCredentials' has wrong args (expected s)");
++    }
++
++  /*
++   * GetConnectionSELinuxSecurityContext
++   */
++  else if (!g_strcmp0 (member, "GetConnectionSELinuxSecurityContext"))
++    {
++      if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++        {
++          gchar *name;
++          gchar *label;
++
++          g_variant_get (body, "(&s)", &name);
++
++          label = _g_kdbus_GetConnectionSecurityLabel (worker, name, &local_error);
++          if (label == NULL && local_error == NULL)
++            g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Operation not supported");
++          else if (local_error == NULL)
++            {
++              /* 'label' (KDBUS_ITEM_SECLABEL item) contains valid LSM security label... */
++              if (_mac_smack_use())
++                {
++                  /* but if we are using SMACK - to keep compatibility with legacy dbus1 - return error */
++                  g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN,
++                               "Could not determine security context for '%s'", name);
++                }
++              else
++                {
++                  /* if it is not SMACK - let's assume that it's SELinux label */
++                  GVariantBuilder builder;
++                  gint counter;
++
++                  g_variant_builder_init (&builder, G_VARIANT_TYPE ("(ay)"));
++                  g_variant_builder_open (&builder, G_VARIANT_TYPE ("ay"));
++
++                  for (counter = 0 ; counter < strlen (label) ; counter++)
++                    g_variant_builder_add (&builder, "y", label[counter]);
++
++                  g_variant_builder_close (&builder);
++                  reply_body = g_variant_builder_end (&builder);
++                }
++              g_free (label);
++            }
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'GetConnectionSELinuxSecurityContext' has wrong args (expected s)");
++    }
++
++  /*
++   * GetConnectionUnixProcessID
++   */
++  else if (!g_strcmp0 (member, "GetConnectionUnixProcessID"))
++    {
++      if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++        {
++          gchar *name;
++          pid_t pid;
++
++          g_variant_get (body, "(&s)", &name);
++          pid = _g_kdbus_GetConnectionUnixProcessID (worker, name, &local_error);
++          if (local_error == NULL)
++            reply_body = g_variant_new ("(u)", pid);
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'GetConnectionUnixProcessID' has wrong args (expected s)");
++    }
++
++  /*
++   * GetConnectionUnixUser
++   */
++  else if (!g_strcmp0 (member, "GetConnectionUnixUser"))
++    {
++      if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++        {
++          gchar *name;
++          uid_t uid;
++
++          g_variant_get (body, "(&s)", &name);
++          uid = _g_kdbus_GetConnectionUnixUser (worker, name, &local_error);
++          if (local_error == NULL)
++            reply_body = g_variant_new ("(u)", uid);
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'GetConnectionUnixUser' has wrong args (expected s)");
++    }
++
++  /*
++   * GetId
++   */
++  else if (!g_strcmp0 (member, "GetId"))
++    {
++      if ((body == NULL) || g_variant_is_of_type (body, G_VARIANT_TYPE ("()")))
++        {
++          gchar *bus_id;
++
++          bus_id = _g_kdbus_GetBusId (worker, NULL);
++          reply_body = g_variant_new ("(s)", bus_id);
++
++          g_free (bus_id);
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'GetId' has wrong args");
++    }
++
++  /*
++   * GetNameOwner
++   */
++  else if (!g_strcmp0 (member, "GetNameOwner"))
++    {
++      if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++        {
++          gchar *unique_name;
++          gchar *name;
++
++          g_variant_get (body, "(&s)", &name);
++
++          unique_name = _g_kdbus_GetNameOwner (worker, name, &local_error);
++          if (local_error == NULL)
++            reply_body = g_variant_new ("(s)", unique_name);
++          g_free (unique_name);
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'GetNameOwner' has wrong args (expected s)");
++    }
++
++  /*
++   * Hello
++   */
++  else if (!g_strcmp0 (member, "Hello"))
++    {
++      if ((body == NULL) || g_variant_is_of_type (body, G_VARIANT_TYPE ("()")))
++        {
++          const gchar *unique_name;
++
++          unique_name = _g_kdbus_Hello (worker, &local_error);
++          if (local_error == NULL)
++            reply_body = g_variant_new ("(s)", unique_name);
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'Hello' has wrong args");
++    }
++
++  /*
++   * ListActivatableNames
++   */
++  else if (!g_strcmp0 (member, "ListActivatableNames"))
++    {
++      if ((body == NULL) || g_variant_is_of_type (body, G_VARIANT_TYPE ("()")))
++        {
++          gchar **strv;
++          gint cnt;
++
++          cnt = 0;
++
++          strv = _g_kdbus_GetListNames (worker, TRUE, &local_error);
++          if (local_error == NULL)
++            {
++              GVariantBuilder *builder;
++
++              builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
++
++              while (strv[cnt])
++                g_variant_builder_add (builder, "s", strv[cnt++]);
++
++              reply_body = g_variant_new ("(as)", builder);
++
++              g_variant_builder_unref (builder);
++            }
++          g_strfreev (strv);
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'ListActivatableNames' has wrong args");
++    }
++
++  /*
++   * ListNames
++   */
++  else if (!g_strcmp0 (member, "ListNames"))
++    {
++      if ((body == NULL) || g_variant_is_of_type (body, G_VARIANT_TYPE ("()")))
++        {
++          gchar **strv;
++          gint cnt;
++
++          cnt = 0;
++
++          strv = _g_kdbus_GetListNames (worker, FALSE, &local_error);
++          if (local_error == NULL)
++            {
++              GVariantBuilder *builder;
++
++              builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
++
++              while (strv[cnt])
++                g_variant_builder_add (builder, "s", strv[cnt++]);
++
++              reply_body = g_variant_new ("(as)", builder);
++
++              g_variant_builder_unref (builder);
++            }
++          g_strfreev (strv);
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'ListNames' has wrong args");
++    }
++
++  /*
++   * ListQueuedOwners
++   */
++  else if (!g_strcmp0 (member, "ListQueuedOwners"))
++    {
++      if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++        {
++          gchar **strv;
++          gchar *name;
++          gint cnt;
++
++          cnt = 0;
++
++          g_variant_get (body, "(&s)", &name);
++          strv = _g_kdbus_GetListQueuedOwners (worker, name, &local_error);
++          if (local_error == NULL)
++            {
++              GVariantBuilder *builder;
++
++              builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
++
++              while (strv[cnt])
++                g_variant_builder_add (builder, "s", strv[cnt++]);
++
++              reply_body = g_variant_new ("(as)", builder);
++
++              g_variant_builder_unref (builder);
++            }
++          g_strfreev (strv);
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'ListQueuedOwners' has wrong args (expected s)");
++    }
++
++  /*
++   * NameHasOwner
++   */
++  else if (!g_strcmp0 (member, "NameHasOwner"))
++    {
++      if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++        {
++          gboolean ret;
++          gchar *name;
++
++          g_variant_get (body, "(&s)", &name);
++
++          ret = _g_kdbus_NameHasOwner (worker, name, &local_error);
++          if (local_error == NULL)
++            {
++              if (ret)
++                reply_body = g_variant_new ("(b)", TRUE);
++              else
++                reply_body = g_variant_new ("(b)", FALSE);
++            }
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'NameHasOwner' has wrong args (expected s)");
++    }
++
++  /*
++   * ReleaseName
++   */
++  else if (!g_strcmp0 (member, "ReleaseName"))
++    {
++      if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++        {
++          GBusReleaseNameReplyFlags status;
++          gchar *name;
++
++          g_variant_get (body, "(&s)", &name);
++
++          status = _g_kdbus_ReleaseName (worker, name, &local_error);
++          if (local_error == NULL)
++            reply_body = g_variant_new ("(u)", status);
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'ReleaseName' has wrong args (expected s)");
++    }
++
++  /*
++   * ReloadConfig
++   */
++  else if (!g_strcmp0 (member, "ReloadConfig"))
++    {
++      if ((body == NULL) || g_variant_is_of_type (body, G_VARIANT_TYPE ("()")))
++        reply_body = g_variant_new ("()", NULL);
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'ReloadConfig' has wrong args");
++    }
++
++  /*
++   * RequestName
++   */
++  else if (!g_strcmp0 (member, "RequestName"))
++    {
++      if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(su)")))
++        {
++          GBusRequestNameReplyFlags status;
++          guint32 flags;
++          gchar *name;
++
++          g_variant_get (body, "(&su)", &name, &flags);
++
++          status = _g_kdbus_RequestName (worker, name, flags, &local_error);
++          if (local_error == NULL)
++            reply_body = g_variant_new ("(u)", status);
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'RequestName' has wrong args (expected su)");
++    }
++
++  /*
++   * StartServiceByName
++   */
++  else if (!g_strcmp0 (member, "StartServiceByName"))
++    {
++      if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(su)")))
++        {
++          GBusStartServiceReplyFlags status;
++          gchar *name;
++          guint32 flags;
++
++          g_variant_get (body, "(&su)", &name, &flags);
++
++          if (sync)
++            status = _g_kdbus_StartServiceByName (worker, name, flags, NULL, &local_error);
++          else
++            status = _g_kdbus_StartServiceByName (worker, name, flags, message, &local_error);
++
++          if (local_error == NULL)
++            {
++              if (status == G_BUS_START_SERVICE_REPLY_ALREADY_RUNNING || sync)
++                reply_body = g_variant_new ("(u)", status);
++              else
++                return NULL;
++            }
++        }
++      else
++        g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++                     "Call to 'StartServiceByName' has wrong args (expected su)");
++    }
++
++  /*
++   * UpdateActivationEnvironment
++   */
++  else if (!g_strcmp0 (member, "UpdateActivationEnvironment"))
++    {
++      g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED,
++                   "'%s' method not supported", member);
++    }
++
++  /*
++   * Method not supported
++   */
++  else
++    {
++      g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
++                   "org.freedesktop.DBus does not understand message %s", member);
++    }
++
++  if (reply_body == NULL && local_error)
++    {
++      gchar *dbus_error_name;
++
++      dbus_error_name = g_dbus_error_encode_gerror (local_error);
++      reply = g_dbus_message_new_method_error (message, dbus_error_name, "%s", local_error->message);
++      g_free (dbus_error_name);
++    }
++  else
++    {
++      reply = g_dbus_message_new_method_reply (message);
++      g_dbus_message_set_body (reply, reply_body);
++    }
++
++  g_dbus_message_set_serial (reply, -1);
++
++  if (local_error)
++    g_error_free (local_error);
++
++  if (G_UNLIKELY (_g_dbus_debug_message ()))
++    {
++      gchar *s;
++      _g_dbus_debug_print_lock ();
++      g_print ("========================================================================\n"
++               "GDBus-debug:Message:\n"
++               "  <<<< RECEIVED synthetic D-Bus message\n");
++      s = g_dbus_message_print (reply, 2);
++      g_print ("%s", s);
++      g_free (s);
++      _g_dbus_debug_print_unlock ();
++    }
++
++  return reply;
++}
+diff --git a/gio/gkdbusfakedaemon.h b/gio/gkdbusfakedaemon.h
+new file mode 100644
+index 0000000..969b7b5
+--- /dev/null
++++ b/gio/gkdbusfakedaemon.h
+@@ -0,0 +1,39 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright (C) 2015 Samsung Electronics
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Author: Lukasz Skalski       <l.skalski@samsung.com>
++ */
++
++#ifndef __G_KDBUSFAKEDAEMON_H__
++#define __G_KDBUSFAKEDAEMON_H__
++
++#if !defined (GIO_COMPILATION)
++#error "gkdbusfakedaemon.h is a private header file."
++#endif
++
++#include <gio/giotypes.h>
++
++gboolean              _is_message_to_dbus_daemon             (GDBusMessage  *message);
++
++GDBusMessage *        _dbus_daemon_synthetic_reply           (GKDBusWorker  *worker,
++                                                              GDBusMessage  *message,
++                                                              gboolean sync);
++G_END_DECLS
++
++#endif /* __G_KDBUSFAKEDAEMON_H__ */
+diff --git a/gio/gunixfdlist.h b/gio/gunixfdlist.h
+index df5587e..d5a9e8e 100644
+--- a/gio/gunixfdlist.h
++++ b/gio/gunixfdlist.h
+@@ -71,6 +71,9 @@ GIO_AVAILABLE_IN_ALL
+ GUnixFDList *           g_unix_fd_list_new_from_array                   (const gint   *fds,
+                                                                          gint          n_fds);
++GIO_AVAILABLE_IN_2_44
++void                    g_unix_fd_list_lock                             (GUnixFDList  *list);
++
+ GIO_AVAILABLE_IN_ALL
+ gint                    g_unix_fd_list_append                           (GUnixFDList  *list,
+                                                                          gint          fd,
+diff --git a/gio/meson.build b/gio/meson.build
+index a4e9b30..f230c05 100644
+--- a/gio/meson.build
++++ b/gio/meson.build
+@@ -231,6 +231,13 @@ gdbus_sources = files(
+   'gtestdbus.c',
+ )
++if get_option('kdbus')
++gdbus_sources += files(
++  'gkdbus.c',
++  'gkdbusfakedaemon.c',
++)
++endif
++
+ # Generate gdbus-codegen
+ subdir('gdbus-2.0/codegen')
+@@ -837,6 +844,16 @@ else
+   gio_dtrace_hdr = []
+ endif
++if get_option('kdbus')
++  gio_c_args += '-DKDBUS'
++endif
++
++libdbuspolicy_dep = dependency('', required : false)
++if get_option('libdbuspolicy')
++  libdbuspolicy_dep = dependency('libdbuspolicy1', version : '>=1', required : true)
++  gio_c_args += '-DLIBDBUSPOLICY'
++endif
++
+ libgio = library('gio-2.0',
+   gioenumtypes_h, gioenumtypes_c, gnetworking_h, gio_sources,
+   gio_dtrace_hdr, gio_dtrace_obj,
+@@ -849,7 +866,7 @@ libgio = library('gio-2.0',
+   link_with: internal_deps,
+   dependencies : [libz_dep, libdl_dep, libmount_dep, libglib_dep,
+                   libgobject_dep, libgmodule_dep, selinux_dep, xattr_dep,
+-                  platform_deps, network_libs, libsysprof_capture_dep,
++                  platform_deps, network_libs, libdbuspolicy_dep, libsysprof_capture_dep,
+                   gioenumtypes_dep, gvdb_dep],
+   c_args : [gio_c_args, gio_c_args_internal],
+   objc_args : [gio_c_args, gio_c_args_internal],
+diff --git a/meson_options.txt b/meson_options.txt
+index 517d575..7c95d03 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -93,6 +93,16 @@ option('nls',
+        yield: true,
+        description : 'Enable native language support (translations)')
++option('kdbus',
++       type : 'boolean',
++       value : false,
++       description : 'enable kdbus transport')
++
++option('libdbuspolicy',
++       type : 'boolean',
++       value : false,
++       description : 'enable libdbuspolicy for kdbus transport')
++
+ option('oss_fuzz',
+        type : 'feature',
+        value : 'disabled',
+-- 
+2.25.1
+