[gnome-shell/wip/hadess/import-gnome-bluetooth: 127/133] bluetooth: Import GnomeBluetoothClient into our C code




commit 7e304b38bcb632f9876d770141541689a2ab8fb6
Author: Bastien Nocera <hadess hadess net>
Date:   Tue Nov 2 14:55:09 2021 +0100

    bluetooth: Import GnomeBluetoothClient into our C code
    
    This is a minimal import that:
    - namespaces the source files and headers
    - namespaces the functions without caring about indentation
    - hardcodes a libudev dependency that will soon be removed

 src/meson.build                      |   14 +-
 src/shell-bluetooth-client-private.h |   66 ++
 src/shell-bluetooth-client.c         | 1765 ++++++++++++++++++++++++++++++++++
 src/shell-bluetooth-client.h         |   80 ++
 src/shell-bluetooth-client.xml       |   60 ++
 src/shell-bluetooth-enums.h          |  182 ++++
 src/shell-bluetooth-pin.c            |  239 +++++
 src/shell-bluetooth-pin.h            |   36 +
 src/shell-bluetooth-utils.c          |  430 +++++++++
 src/shell-bluetooth-utils.h          |   68 ++
 10 files changed, 2939 insertions(+), 1 deletion(-)
---
diff --git a/src/meson.build b/src/meson.build
index 53b8b527d5..95eb45fd35 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -48,6 +48,7 @@ gnome_shell_cflags = [
 
 install_rpath = ':'.join([mutter_typelibdir, pkglibdir])
 
+libudev_dep = dependency('libudev')
 gnome_shell_deps = [
   gio_unix_dep,
   libxml_dep,
@@ -61,7 +62,8 @@ gnome_shell_deps = [
   gi_dep,
   polkit_dep,
   gcr_dep,
-  libsystemd_dep
+  libsystemd_dep,
+  libudev_dep
 ]
 
 gnome_shell_deps += nm_deps
@@ -95,6 +97,8 @@ libshell_public_headers = [
   'shell-app.h',
   'shell-app-system.h',
   'shell-app-usage.h',
+  'shell-bluetooth-client.h',
+  'shell-bluetooth-enums.h',
   'shell-blur-effect.h',
   'shell-embedded-window.h',
   'shell-glsl-effect.h',
@@ -134,6 +138,9 @@ libshell_sources = [
   'shell-app.c',
   'shell-app-system.c',
   'shell-app-usage.c',
+  'shell-bluetooth-client.c',
+  'shell-bluetooth-utils.c',
+  'shell-bluetooth-pin.c',
   'shell-blur-effect.c',
   'shell-embedded-window.c',
   'shell-embedded-window-private.h',
@@ -195,6 +202,11 @@ dbus_generated += gnome.gdbus_codegen('switcheroo-control',
   namespace: 'Shell'
 )
 
+dbus_generated += gnome.gdbus_codegen('shell-bluetooth-client-glue',
+  'shell-bluetooth-client.xml',
+  interface_prefix: 'org.bluez',
+)
+
 libshell_no_gir_sources += dbus_generated
 
 libshell = library('gnome-shell',
diff --git a/src/shell-bluetooth-client-private.h b/src/shell-bluetooth-client-private.h
new file mode 100644
index 0000000000..56f8bc44d7
--- /dev/null
+++ b/src/shell-bluetooth-client-private.h
@@ -0,0 +1,66 @@
+/*
+ *
+ *  BlueZ - ShellBluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  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.1 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#pragma once
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <shell-bluetooth-enums.h>
+
+typedef void (*ShellBluetoothClientSetupFunc) (ShellBluetoothClient *client,
+                                         const GError    *error,
+                                         const char      *device_path);
+
+void shell_bluetooth_client_setup_device (ShellBluetoothClient          *client,
+                                   const char               *path,
+                                   gboolean                  pair,
+                                   GCancellable             *cancellable,
+                                   GAsyncReadyCallback       callback,
+                                   gpointer                  user_data);
+gboolean shell_bluetooth_client_setup_device_finish (ShellBluetoothClient  *client,
+                                              GAsyncResult     *res,
+                                              char            **path,
+                                              GError          **error);
+
+void shell_bluetooth_client_cancel_setup_device (ShellBluetoothClient     *client,
+                                          const char          *path,
+                                          GCancellable        *cancellable,
+                                          GAsyncReadyCallback  callback,
+                                          gpointer             user_data);
+gboolean shell_bluetooth_client_cancel_setup_device_finish (ShellBluetoothClient *client,
+                                                     GAsyncResult     *res,
+                                                     char            **path,
+                                                     GError          **error);
+
+gboolean shell_bluetooth_client_set_trusted(ShellBluetoothClient *client,
+                                       const char *device, gboolean trusted);
+
+GDBusProxy *shell_bluetooth_client_get_device (ShellBluetoothClient *client,
+                                        const char      *path);
+
+void shell_bluetooth_client_dump_device (GtkTreeModel *model,
+                                  GtkTreeIter *iter);
+
+gboolean shell_bluetooth_client_get_connectable(const char **uuids);
+
+GDBusProxy *_shell_bluetooth_client_get_default_adapter (ShellBluetoothClient *client);
diff --git a/src/shell-bluetooth-client.c b/src/shell-bluetooth-client.c
new file mode 100644
index 0000000000..d42e51dfd7
--- /dev/null
+++ b/src/shell-bluetooth-client.c
@@ -0,0 +1,1765 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2010       Giovanni Campagna <scampa giovanni gmail com>
+ *  Copyright (C) 2013       Intel Corporation.
+ *
+ *
+ *  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.1 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/**
+ * SECTION:shell-bluetooth-client
+ * @short_description: ShellBluetooth client object
+ * @stability: Stable
+ * @include: shell-bluetooth-client.h
+ *
+ * The #ShellBluetoothClient object is used to query the state of Bluetooth
+ * devices and adapters.
+ **/
+
+#include <config.h>
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include "shell-bluetooth-client.h"
+#include "shell-bluetooth-client-private.h"
+#include "shell-bluetooth-client-glue.h"
+#include "shell-bluetooth-utils.h"
+#include "shell-enum-types.h"
+#include "shell-bluetooth-pin.h"
+
+#define BLUEZ_SERVICE                  "org.bluez"
+#define BLUEZ_MANAGER_PATH             "/"
+#define BLUEZ_ADAPTER_INTERFACE                "org.bluez.Adapter1"
+#define BLUEZ_DEVICE_INTERFACE         "org.bluez.Device1"
+
+#define SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(obj) shell_bluetooth_client_get_instance_private (obj)
+
+typedef struct _ShellBluetoothClientPrivate ShellBluetoothClientPrivate;
+
+struct _ShellBluetoothClientPrivate {
+       GDBusObjectManager *manager;
+       GCancellable *cancellable;
+       GtkTreeStore *store;
+       GtkTreeRowReference *default_adapter;
+       /* Discoverable during discovery? */
+       gboolean disco_during_disco;
+       gboolean discovery_started;
+};
+
+enum {
+       PROP_0,
+       PROP_DEFAULT_ADAPTER,
+       PROP_DEFAULT_ADAPTER_POWERED,
+       PROP_DEFAULT_ADAPTER_DISCOVERABLE,
+       PROP_DEFAULT_ADAPTER_NAME,
+       PROP_DEFAULT_ADAPTER_DISCOVERING
+};
+
+enum {
+       DEVICE_REMOVED,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static const char *connectable_uuids[] = {
+       "HSP",
+       "AudioSource",
+       "AudioSink",
+       "A/V_RemoteControlTarget",
+       "A/V_RemoteControl",
+       "Headset_-_AG",
+       "Handsfree",
+       "HandsfreeAudioGateway",
+       "HumanInterfaceDeviceService",
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(ShellBluetoothClient, shell_bluetooth_client, G_TYPE_OBJECT)
+
+typedef gboolean (*IterSearchFunc) (GtkTreeStore *store,
+                               GtkTreeIter *iter, gpointer user_data);
+
+static gboolean iter_search(GtkTreeStore *store,
+                               GtkTreeIter *iter, GtkTreeIter *parent,
+                               IterSearchFunc func, gpointer user_data)
+{
+       gboolean cont, found = FALSE;
+
+       if (parent == NULL)
+               cont = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store),
+                                                                       iter);
+       else
+               cont = gtk_tree_model_iter_children(GTK_TREE_MODEL(store),
+                                                               iter, parent);
+
+       while (cont == TRUE) {
+               GtkTreeIter child;
+
+               found = func(store, iter, user_data);
+               if (found == TRUE)
+                       break;
+
+               found = iter_search(store, &child, iter, func, user_data);
+               if (found == TRUE) {
+                       *iter = child;
+                       break;
+               }
+
+               cont = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), iter);
+       }
+
+       return found;
+}
+
+static gboolean
+compare_path (GtkTreeStore *store,
+             GtkTreeIter *iter,
+             gpointer user_data)
+{
+       const gchar *path = user_data;
+       g_autoptr(GDBusProxy) object = NULL;
+
+       gtk_tree_model_get (GTK_TREE_MODEL(store), iter,
+                           SHELL_BLUETOOTH_COLUMN_PROXY, &object,
+                           -1);
+
+       return (object != NULL &&
+               g_str_equal (path, g_dbus_proxy_get_object_path (object)));
+}
+
+static gboolean
+compare_address (GtkTreeStore *store,
+                GtkTreeIter *iter,
+                gpointer user_data)
+{
+       const char *address = user_data;
+       g_autofree char *tmp_address = NULL;
+
+       gtk_tree_model_get (GTK_TREE_MODEL(store), iter,
+                           SHELL_BLUETOOTH_COLUMN_ADDRESS, &tmp_address, -1);
+       return (g_strcmp0 (address, tmp_address) == 0);
+}
+
+static gboolean
+get_iter_from_path (GtkTreeStore *store,
+                   GtkTreeIter  *iter,
+                   const char   *path)
+{
+       g_return_val_if_fail (path != NULL, FALSE);
+       return iter_search(store, iter, NULL, compare_path, (gpointer) path);
+}
+
+static gboolean
+get_iter_from_proxy(GtkTreeStore *store,
+                   GtkTreeIter *iter,
+                   GDBusProxy *proxy)
+{
+       g_return_val_if_fail (proxy != NULL, FALSE);
+       return iter_search(store, iter, NULL, compare_path,
+                          (gpointer) g_dbus_proxy_get_object_path (proxy));
+}
+
+static gboolean
+get_iter_from_address (GtkTreeStore *store,
+                      GtkTreeIter  *iter,
+                      const char   *address,
+                      GDBusProxy   *adapter)
+{
+       GtkTreeIter parent_iter;
+
+       g_return_val_if_fail (address != NULL, FALSE);
+       g_return_val_if_fail (adapter != NULL, FALSE);
+
+       if (get_iter_from_proxy (store, &parent_iter, adapter) == FALSE)
+               return FALSE;
+
+       return iter_search (store, iter, &parent_iter, compare_address, (gpointer) address);
+}
+
+static char **
+device_list_uuids (const gchar * const *uuids)
+{
+       GPtrArray *ret;
+       guint i;
+
+       if (uuids == NULL)
+               return NULL;
+
+       ret = g_ptr_array_new ();
+
+       for (i = 0; uuids[i] != NULL; i++) {
+               const char *uuid;
+
+               uuid = shell_bluetooth_uuid_to_string (uuids[i]);
+               if (uuid == NULL)
+                       continue;
+               g_ptr_array_add (ret, g_strdup (uuid));
+       }
+
+       if (ret->len == 0) {
+               g_ptr_array_free (ret, TRUE);
+               return NULL;
+       }
+
+       g_ptr_array_add (ret, NULL);
+
+       return (char **) g_ptr_array_free (ret, FALSE);
+}
+
+gboolean
+shell_bluetooth_client_get_connectable(const char **uuids)
+{
+       int i, j;
+
+       for (i = 0; uuids && uuids[i] != NULL; i++) {
+               for (j = 0; j < G_N_ELEMENTS (connectable_uuids); j++) {
+                       if (g_str_equal (connectable_uuids[j], uuids[i]))
+                               return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+static const char *
+phone_oui_to_icon_name (const char *bdaddr)
+{
+       char *vendor;
+       const char *ret = NULL;
+
+       vendor = oui_to_vendor (bdaddr);
+       if (vendor == NULL)
+               return NULL;
+
+       if (strstr (vendor, "Apple") != NULL)
+               ret = "phone-apple-iphone";
+       else if (strstr (vendor, "Samsung") != NULL)
+               ret = "phone-samsung-galaxy-s";
+       else if (strstr (vendor, "Google") != NULL)
+               ret = "phone-google-nexus-one";
+       g_free (vendor);
+
+       return ret;
+}
+
+static const char *
+icon_override (const char    *bdaddr,
+              ShellBluetoothType  type)
+{
+       /* audio-card, you're ugly */
+       switch (type) {
+       case SHELL_BLUETOOTH_TYPE_HEADSET:
+               return "audio-headset";
+       case SHELL_BLUETOOTH_TYPE_HEADPHONES:
+               return "audio-headphones";
+       case SHELL_BLUETOOTH_TYPE_OTHER_AUDIO:
+               return "audio-speakers";
+       case SHELL_BLUETOOTH_TYPE_PHONE:
+               return phone_oui_to_icon_name (bdaddr);
+       case SHELL_BLUETOOTH_TYPE_DISPLAY:
+               return "video-display";
+       case SHELL_BLUETOOTH_TYPE_SCANNER:
+               return "scanner";
+       case SHELL_BLUETOOTH_TYPE_REMOTE_CONTROL:
+       case SHELL_BLUETOOTH_TYPE_WEARABLE:
+       case SHELL_BLUETOOTH_TYPE_TOY:
+               /* FIXME */
+       default:
+               return NULL;
+       }
+}
+
+static void
+device_resolve_type_and_icon (Device1 *device, ShellBluetoothType *type, const char **icon)
+{
+       g_return_if_fail (type);
+       g_return_if_fail (icon);
+
+       if (g_strcmp0 (device1_get_name (device), "ION iCade Game Controller") == 0 ||
+           g_strcmp0 (device1_get_name (device), "8Bitdo Zero GamePad") == 0) {
+               *type = SHELL_BLUETOOTH_TYPE_JOYPAD;
+               *icon = "input-gaming";
+               return;
+       }
+
+       if (*type == 0 || *type == SHELL_BLUETOOTH_TYPE_ANY)
+               *type = shell_bluetooth_appearance_to_type (device1_get_appearance (device));
+       if (*type == 0 || *type == SHELL_BLUETOOTH_TYPE_ANY)
+               *type = shell_bluetooth_class_to_type (device1_get_class (device));
+
+       *icon = icon_override (device1_get_address (device), *type);
+
+       if (!*icon)
+               *icon = device1_get_icon (device);
+
+       if (!*icon)
+               *icon = "bluetooth";
+}
+
+static void
+device_notify_cb (Device1         *device,
+                 GParamSpec      *pspec,
+                 ShellBluetoothClient *client)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       const char *property = g_param_spec_get_name (pspec);
+       GtkTreeIter iter;
+
+       if (get_iter_from_proxy (priv->store, &iter, G_DBUS_PROXY (device)) == FALSE)
+               return;
+
+       if (g_strcmp0 (property, "name") == 0) {
+               const gchar *name = device1_get_name (device);
+
+               gtk_tree_store_set (priv->store, &iter,
+                                   SHELL_BLUETOOTH_COLUMN_NAME, name, -1);
+       } else if (g_strcmp0 (property, "alias") == 0) {
+               const gchar *alias = device1_get_alias (device);
+
+               gtk_tree_store_set (priv->store, &iter,
+                                   SHELL_BLUETOOTH_COLUMN_ALIAS, alias, -1);
+       } else if (g_strcmp0 (property, "paired") == 0) {
+               gboolean paired = device1_get_paired (device);
+
+               gtk_tree_store_set (priv->store, &iter,
+                                   SHELL_BLUETOOTH_COLUMN_PAIRED, paired, -1);
+       } else if (g_strcmp0 (property, "trusted") == 0) {
+               gboolean trusted = device1_get_trusted (device);
+
+               gtk_tree_store_set (priv->store, &iter,
+                                   SHELL_BLUETOOTH_COLUMN_TRUSTED, trusted, -1);
+       } else if (g_strcmp0 (property, "connected") == 0) {
+               gboolean connected = device1_get_connected (device);
+
+               gtk_tree_store_set (priv->store, &iter,
+                                   SHELL_BLUETOOTH_COLUMN_CONNECTED, connected, -1);
+       } else if (g_strcmp0 (property, "uuids") == 0) {
+               char **uuids;
+
+               uuids = device_list_uuids (device1_get_uuids (device));
+
+               gtk_tree_store_set (priv->store, &iter,
+                                   SHELL_BLUETOOTH_COLUMN_UUIDS, uuids, -1);
+               g_strfreev (uuids);
+       } else if (g_strcmp0 (property, "legacy-pairing") == 0) {
+               gboolean legacypairing = device1_get_legacy_pairing (device);
+
+               gtk_tree_store_set (priv->store, &iter,
+                                   SHELL_BLUETOOTH_COLUMN_LEGACYPAIRING, legacypairing,
+                                   -1);
+       } else if (g_strcmp0 (property, "icon") == 0 ||
+                  g_strcmp0 (property, "class") == 0 ||
+                  g_strcmp0 (property, "appearance") == 0) {
+               ShellBluetoothType type = SHELL_BLUETOOTH_TYPE_ANY;
+               const char *icon = NULL;
+
+               device_resolve_type_and_icon (device, &type, &icon);
+
+               gtk_tree_store_set (priv->store, &iter,
+                                   SHELL_BLUETOOTH_COLUMN_TYPE, type,
+                                   SHELL_BLUETOOTH_COLUMN_ICON, icon,
+                                   -1);
+       } else {
+               g_debug ("Unhandled property: %s", property);
+       }
+}
+
+static void
+device_added (GDBusObjectManager   *manager,
+             Device1              *device,
+             ShellBluetoothClient      *client)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       GDBusProxy *adapter;
+       const char *adapter_path, *address, *alias, *name, *icon;
+       char **uuids;
+       gboolean paired, trusted, connected;
+       int legacypairing;
+       ShellBluetoothType type = SHELL_BLUETOOTH_TYPE_ANY;
+       GtkTreeIter iter, parent;
+
+       g_signal_connect_object (G_OBJECT (device), "notify",
+                                G_CALLBACK (device_notify_cb), client, 0);
+
+       adapter_path = device1_get_adapter (device);
+       address = device1_get_address (device);
+       alias = device1_get_alias (device);
+       name = device1_get_name (device);
+       paired = device1_get_paired (device);
+       trusted = device1_get_trusted (device);
+       connected = device1_get_connected (device);
+       uuids = device_list_uuids (device1_get_uuids (device));
+       legacypairing = device1_get_legacy_pairing (device);
+
+       device_resolve_type_and_icon (device, &type, &icon);
+
+       if (get_iter_from_path (priv->store, &parent, adapter_path) == FALSE)
+               return;
+
+       gtk_tree_model_get (GTK_TREE_MODEL(priv->store), &parent,
+                           SHELL_BLUETOOTH_COLUMN_PROXY, &adapter, -1);
+
+       if (get_iter_from_address (priv->store, &iter, address, adapter) == FALSE) {
+               gtk_tree_store_insert_with_values (priv->store, &iter, &parent, -1,
+                                                  SHELL_BLUETOOTH_COLUMN_ADDRESS, address,
+                                                  SHELL_BLUETOOTH_COLUMN_ALIAS, alias,
+                                                  SHELL_BLUETOOTH_COLUMN_NAME, name,
+                                                  SHELL_BLUETOOTH_COLUMN_TYPE, type,
+                                                  SHELL_BLUETOOTH_COLUMN_ICON, icon,
+                                                  SHELL_BLUETOOTH_COLUMN_LEGACYPAIRING, legacypairing,
+                                                  SHELL_BLUETOOTH_COLUMN_UUIDS, uuids,
+                                                  SHELL_BLUETOOTH_COLUMN_PAIRED, paired,
+                                                  SHELL_BLUETOOTH_COLUMN_CONNECTED, connected,
+                                                  SHELL_BLUETOOTH_COLUMN_TRUSTED, trusted,
+                                                  SHELL_BLUETOOTH_COLUMN_PROXY, device,
+                                                  -1);
+       } else {
+               gtk_tree_store_set(priv->store, &iter,
+                                  SHELL_BLUETOOTH_COLUMN_ADDRESS, address,
+                                  SHELL_BLUETOOTH_COLUMN_ALIAS, alias,
+                                  SHELL_BLUETOOTH_COLUMN_NAME, name,
+                                  SHELL_BLUETOOTH_COLUMN_TYPE, type,
+                                  SHELL_BLUETOOTH_COLUMN_ICON, icon,
+                                  SHELL_BLUETOOTH_COLUMN_LEGACYPAIRING, legacypairing,
+                                  SHELL_BLUETOOTH_COLUMN_UUIDS, uuids,
+                                  SHELL_BLUETOOTH_COLUMN_PAIRED, paired,
+                                  SHELL_BLUETOOTH_COLUMN_CONNECTED, connected,
+                                  SHELL_BLUETOOTH_COLUMN_TRUSTED, trusted,
+                                  SHELL_BLUETOOTH_COLUMN_PROXY, device,
+                                  -1);
+       }
+       g_strfreev (uuids);
+       g_object_unref (adapter);
+}
+
+static void
+device_removed (const char      *path,
+               ShellBluetoothClient *client)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       GtkTreeIter iter;
+
+       if (get_iter_from_path(priv->store, &iter, path) == TRUE) {
+               /* Note that removal can also happen from adapter_removed. */
+               g_signal_emit (G_OBJECT (client), signals[DEVICE_REMOVED], 0, path);
+               gtk_tree_store_remove(priv->store, &iter);
+       }
+}
+
+static void
+adapter_set_powered_cb (GDBusProxy *proxy,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+{
+       g_autoptr(GError) error = NULL;
+       g_autoptr(GVariant) ret = NULL;
+
+       ret = g_dbus_proxy_call_finish (proxy, res, &error);
+       if (!ret) {
+               g_debug ("Error setting property 'Powered' on interface org.bluez.Adapter1: %s (%s, %d)",
+                        error->message, g_quark_to_string (error->domain), error->code);
+       }
+}
+
+static void
+adapter_set_powered (ShellBluetoothClient *client,
+                    const char *path,
+                    gboolean powered)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       g_autoptr(GObject) adapter = NULL;
+       GtkTreeIter iter;
+       GVariant *variant;
+
+       g_return_if_fail (SHELL_BLUETOOTH_IS_CLIENT (client));
+
+       if (get_iter_from_path (priv->store, &iter, path) == FALSE)
+               return;
+
+       gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
+                           SHELL_BLUETOOTH_COLUMN_PROXY, &adapter, -1);
+
+       if (adapter == NULL)
+               return;
+
+       variant = g_variant_new_boolean (powered);
+       g_dbus_proxy_call (G_DBUS_PROXY (adapter),
+                          "org.freedesktop.DBus.Properties.Set",
+                          g_variant_new ("(ssv)", "org.bluez.Adapter1", "Powered", variant),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          -1,
+                          NULL, (GAsyncReadyCallback) adapter_set_powered_cb, client);
+}
+
+static void
+default_adapter_changed (GDBusObjectManager   *manager,
+                        const char           *path,
+                        ShellBluetoothClient      *client)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       GtkTreeIter iter;
+       GtkTreePath *tree_path;
+       gboolean powered;
+
+       g_assert (!priv->default_adapter);
+
+       if (get_iter_from_path (priv->store, &iter, path) == FALSE)
+               return;
+
+       tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->store), &iter);
+       priv->default_adapter = gtk_tree_row_reference_new (GTK_TREE_MODEL (priv->store), tree_path);
+       gtk_tree_path_free (tree_path);
+
+       gtk_tree_store_set (priv->store, &iter,
+                           SHELL_BLUETOOTH_COLUMN_DEFAULT, TRUE, -1);
+
+       gtk_tree_model_get (GTK_TREE_MODEL(priv->store), &iter,
+                          SHELL_BLUETOOTH_COLUMN_POWERED, &powered, -1);
+
+       if (powered) {
+               g_object_notify (G_OBJECT (client), "default-adapter");
+               g_object_notify (G_OBJECT (client), "default-adapter-powered");
+               g_object_notify (G_OBJECT (client), "default-adapter-discoverable");
+               g_object_notify (G_OBJECT (client), "default-adapter-discovering");
+               g_object_notify (G_OBJECT (client), "default-adapter-name");
+               return;
+       }
+
+       /*
+        * If the adapter is turn off (Powered = False in bluetooth) object
+        * notifications will be sent only when a Powered = True signal arrives
+        * from bluetoothd
+        */
+       adapter_set_powered (client, path, TRUE);
+}
+
+static void
+adapter_notify_cb (Adapter1       *adapter,
+                  GParamSpec     *pspec,
+                  ShellBluetoothClient *client)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       const char *property = g_param_spec_get_name (pspec);
+       GtkTreeIter iter;
+       gboolean notify = TRUE;
+       gboolean is_default;
+
+       if (get_iter_from_proxy (priv->store, &iter, G_DBUS_PROXY (adapter)) == FALSE)
+               return;
+
+       gtk_tree_model_get (GTK_TREE_MODEL(priv->store), &iter,
+                           SHELL_BLUETOOTH_COLUMN_DEFAULT, &is_default, -1);
+
+       if (g_strcmp0 (property, "alias") == 0) {
+               const gchar *alias = adapter1_get_alias (adapter);
+
+               gtk_tree_store_set (priv->store, &iter,
+                                   SHELL_BLUETOOTH_COLUMN_ALIAS, alias, -1);
+
+               if (is_default) {
+                       g_object_notify (G_OBJECT (client), "default-adapter-powered");
+                       g_object_notify (G_OBJECT (client), "default-adapter-name");
+               }
+       } else if (g_strcmp0 (property, "discovering") == 0) {
+               gboolean discovering = adapter1_get_discovering (adapter);
+
+               gtk_tree_store_set (priv->store, &iter,
+                                   SHELL_BLUETOOTH_COLUMN_DISCOVERING, discovering, -1);
+
+               if (is_default)
+                       g_object_notify (G_OBJECT (client), "default-adapter-discovering");
+       } else if (g_strcmp0 (property, "powered") == 0) {
+               gboolean powered = adapter1_get_powered (adapter);
+
+               gtk_tree_store_set (priv->store, &iter,
+                                   SHELL_BLUETOOTH_COLUMN_POWERED, powered, -1);
+
+               if (is_default && powered) {
+                       g_object_notify (G_OBJECT (client), "default-adapter");
+                       g_object_notify (G_OBJECT (client), "default-adapter-discoverable");
+                       g_object_notify (G_OBJECT (client), "default-adapter-discovering");
+                       g_object_notify (G_OBJECT (client), "default-adapter-name");
+               }
+               g_object_notify (G_OBJECT (client), "default-adapter-powered");
+       } else if (g_strcmp0 (property, "discoverable") == 0) {
+               gboolean discoverable = adapter1_get_discoverable (adapter);
+
+               gtk_tree_store_set (priv->store, &iter,
+                                   SHELL_BLUETOOTH_COLUMN_DISCOVERABLE, discoverable, -1);
+
+               if (is_default)
+                       g_object_notify (G_OBJECT (client), "default-adapter-discoverable");
+       } else {
+               notify = FALSE;
+       }
+
+       if (notify != FALSE) {
+               GtkTreePath *path;
+
+               /* Tell the world */
+               path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->store), &iter);
+               gtk_tree_model_row_changed (GTK_TREE_MODEL (priv->store), path, &iter);
+               gtk_tree_path_free (path);
+       }
+}
+
+static void
+adapter_added (GDBusObjectManager   *manager,
+              Adapter1             *adapter,
+              ShellBluetoothClient      *client)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       GtkTreeIter iter;
+       const gchar *address, *name, *alias;
+       gboolean discovering, discoverable, powered;
+
+       g_signal_connect_object (G_OBJECT (adapter), "notify",
+                                G_CALLBACK (adapter_notify_cb), client, 0);
+
+       address = adapter1_get_address (adapter);
+       name = adapter1_get_name (adapter);
+       alias = adapter1_get_alias (adapter);
+       discovering = adapter1_get_discovering (adapter);
+       powered = adapter1_get_powered (adapter);
+       discoverable = adapter1_get_discoverable (adapter);
+
+       gtk_tree_store_insert_with_values(priv->store, &iter, NULL, -1,
+                                         SHELL_BLUETOOTH_COLUMN_PROXY, adapter,
+                                         SHELL_BLUETOOTH_COLUMN_ADDRESS, address,
+                                         SHELL_BLUETOOTH_COLUMN_NAME, name,
+                                         SHELL_BLUETOOTH_COLUMN_ALIAS, alias,
+                                         SHELL_BLUETOOTH_COLUMN_DISCOVERING, discovering,
+                                         SHELL_BLUETOOTH_COLUMN_DISCOVERABLE, discoverable,
+                                         SHELL_BLUETOOTH_COLUMN_POWERED, powered,
+                                         -1);
+
+       if (!priv->default_adapter) {
+               default_adapter_changed (manager,
+                                        g_dbus_object_get_object_path (g_dbus_interface_get_object 
(G_DBUS_INTERFACE (adapter))),
+                                        client);
+       }
+}
+
+static void
+adapter_removed (GDBusObjectManager   *manager,
+                const char           *path,
+                ShellBluetoothClient      *client)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       GtkTreeIter iter, childiter;
+       gboolean was_default;
+       gboolean have_child;
+
+       if (get_iter_from_path (priv->store, &iter, path) == FALSE)
+               return;
+
+       gtk_tree_model_get (GTK_TREE_MODEL(priv->store), &iter,
+                          SHELL_BLUETOOTH_COLUMN_DEFAULT, &was_default, -1);
+
+       if (!was_default)
+               return;
+
+       /* Ensure that all devices are removed. This can happen if bluetoothd
+        * crashes as the "object-removed" signal is emitted in an undefined
+        * order. */
+       have_child = gtk_tree_model_iter_children (GTK_TREE_MODEL (priv->store), &childiter, &iter);
+       while (have_child) {
+               GDBusProxy *object;
+
+               gtk_tree_model_get (GTK_TREE_MODEL(priv->store), &childiter,
+                                   SHELL_BLUETOOTH_COLUMN_PROXY, &object, -1);
+
+               g_signal_emit (G_OBJECT (client), signals[DEVICE_REMOVED], 0, g_dbus_proxy_get_object_path 
(object));
+               g_object_unref (object);
+
+               have_child = gtk_tree_store_remove (priv->store, &childiter);
+       }
+
+       g_clear_pointer (&priv->default_adapter, gtk_tree_row_reference_free);
+       gtk_tree_store_remove (priv->store, &iter);
+
+       if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL(priv->store),
+                                          &iter)) {
+               GDBusProxy *adapter;
+               const char *adapter_path;
+
+               gtk_tree_model_get (GTK_TREE_MODEL(priv->store), &iter,
+                                  SHELL_BLUETOOTH_COLUMN_PROXY, &adapter, -1);
+
+               adapter_path = g_dbus_proxy_get_object_path (adapter);
+               default_adapter_changed (manager, adapter_path, client);
+
+               g_object_unref(adapter);
+       } else {
+               g_object_notify (G_OBJECT (client), "default-adapter");
+               g_object_notify (G_OBJECT (client), "default-adapter-powered");
+               g_object_notify (G_OBJECT (client), "default-adapter-discoverable");
+               g_object_notify (G_OBJECT (client), "default-adapter-discovering");
+       }
+}
+
+static GType
+object_manager_get_proxy_type_func (GDBusObjectManagerClient *manager,
+                                   const gchar              *object_path,
+                                   const gchar              *interface_name,
+                                   gpointer                  user_data)
+{
+       if (interface_name == NULL)
+               return G_TYPE_DBUS_OBJECT_PROXY;
+
+       if (g_str_equal (interface_name, BLUEZ_DEVICE_INTERFACE))
+               return TYPE_DEVICE1_PROXY;
+       if (g_str_equal (interface_name, BLUEZ_ADAPTER_INTERFACE))
+               return TYPE_ADAPTER1_PROXY;
+
+       return G_TYPE_DBUS_PROXY;
+}
+
+static void
+interface_added (GDBusObjectManager *manager,
+                GDBusObject        *object,
+                GDBusInterface     *interface,
+                gpointer            user_data)
+{
+       ShellBluetoothClient *client = user_data;
+
+       if (IS_ADAPTER1 (interface)) {
+               adapter_added (manager,
+                              ADAPTER1 (interface),
+                              client);
+       } else if (IS_DEVICE1 (interface)) {
+               device_added (manager,
+                             DEVICE1 (interface),
+                             client);
+       }
+}
+
+static void
+interface_removed (GDBusObjectManager *manager,
+                  GDBusObject        *object,
+                  GDBusInterface     *interface,
+                  gpointer            user_data)
+{
+       ShellBluetoothClient *client = user_data;
+
+       if (IS_ADAPTER1 (interface)) {
+               adapter_removed (manager,
+                                g_dbus_object_get_object_path (object),
+                                client);
+       } else if (IS_DEVICE1 (interface)) {
+               device_removed (g_dbus_object_get_object_path (object),
+                               client);
+       }
+}
+
+static void
+object_added (GDBusObjectManager *manager,
+             GDBusObject        *object,
+             ShellBluetoothClient    *client)
+{
+       GList *interfaces, *l;
+
+       interfaces = g_dbus_object_get_interfaces (object);
+
+       for (l = interfaces; l != NULL; l = l->next)
+               interface_added (manager, object, G_DBUS_INTERFACE (l->data), client);
+
+       g_list_free_full (interfaces, g_object_unref);
+}
+
+static void
+object_removed (GDBusObjectManager *manager,
+               GDBusObject        *object,
+               ShellBluetoothClient    *client)
+{
+       GList *interfaces, *l;
+
+       interfaces = g_dbus_object_get_interfaces (object);
+
+       for (l = interfaces; l != NULL; l = l->next)
+               interface_removed (manager, object, G_DBUS_INTERFACE (l->data), client);
+
+       g_list_free_full (interfaces, g_object_unref);
+}
+
+static void
+object_manager_new_callback(GObject      *source_object,
+                           GAsyncResult *res,
+                           void         *user_data)
+{
+       ShellBluetoothClient *client;
+       ShellBluetoothClientPrivate *priv;
+       GDBusObjectManager *manager;
+       GList *object_list, *l;
+       GError *error = NULL;
+
+       manager = g_dbus_object_manager_client_new_for_bus_finish (res, &error);
+       if (!manager) {
+               if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+                       g_warning ("Could not create bluez object manager: %s", error->message);
+               g_error_free (error);
+               return;
+       }
+
+       client = SHELL_BLUETOOTH_CLIENT (user_data);
+       priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       priv->manager = manager;
+
+       g_signal_connect_object (G_OBJECT (priv->manager), "interface-added", (GCallback) interface_added, 
client, 0);
+       g_signal_connect_object (G_OBJECT (priv->manager), "interface-removed", (GCallback) 
interface_removed, client, 0);
+
+       g_signal_connect_object (G_OBJECT (priv->manager), "object-added", (GCallback) object_added, client, 
0);
+       g_signal_connect_object (G_OBJECT (priv->manager), "object-removed", (GCallback) object_removed, 
client, 0);
+
+       object_list = g_dbus_object_manager_get_objects (priv->manager);
+
+       /* We need to add the adapters first, otherwise the devices will
+        * be dropped to the floor, as they wouldn't have a parent in
+        * the treestore */
+       for (l = object_list; l != NULL; l = l->next) {
+               GDBusObject *object = l->data;
+               GDBusInterface *iface;
+
+               iface = g_dbus_object_get_interface (object, BLUEZ_ADAPTER_INTERFACE);
+               if (!iface)
+                       continue;
+
+               adapter_added (priv->manager,
+                              ADAPTER1 (iface),
+                              client);
+       }
+
+       for (l = object_list; l != NULL; l = l->next) {
+               GDBusObject *object = l->data;
+               GDBusInterface *iface;
+
+               iface = g_dbus_object_get_interface (object, BLUEZ_DEVICE_INTERFACE);
+               if (!iface)
+                       continue;
+
+               device_added (priv->manager,
+                             DEVICE1 (iface),
+                             client);
+       }
+       g_list_free_full (object_list, g_object_unref);
+}
+
+static void shell_bluetooth_client_init(ShellBluetoothClient *client)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+
+       priv->cancellable = g_cancellable_new ();
+       priv->store = gtk_tree_store_new(_SHELL_BLUETOOTH_NUM_COLUMNS,
+                                        G_TYPE_OBJECT,     /* SHELL_BLUETOOTH_COLUMN_PROXY */
+                                        G_TYPE_OBJECT,     /* SHELL_BLUETOOTH_COLUMN_PROPERTIES */
+                                        G_TYPE_STRING,     /* SHELL_BLUETOOTH_COLUMN_ADDRESS */
+                                        G_TYPE_STRING,     /* SHELL_BLUETOOTH_COLUMN_ALIAS */
+                                        G_TYPE_STRING,     /* SHELL_BLUETOOTH_COLUMN_NAME */
+                                        G_TYPE_UINT,       /* SHELL_BLUETOOTH_COLUMN_TYPE */
+                                        G_TYPE_STRING,     /* SHELL_BLUETOOTH_COLUMN_ICON */
+                                        G_TYPE_BOOLEAN,    /* SHELL_BLUETOOTH_COLUMN_DEFAULT */
+                                        G_TYPE_BOOLEAN,    /* SHELL_BLUETOOTH_COLUMN_PAIRED */
+                                        G_TYPE_BOOLEAN,    /* SHELL_BLUETOOTH_COLUMN_TRUSTED */
+                                        G_TYPE_BOOLEAN,    /* SHELL_BLUETOOTH_COLUMN_CONNECTED */
+                                        G_TYPE_BOOLEAN,    /* SHELL_BLUETOOTH_COLUMN_DISCOVERABLE */
+                                        G_TYPE_BOOLEAN,    /* SHELL_BLUETOOTH_COLUMN_DISCOVERING */
+                                        G_TYPE_INT,        /* SHELL_BLUETOOTH_COLUMN_LEGACYPAIRING */
+                                        G_TYPE_BOOLEAN,    /* SHELL_BLUETOOTH_COLUMN_POWERED */
+                                        G_TYPE_HASH_TABLE, /* SHELL_BLUETOOTH_COLUMN_SERVICES */
+                                        G_TYPE_STRV);      /* SHELL_BLUETOOTH_COLUMN_UUIDS */
+
+       g_dbus_object_manager_client_new_for_bus (G_BUS_TYPE_SYSTEM,
+                                                 G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
+                                                 BLUEZ_SERVICE,
+                                                 BLUEZ_MANAGER_PATH,
+                                                 object_manager_get_proxy_type_func,
+                                                 NULL, NULL,
+                                                 priv->cancellable,
+                                                 object_manager_new_callback, client);
+}
+
+GDBusProxy *
+_shell_bluetooth_client_get_default_adapter(ShellBluetoothClient *client)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       GtkTreePath *path;
+       GtkTreeIter iter;
+       GDBusProxy *adapter;
+
+       g_return_val_if_fail (SHELL_BLUETOOTH_IS_CLIENT (client), NULL);
+
+       if (priv->default_adapter == NULL)
+               return NULL;
+
+       path = gtk_tree_row_reference_get_path (priv->default_adapter);
+       gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->store), &iter, path);
+       gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
+                           SHELL_BLUETOOTH_COLUMN_PROXY, &adapter, -1);
+       gtk_tree_path_free (path);
+
+       return adapter;
+}
+
+static const char*
+_shell_bluetooth_client_get_default_adapter_path (ShellBluetoothClient *self)
+{
+       GDBusProxy *adapter = _shell_bluetooth_client_get_default_adapter (self);
+
+       if (adapter != NULL) {
+               const char *ret = g_dbus_proxy_get_object_path (adapter);
+               g_object_unref (adapter);
+               return ret;
+       }
+       return NULL;
+}
+
+static gboolean
+_shell_bluetooth_client_get_default_adapter_powered (ShellBluetoothClient *self)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE (self);
+       GtkTreePath *path;
+       GtkTreeIter iter;
+       gboolean ret;
+
+       if (priv->default_adapter == NULL)
+               return FALSE;
+
+       path = gtk_tree_row_reference_get_path (priv->default_adapter);
+       gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->store), &iter, path);
+       gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter, SHELL_BLUETOOTH_COLUMN_POWERED, &ret, -1);
+       gtk_tree_path_free (path);
+
+       return ret;
+}
+
+static char *
+_shell_bluetooth_client_get_default_adapter_name (ShellBluetoothClient *self)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE (self);
+       GtkTreePath *path;
+       GtkTreeIter iter;
+       char *ret;
+
+       if (priv->default_adapter == NULL)
+               return NULL;
+
+       path = gtk_tree_row_reference_get_path (priv->default_adapter);
+       gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->store), &iter, path);
+       gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter, SHELL_BLUETOOTH_COLUMN_ALIAS, &ret, -1);
+       gtk_tree_path_free (path);
+
+       return ret;
+}
+
+static void
+_shell_bluetooth_client_set_default_adapter_discovering (ShellBluetoothClient *client,
+                                                  gboolean         discovering,
+                                                  gboolean         discoverable)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE (client);
+       g_autoptr(GDBusProxy) adapter = NULL;
+       GVariantBuilder builder;
+
+       adapter = _shell_bluetooth_client_get_default_adapter (client);
+       if (adapter == NULL)
+               return;
+
+       if (discovering) {
+               g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+               g_variant_builder_add (&builder, "{sv}",
+                                      "Discoverable", g_variant_new_boolean (discoverable));
+               adapter1_call_set_discovery_filter_sync (ADAPTER1 (adapter),
+                                                        g_variant_builder_end (&builder), NULL, NULL);
+       }
+
+       priv->discovery_started = discovering;
+       if (discovering)
+               adapter1_call_start_discovery (ADAPTER1 (adapter), NULL, NULL, NULL);
+       else
+               adapter1_call_stop_discovery (ADAPTER1 (adapter), NULL, NULL, NULL);
+}
+
+static gboolean
+_shell_bluetooth_client_get_default_adapter_discovering (ShellBluetoothClient *self)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE (self);
+       GtkTreePath *path;
+       GtkTreeIter iter;
+       gboolean ret;
+
+       if (priv->default_adapter == NULL)
+               return FALSE;
+
+       path = gtk_tree_row_reference_get_path (priv->default_adapter);
+       gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->store), &iter, path);
+       gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter, SHELL_BLUETOOTH_COLUMN_DISCOVERING, &ret, 
-1);
+       gtk_tree_path_free (path);
+
+       return ret;
+}
+
+static void
+shell_bluetooth_client_get_property (GObject        *object,
+                              guint           property_id,
+                              GValue         *value,
+                              GParamSpec     *pspec)
+{
+       ShellBluetoothClient *self = SHELL_BLUETOOTH_CLIENT (object);
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE (self);
+
+       switch (property_id) {
+       case PROP_DEFAULT_ADAPTER:
+               g_value_set_string (value, _shell_bluetooth_client_get_default_adapter_path (self));
+               break;
+       case PROP_DEFAULT_ADAPTER_POWERED:
+               g_value_set_boolean (value, _shell_bluetooth_client_get_default_adapter_powered (self));
+               break;
+       case PROP_DEFAULT_ADAPTER_NAME:
+               g_value_take_string (value, _shell_bluetooth_client_get_default_adapter_name (self));
+               break;
+       case PROP_DEFAULT_ADAPTER_DISCOVERABLE:
+               g_value_set_boolean (value, priv->disco_during_disco);
+               break;
+       case PROP_DEFAULT_ADAPTER_DISCOVERING:
+               g_value_set_boolean (value, _shell_bluetooth_client_get_default_adapter_discovering (self));
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+               break;
+       }
+}
+
+static void
+shell_bluetooth_client_set_property (GObject        *object,
+                              guint           property_id,
+                              const GValue   *value,
+                              GParamSpec     *pspec)
+{
+       ShellBluetoothClient *self = SHELL_BLUETOOTH_CLIENT (object);
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE (self);
+
+       switch (property_id) {
+       case PROP_DEFAULT_ADAPTER_DISCOVERABLE:
+               priv->disco_during_disco = g_value_get_boolean (value);
+               _shell_bluetooth_client_set_default_adapter_discovering (self, priv->discovery_started, 
priv->disco_during_disco);
+               break;
+       case PROP_DEFAULT_ADAPTER_DISCOVERING:
+               _shell_bluetooth_client_set_default_adapter_discovering (self, g_value_get_boolean (value), 
priv->disco_during_disco);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+               break;
+       }
+}
+
+static void shell_bluetooth_client_finalize(GObject *object)
+{
+       ShellBluetoothClient *client = SHELL_BLUETOOTH_CLIENT (object);
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE (client);
+
+       if (priv->cancellable != NULL) {
+               g_cancellable_cancel (priv->cancellable);
+               g_clear_object (&priv->cancellable);
+       }
+       g_clear_object (&priv->manager);
+       g_object_unref (priv->store);
+
+       g_clear_pointer (&priv->default_adapter, gtk_tree_row_reference_free);
+
+       G_OBJECT_CLASS(shell_bluetooth_client_parent_class)->finalize (object);
+}
+
+static void shell_bluetooth_client_class_init(ShellBluetoothClientClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->finalize = shell_bluetooth_client_finalize;
+       object_class->get_property = shell_bluetooth_client_get_property;
+       object_class->set_property = shell_bluetooth_client_set_property;
+
+       /**
+        * ShellBluetoothClient::device-removed:
+        * @client: a #ShellBluetoothClient object which received the signal
+        * @device: the D-Bus object path for the now-removed device
+        *
+        * The #ShellBluetoothClient::device-removed signal is launched when a
+        * device gets removed from the model.
+        **/
+       signals[DEVICE_REMOVED] =
+               g_signal_new ("device-removed",
+                             G_TYPE_FROM_CLASS (klass),
+                             G_SIGNAL_RUN_LAST,
+                             0,
+                             NULL, NULL,
+                             g_cclosure_marshal_VOID__STRING,
+                             G_TYPE_NONE, 1, G_TYPE_STRING);
+
+       /**
+        * ShellBluetoothClient:default-adapter:
+        *
+        * The D-Bus path of the default ShellBluetooth adapter or %NULL.
+        */
+       g_object_class_install_property (object_class, PROP_DEFAULT_ADAPTER,
+                                        g_param_spec_string ("default-adapter", NULL,
+                                                             "The D-Bus path of the default adapter",
+                                                             NULL, G_PARAM_READABLE));
+       /**
+        * ShellBluetoothClient:default-adapter-powered:
+        *
+        * %TRUE if the default ShellBluetooth adapter is powered.
+        */
+       g_object_class_install_property (object_class, PROP_DEFAULT_ADAPTER_POWERED,
+                                        g_param_spec_boolean ("default-adapter-powered", NULL,
+                                                             "Whether the default adapter is powered",
+                                                              FALSE, G_PARAM_READABLE));
+       /**
+        * ShellBluetoothClient:default-adapter-discoverable:
+        *
+        * %TRUE if the default ShellBluetooth adapter is discoverable during discovery.
+        */
+       g_object_class_install_property (object_class, PROP_DEFAULT_ADAPTER_DISCOVERABLE,
+                                        g_param_spec_boolean ("default-adapter-discoverable", NULL,
+                                                             "Whether the default adapter is visible by 
other devices during discovery",
+                                                              FALSE, G_PARAM_READWRITE));
+       /**
+        * ShellBluetoothClient:default-adapter-name:
+        *
+        * The name of the default ShellBluetooth adapter or %NULL.
+        */
+       g_object_class_install_property (object_class, PROP_DEFAULT_ADAPTER_NAME,
+                                        g_param_spec_string ("default-adapter-name", NULL,
+                                                             "The human readable name of the default 
adapter",
+                                                             NULL, G_PARAM_READABLE));
+       /**
+        * ShellBluetoothClient:default-adapter-discovering:
+        *
+        * %TRUE if the default ShellBluetooth adapter is discovering.
+        */
+       g_object_class_install_property (object_class, PROP_DEFAULT_ADAPTER_DISCOVERING,
+                                        g_param_spec_boolean ("default-adapter-discovering", NULL,
+                                                             "Whether the default adapter is searching for 
devices",
+                                                              FALSE, G_PARAM_READWRITE));
+}
+
+/**
+ * shell_bluetooth_client_new:
+ *
+ * Returns a reference to the #ShellBluetoothClient singleton. Use g_object_unref() when done with the 
object.
+ *
+ * Return value: (transfer full): a #ShellBluetoothClient object.
+ **/
+ShellBluetoothClient *shell_bluetooth_client_new(void)
+{
+       static ShellBluetoothClient *shell_bluetooth_client = NULL;
+
+       if (shell_bluetooth_client != NULL)
+               return g_object_ref(shell_bluetooth_client);
+
+       shell_bluetooth_client = SHELL_BLUETOOTH_CLIENT (g_object_new (SHELL_BLUETOOTH_TYPE_CLIENT, NULL));
+       g_object_add_weak_pointer (G_OBJECT (shell_bluetooth_client),
+                                  (gpointer) &shell_bluetooth_client);
+
+       return shell_bluetooth_client;
+}
+
+/**
+ * shell_bluetooth_client_get_model:
+ * @client: a #ShellBluetoothClient object
+ *
+ * Returns an unfiltered #GtkTreeModel representing the adapter and devices available on the system.
+ *
+ * Return value: (transfer full): a #GtkTreeModel object.
+ **/
+GtkTreeModel *shell_bluetooth_client_get_model (ShellBluetoothClient *client)
+{
+       ShellBluetoothClientPrivate *priv;
+       GtkTreeModel *model;
+
+       g_return_val_if_fail (SHELL_BLUETOOTH_IS_CLIENT (client), NULL);
+
+       priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       model = GTK_TREE_MODEL (g_object_ref(priv->store));
+
+       return model;
+}
+
+/**
+ * shell_bluetooth_client_get_filter_model:
+ * @client: a #ShellBluetoothClient object
+ * @func: a #GtkTreeModelFilterVisibleFunc
+ * @data: user data to pass to gtk_tree_model_filter_set_visible_func()
+ * @destroy: a destroy function for gtk_tree_model_filter_set_visible_func()
+ *
+ * Returns a #GtkTreeModelFilter of devices filtered using the @func, @data and @destroy arguments to pass 
to gtk_tree_model_filter_set_visible_func().
+ *
+ * Return value: (transfer full): a #GtkTreeModel object.
+ **/
+GtkTreeModel *shell_bluetooth_client_get_filter_model (ShellBluetoothClient *client,
+                                                GtkTreeModelFilterVisibleFunc func,
+                                                gpointer data, GDestroyNotify destroy)
+{
+       ShellBluetoothClientPrivate *priv;
+       GtkTreeModel *model;
+
+       g_return_val_if_fail (SHELL_BLUETOOTH_IS_CLIENT (client), NULL);
+
+       priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       model = gtk_tree_model_filter_new(GTK_TREE_MODEL(priv->store), NULL);
+
+       gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(model),
+                                                       func, data, destroy);
+
+       return model;
+}
+
+static gboolean adapter_filter(GtkTreeModel *model,
+                                       GtkTreeIter *iter, gpointer user_data)
+{
+       GDBusProxy *proxy;
+       gboolean active;
+
+       gtk_tree_model_get(model, iter, SHELL_BLUETOOTH_COLUMN_PROXY, &proxy, -1);
+
+       if (proxy == NULL)
+               return FALSE;
+
+       active = g_str_equal(BLUEZ_ADAPTER_INTERFACE,
+                                       g_dbus_proxy_get_interface_name(proxy));
+
+       g_object_unref(proxy);
+
+       return active;
+}
+
+/**
+ * shell_bluetooth_client_get_adapter_model:
+ * @client: a #ShellBluetoothClient object
+ *
+ * Returns a #GtkTreeModelFilter with only adapters present.
+ *
+ * Return value: (transfer full): a #GtkTreeModel object.
+ **/
+GtkTreeModel *shell_bluetooth_client_get_adapter_model (ShellBluetoothClient *client)
+{
+       return shell_bluetooth_client_get_filter_model (client, adapter_filter,
+                                                 NULL, NULL);
+}
+
+/**
+ * shell_bluetooth_client_get_device_model:
+ * @client: a #ShellBluetoothClient object
+ *
+ * Returns a #GtkTreeModelFilter with only devices belonging to the default adapter listed.
+ * Note that the model will follow a specific adapter, and will not follow the default adapter.
+ * Also note that due to the way #GtkTreeModelFilter works, you will probably want to
+ * monitor signals on the "child-model" #GtkTreeModel to monitor for changes.
+ *
+ * Return value: (transfer full): a #GtkTreeModel object.
+ **/
+GtkTreeModel *shell_bluetooth_client_get_device_model (ShellBluetoothClient *client)
+{
+       ShellBluetoothClientPrivate *priv;
+       GtkTreeModel *model;
+       GtkTreePath *path;
+       GtkTreeIter iter;
+       gboolean cont, found = FALSE;
+
+       g_return_val_if_fail (SHELL_BLUETOOTH_IS_CLIENT (client), NULL);
+
+       priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       cont = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(priv->store), &iter);
+
+       while (cont == TRUE) {
+               gboolean is_default;
+
+               gtk_tree_model_get (GTK_TREE_MODEL(priv->store), &iter,
+                                   SHELL_BLUETOOTH_COLUMN_DEFAULT, &is_default, -1);
+
+               if (is_default == TRUE) {
+                       found = TRUE;
+                       break;
+               }
+
+               cont = gtk_tree_model_iter_next (GTK_TREE_MODEL(priv->store), &iter);
+       }
+
+       if (found == TRUE) {
+               path = gtk_tree_model_get_path (GTK_TREE_MODEL(priv->store), &iter);
+               model = gtk_tree_model_filter_new (GTK_TREE_MODEL(priv->store), path);
+               gtk_tree_path_free (path);
+       } else
+               model = NULL;
+
+       return model;
+}
+
+typedef struct {
+       ShellBluetoothClientSetupFunc func;
+       ShellBluetoothClient *client;
+} CreateDeviceData;
+
+static void
+device_pair_callback (GDBusProxy   *proxy,
+                     GAsyncResult *res,
+                     GTask        *task)
+{
+       GError *error = NULL;
+
+       if (device1_call_pair_finish (DEVICE1(proxy), res, &error) == FALSE) {
+               g_debug ("Pair() failed for %s: %s",
+                        g_dbus_proxy_get_object_path (proxy),
+                        error->message);
+               g_task_return_error (task, error);
+       } else {
+               g_task_return_boolean (task, TRUE);
+       }
+       g_object_unref (task);
+}
+
+/**
+ * shell_bluetooth_client_setup_device_finish:
+ * @client:
+ * @res:
+ * @path: (out):
+ * @error:
+ */
+gboolean
+shell_bluetooth_client_setup_device_finish (ShellBluetoothClient  *client,
+                                     GAsyncResult     *res,
+                                     char            **path,
+                                     GError          **error)
+{
+       GTask *task;
+       char *object_path;
+       gboolean ret;
+
+       g_return_val_if_fail (path != NULL, FALSE);
+
+       task = G_TASK (res);
+
+       g_warn_if_fail (g_task_get_source_tag (task) == shell_bluetooth_client_setup_device);
+
+       ret = g_task_propagate_boolean (task, error);
+       object_path = g_strdup (g_task_get_task_data (task));
+       *path = object_path;
+       g_debug ("shell_bluetooth_client_setup_device_finish() %s (path: %s)",
+                ret ? "success" : "failure", object_path);
+       return ret;
+}
+
+void
+shell_bluetooth_client_setup_device (ShellBluetoothClient          *client,
+                              const char               *path,
+                              gboolean                  pair,
+                              GCancellable             *cancellable,
+                              GAsyncReadyCallback       callback,
+                              gpointer                  user_data)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       GTask *task;
+       g_autoptr(GDBusProxy) device = NULL;
+       GtkTreeIter iter, adapter_iter;
+       gboolean paired;
+
+       g_return_if_fail (SHELL_BLUETOOTH_IS_CLIENT (client));
+       g_return_if_fail (path != NULL);
+
+       task = g_task_new (G_OBJECT (client),
+                          cancellable,
+                          callback,
+                          user_data);
+       g_task_set_source_tag (task, shell_bluetooth_client_setup_device);
+       g_task_set_task_data (task, g_strdup (path), (GDestroyNotify) g_free);
+
+       if (get_iter_from_path (priv->store, &iter, path) == FALSE) {
+               g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                                        "Device with object path %s does not exist",
+                                        path);
+               g_object_unref (task);
+               return;
+       }
+
+       gtk_tree_model_get (GTK_TREE_MODEL(priv->store), &iter,
+                           SHELL_BLUETOOTH_COLUMN_PROXY, &device,
+                           SHELL_BLUETOOTH_COLUMN_PAIRED, &paired, -1);
+
+       if (paired != FALSE &&
+           gtk_tree_model_iter_parent (GTK_TREE_MODEL(priv->store), &adapter_iter, &iter)) {
+               GDBusProxy *adapter;
+               g_autoptr(GError) err = NULL;
+
+               gtk_tree_model_get (GTK_TREE_MODEL(priv->store), &adapter_iter,
+                                   SHELL_BLUETOOTH_COLUMN_PROXY, &adapter,
+                                   -1);
+               adapter1_call_remove_device_sync (ADAPTER1 (adapter),
+                                                 path,
+                                                 NULL, &err);
+               if (err != NULL)
+                       g_warning ("Failed to remove device: %s", err->message);
+               g_object_unref (adapter);
+       }
+
+       if (pair == TRUE) {
+               device1_call_pair (DEVICE1(device),
+                                  cancellable,
+                                  (GAsyncReadyCallback) device_pair_callback,
+                                  task);
+       } else {
+               g_task_return_boolean (task, TRUE);
+               g_object_unref (task);
+       }
+}
+
+/**
+ * shell_bluetooth_client_cancel_setup_device_finish:
+ * @client:
+ * @res:
+ * @path: (out):
+ * @error:
+ */
+gboolean
+shell_bluetooth_client_cancel_setup_device_finish (ShellBluetoothClient  *client,
+                                            GAsyncResult     *res,
+                                            char            **path,
+                                            GError          **error)
+{
+       GTask *task;
+       char *object_path;
+       gboolean ret;
+
+       g_return_val_if_fail (path != NULL, FALSE);
+
+       task = G_TASK (res);
+
+       g_warn_if_fail (g_task_get_source_tag (task) == shell_bluetooth_client_cancel_setup_device);
+
+       ret = g_task_propagate_boolean (task, error);
+       object_path = g_strdup (g_task_get_task_data (task));
+       *path = object_path;
+       g_debug ("shell_bluetooth_client_cancel_setup_device_finish() %s (path: %s)",
+                ret ? "success" : "failure", object_path);
+       return ret;
+}
+
+static void
+device_cancel_pairing_callback (GDBusProxy   *proxy,
+                               GAsyncResult *res,
+                               GTask        *task)
+{
+       GError *error = NULL;
+
+       if (device1_call_cancel_pairing_finish (DEVICE1(proxy), res, &error) == FALSE) {
+               g_debug ("CancelPairing() failed for %s: %s",
+                        g_dbus_proxy_get_object_path (proxy),
+                        error->message);
+               g_task_return_error (task, error);
+       } else {
+               g_task_return_boolean (task, TRUE);
+       }
+       g_object_unref (task);
+}
+
+void
+shell_bluetooth_client_cancel_setup_device (ShellBluetoothClient          *client,
+                                     const char               *path,
+                                     GCancellable             *cancellable,
+                                     GAsyncReadyCallback       callback,
+                                     gpointer                  user_data)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       GTask *task;
+       g_autoptr(GDBusProxy) device = NULL;
+       GtkTreeIter iter;
+
+       g_return_if_fail (SHELL_BLUETOOTH_IS_CLIENT (client));
+       g_return_if_fail (path != NULL);
+
+       task = g_task_new (G_OBJECT (client),
+                          cancellable,
+                          callback,
+                          user_data);
+       g_task_set_source_tag (task, shell_bluetooth_client_cancel_setup_device);
+       g_task_set_task_data (task, g_strdup (path), (GDestroyNotify) g_free);
+
+       if (get_iter_from_path (priv->store, &iter, path) == FALSE) {
+               g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                                        "Device with object path %s does not exist",
+                                        path);
+               g_object_unref (task);
+               return;
+       }
+
+       gtk_tree_model_get (GTK_TREE_MODEL(priv->store), &iter,
+                           SHELL_BLUETOOTH_COLUMN_PROXY, &device,
+                           -1);
+
+       device1_call_cancel_pairing (DEVICE1(device),
+                                    cancellable,
+                                    (GAsyncReadyCallback) device_cancel_pairing_callback,
+                                    task);
+}
+
+gboolean
+shell_bluetooth_client_set_trusted (ShellBluetoothClient *client,
+                             const char      *device_path,
+                             gboolean         trusted)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       GObject *device;
+       GtkTreeIter iter;
+
+       g_return_val_if_fail (SHELL_BLUETOOTH_IS_CLIENT (client), FALSE);
+       g_return_val_if_fail (device_path != NULL, FALSE);
+
+       if (get_iter_from_path (priv->store, &iter, device_path) == FALSE) {
+               g_debug ("Couldn't find device '%s' in tree to mark it as trusted", device_path);
+               return FALSE;
+       }
+
+       gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
+                           SHELL_BLUETOOTH_COLUMN_PROXY, &device, -1);
+
+       if (device == NULL)
+               return FALSE;
+
+       g_object_set (device, "trusted", trusted, NULL);
+       g_object_unref (device);
+
+       return TRUE;
+}
+
+GDBusProxy *
+shell_bluetooth_client_get_device (ShellBluetoothClient *client,
+                            const char       *path)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       GtkTreeIter iter;
+       GDBusProxy *proxy;
+
+       if (get_iter_from_path (priv->store, &iter, path) == FALSE) {
+               return NULL;
+       }
+
+       gtk_tree_model_get (GTK_TREE_MODEL(priv->store), &iter,
+                                       SHELL_BLUETOOTH_COLUMN_PROXY, &proxy,
+                                       -1);
+       return proxy;
+}
+
+static void
+connect_callback (GDBusProxy   *proxy,
+                 GAsyncResult *res,
+                 GTask        *task)
+{
+       gboolean retval;
+       GError *error = NULL;
+
+       retval = device1_call_connect_finish (DEVICE1 (proxy), res, &error);
+       if (retval == FALSE) {
+               g_debug ("Connect failed for %s: %s",
+                        g_dbus_proxy_get_object_path (proxy), error->message);
+               g_task_return_error (task, error);
+       } else {
+               g_debug ("Connect succeeded for %s",
+                        g_dbus_proxy_get_object_path (proxy));
+               g_task_return_boolean (task, retval);
+       }
+
+       g_object_unref (task);
+}
+
+static void
+disconnect_callback (GDBusProxy   *proxy,
+                    GAsyncResult *res,
+                    GTask        *task)
+{
+       gboolean retval;
+       GError *error = NULL;
+
+       retval = device1_call_disconnect_finish (DEVICE1 (proxy), res, &error);
+       if (retval == FALSE) {
+               g_debug ("Disconnect failed for %s: %s",
+                        g_dbus_proxy_get_object_path (proxy),
+                        error->message);
+               g_task_return_error (task, error);
+       } else {
+               g_debug ("Disconnect succeeded for %s",
+                        g_dbus_proxy_get_object_path (proxy));
+               g_task_return_boolean (task, retval);
+       }
+
+       g_object_unref (task);
+}
+
+/**
+ * shell_bluetooth_client_connect_service:
+ * @client: a #ShellBluetoothClient
+ * @path: the object path on which to operate
+ * @connect: Whether try to connect or disconnect from services on a device
+ * @cancellable: optional #GCancellable object, %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the connection is complete
+ * @user_data: the data to pass to callback function
+ *
+ * When the connection operation is finished, @callback will be called. You can
+ * then call shell_bluetooth_client_connect_service_finish() to get the result of the
+ * operation.
+ **/
+void
+shell_bluetooth_client_connect_service (ShellBluetoothClient     *client,
+                                 const char          *path,
+                                 gboolean             connect,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+       ShellBluetoothClientPrivate *priv = SHELL_BLUETOOTH_CLIENT_GET_PRIVATE(client);
+       GtkTreeIter iter;
+       GTask *task;
+       g_autoptr(GDBusProxy) device = NULL;
+
+       g_return_if_fail (SHELL_BLUETOOTH_IS_CLIENT (client));
+       g_return_if_fail (path != NULL);
+
+       task = g_task_new (G_OBJECT (client),
+                          cancellable,
+                          callback,
+                          user_data);
+       g_task_set_source_tag (task, shell_bluetooth_client_connect_service);
+
+       if (get_iter_from_path (priv->store, &iter, path) == FALSE) {
+               g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                                        "Device with object path %s does not exist",
+                                        path);
+               g_object_unref (task);
+               return;
+       }
+
+       gtk_tree_model_get (GTK_TREE_MODEL(priv->store), &iter,
+                           SHELL_BLUETOOTH_COLUMN_PROXY, &device,
+                           -1);
+
+       if (connect) {
+               device1_call_connect (DEVICE1(device),
+                                     cancellable,
+                                     (GAsyncReadyCallback) connect_callback,
+                                     task);
+       } else {
+               device1_call_disconnect (DEVICE1(device),
+                                        cancellable,
+                                        (GAsyncReadyCallback) disconnect_callback,
+                                        task);
+       }
+}
+
+/**
+ * shell_bluetooth_client_connect_service_finish:
+ * @client: a #ShellBluetoothClient
+ * @res: a #GAsyncResult
+ * @error: a #GError
+ *
+ * Finishes the connection operation. See shell_bluetooth_client_connect_service().
+ *
+ * Returns: %TRUE if the connection operation succeeded, %FALSE otherwise.
+ **/
+gboolean
+shell_bluetooth_client_connect_service_finish (ShellBluetoothClient *client,
+                                        GAsyncResult    *res,
+                                        GError         **error)
+{
+       GTask *task;
+
+       task = G_TASK (res);
+
+       g_warn_if_fail (g_task_get_source_tag (task) == shell_bluetooth_client_connect_service);
+
+       return g_task_propagate_boolean (task, error);
+}
+
+#define BOOL_STR(x) (x ? "True" : "False")
+
+void
+shell_bluetooth_client_dump_device (GtkTreeModel *model,
+                             GtkTreeIter *iter)
+{
+       GDBusProxy *proxy;
+       char *address, *alias, *icon, **uuids;
+       gboolean is_default, paired, trusted, connected, discoverable, discovering, powered, is_adapter;
+       GtkTreeIter parent;
+       ShellBluetoothType type;
+
+       gtk_tree_model_get (model, iter,
+                           SHELL_BLUETOOTH_COLUMN_ADDRESS, &address,
+                           SHELL_BLUETOOTH_COLUMN_ALIAS, &alias,
+                           SHELL_BLUETOOTH_COLUMN_TYPE, &type,
+                           SHELL_BLUETOOTH_COLUMN_ICON, &icon,
+                           SHELL_BLUETOOTH_COLUMN_DEFAULT, &is_default,
+                           SHELL_BLUETOOTH_COLUMN_PAIRED, &paired,
+                           SHELL_BLUETOOTH_COLUMN_TRUSTED, &trusted,
+                           SHELL_BLUETOOTH_COLUMN_CONNECTED, &connected,
+                           SHELL_BLUETOOTH_COLUMN_DISCOVERABLE, &discoverable,
+                           SHELL_BLUETOOTH_COLUMN_DISCOVERING, &discovering,
+                           SHELL_BLUETOOTH_COLUMN_POWERED, &powered,
+                           SHELL_BLUETOOTH_COLUMN_UUIDS, &uuids,
+                           SHELL_BLUETOOTH_COLUMN_PROXY, &proxy,
+                           -1);
+       if (proxy) {
+               char *basename;
+               basename = g_path_get_basename(g_dbus_proxy_get_object_path(proxy));
+               is_adapter = !g_str_has_prefix (basename, "dev_");
+               g_free (basename);
+       } else {
+               is_adapter = !gtk_tree_model_iter_parent (model, &parent, iter);
+       }
+
+       if (is_adapter != FALSE) {
+               /* Adapter */
+               g_print ("Adapter: %s (%s)\n", alias, address);
+               if (is_default)
+                       g_print ("\tDefault adapter\n");
+               g_print ("\tD-Bus Path: %s\n", proxy ? g_dbus_proxy_get_object_path (proxy) : "(none)");
+               g_print ("\tDiscoverable: %s\n", BOOL_STR (discoverable));
+               if (discovering)
+                       g_print ("\tDiscovery in progress\n");
+               g_print ("\t%s\n", powered ? "Is powered" : "Is not powered");
+       } else {
+               /* Device */
+               g_print ("Device: %s (%s)\n", alias, address);
+               g_print ("\tD-Bus Path: %s\n", proxy ? g_dbus_proxy_get_object_path (proxy) : "(none)");
+               g_print ("\tType: %s Icon: %s\n", shell_bluetooth_type_to_string (type), icon);
+               g_print ("\tPaired: %s Trusted: %s Connected: %s\n", BOOL_STR(paired), BOOL_STR(trusted), 
BOOL_STR(connected));
+               if (uuids != NULL) {
+                       guint i;
+                       g_print ("\tUUIDs: ");
+                       for (i = 0; uuids[i] != NULL; i++)
+                               g_print ("%s ", uuids[i]);
+                       g_print ("\n");
+               }
+       }
+       g_print ("\n");
+
+       g_free (alias);
+       g_free (address);
+       g_free (icon);
+       g_clear_object (&proxy);
+       g_strfreev (uuids);
+}
+
diff --git a/src/shell-bluetooth-client.h b/src/shell-bluetooth-client.h
new file mode 100644
index 0000000000..c9bf012143
--- /dev/null
+++ b/src/shell-bluetooth-client.h
@@ -0,0 +1,80 @@
+/*
+ *
+ *  BlueZ - ShellBluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  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.1 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#pragma once
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <shell-bluetooth-enums.h>
+
+#define SHELL_BLUETOOTH_TYPE_CLIENT (shell_bluetooth_client_get_type())
+#define SHELL_BLUETOOTH_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+                                       SHELL_BLUETOOTH_TYPE_CLIENT, ShellBluetoothClient))
+#define SHELL_BLUETOOTH_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
+                                       SHELL_BLUETOOTH_TYPE_CLIENT, ShellBluetoothClientClass))
+#define SHELL_BLUETOOTH_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+                                                       SHELL_BLUETOOTH_TYPE_CLIENT))
+#define SHELL_BLUETOOTH_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), \
+                                                       SHELL_BLUETOOTH_TYPE_CLIENT))
+#define SHELL_BLUETOOTH_GET_CLIENT_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
+                                       SHELL_BLUETOOTH_TYPE_CLIENT, ShellBluetoothClientClass))
+
+/**
+ * ShellBluetoothClient:
+ *
+ * The <structname>ShellBluetoothClient</structname> struct contains
+ * only private fields and should not be directly accessed.
+ */
+typedef struct _ShellBluetoothClient ShellBluetoothClient;
+typedef struct _ShellBluetoothClientClass ShellBluetoothClientClass;
+
+struct _ShellBluetoothClient {
+       GObject parent;
+};
+
+struct _ShellBluetoothClientClass {
+       GObjectClass parent_class;
+};
+
+GType shell_bluetooth_client_get_type(void);
+
+ShellBluetoothClient *shell_bluetooth_client_new(void);
+
+GtkTreeModel *shell_bluetooth_client_get_model(ShellBluetoothClient *client);
+
+GtkTreeModel *shell_bluetooth_client_get_filter_model(ShellBluetoothClient *client,
+                               GtkTreeModelFilterVisibleFunc func,
+                               gpointer data, GDestroyNotify destroy);
+GtkTreeModel *shell_bluetooth_client_get_adapter_model(ShellBluetoothClient *client);
+GtkTreeModel *shell_bluetooth_client_get_device_model(ShellBluetoothClient *client);
+
+void shell_bluetooth_client_connect_service (ShellBluetoothClient     *client,
+                                      const char          *path,
+                                      gboolean             connect,
+                                      GCancellable        *cancellable,
+                                      GAsyncReadyCallback  callback,
+                                      gpointer             user_data);
+
+gboolean shell_bluetooth_client_connect_service_finish (ShellBluetoothClient *client,
+                                                 GAsyncResult    *res,
+                                                 GError         **error);
diff --git a/src/shell-bluetooth-client.xml b/src/shell-bluetooth-client.xml
new file mode 100644
index 0000000000..1324821402
--- /dev/null
+++ b/src/shell-bluetooth-client.xml
@@ -0,0 +1,60 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd";>
+
+<node>
+  <interface name="org.bluez.Adapter1">
+    <method name="StartDiscovery"></method>
+    <method name="StopDiscovery"></method>
+    <method name="RemoveDevice"><arg name="device" type="o" direction="in"/></method>
+    <method name="SetDiscoveryFilter"><arg name="properties" type="a{sv}" direction="in"/></method>
+    <property name="Address" type="s" access="read"></property>
+    <property name="Name" type="s" access="read"></property>
+    <property name="Alias" type="s" access="readwrite"></property>
+    <property name="Class" type="u" access="read"></property>
+    <property name="Powered" type="b" access="readwrite"></property>
+    <property name="Discoverable" type="b" access="readwrite"></property>
+    <property name="DiscoverableTimeout" type="u" access="readwrite"></property>
+    <property name="Pairable" type="b" access="readwrite"></property>
+    <property name="PairableTimeout" type="u" access="readwrite"></property>
+    <property name="Discovering" type="b" access="read"></property>
+    <property name="UUIDs" type="as" access="read"></property>
+    <property name="Modalias" type="s" access="read"></property>
+  </interface>
+
+  <interface name="org.bluez.Device1">
+    <method name="Disconnect"></method>
+    <method name="Connect"></method>
+    <method name="ConnectProfile"><arg name="UUID" type="s" direction="in"/> </method>
+    <method name="DisconnectProfile"><arg name="UUID" type="s" direction="in"/> </method>
+    <method name="Pair"></method>
+    <method name="CancelPairing"></method>
+    <property name="Address" type="s" access="read"></property>
+    <property name="Name" type="s" access="read"></property>
+    <property name="Alias" type="s" access="readwrite"></property>
+    <property name="Class" type="u" access="read"></property>
+    <property name="Appearance" type="q" access="read"></property>
+    <property name="Icon" type="s" access="read"></property>
+    <property name="Paired" type="b" access="read"></property>
+    <property name="Trusted" type="b" access="readwrite"></property>
+    <property name="Blocked" type="b" access="readwrite"></property>
+    <property name="LegacyPairing" type="b" access="read"></property>
+    <property name="RSSI" type="n" access="read"></property>
+    <property name="Connected" type="b" access="read"></property>
+    <property name="UUIDs" type="as" access="read"></property>
+    <property name="Modalias" type="s" access="read"></property>
+    <property name="Adapter" type="o" access="read"></property>
+  </interface>
+
+  <interface name="org.bluez.AgentManager1">
+    <method name="RegisterAgent">
+      <arg name="agent" type="o" direction="in"/>
+      <arg name="capability" type="s" direction="in"/>
+    </method>
+    <method name="UnregisterAgent">
+      <arg name="agent" type="o" direction="in"/>
+    </method>
+    <method name="RequestDefaultAgent">
+      <arg name="agent" type="o" direction="in"/>
+    </method>
+  </interface>
+</node>
diff --git a/src/shell-bluetooth-enums.h b/src/shell-bluetooth-enums.h
new file mode 100644
index 0000000000..1ab8f7c15b
--- /dev/null
+++ b/src/shell-bluetooth-enums.h
@@ -0,0 +1,182 @@
+/*
+ *
+ *  BlueZ - ShellBluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  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.1 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#pragma once
+
+#include <glib.h>
+
+/**
+ * SECTION:shell-bluetooth-enums
+ * @short_description: ShellBluetooth related enumerations
+ * @stability: Stable
+ * @include: shell-bluetooth-enums.h
+ *
+ * Enumerations related to ShellBluetooth.
+ **/
+
+/**
+ * ShellBluetoothCategory:
+ * @SHELL_BLUETOOTH_CATEGORY_ALL: all devices
+ * @SHELL_BLUETOOTH_CATEGORY_PAIRED: paired devices
+ * @SHELL_BLUETOOTH_CATEGORY_TRUSTED: trusted devices
+ * @SHELL_BLUETOOTH_CATEGORY_NOT_PAIRED_OR_TRUSTED: neither paired, nor trusted devices
+ * @SHELL_BLUETOOTH_CATEGORY_PAIRED_OR_TRUSTED: paired and/or trusted devices
+ *
+ * The category of a ShellBluetooth devices.
+ **/
+typedef enum {
+       SHELL_BLUETOOTH_CATEGORY_ALL,
+       SHELL_BLUETOOTH_CATEGORY_PAIRED,
+       SHELL_BLUETOOTH_CATEGORY_TRUSTED,
+       SHELL_BLUETOOTH_CATEGORY_NOT_PAIRED_OR_TRUSTED,
+       SHELL_BLUETOOTH_CATEGORY_PAIRED_OR_TRUSTED,
+       /* < private > */
+       SHELL_BLUETOOTH_CATEGORY_NUM_CATEGORIES /*< skip >*/
+} ShellBluetoothCategory;
+
+/**
+ * ShellBluetoothType:
+ * @SHELL_BLUETOOTH_TYPE_ANY: any device, or a device of an unknown type
+ * @SHELL_BLUETOOTH_TYPE_PHONE: a telephone (usually a cell/mobile phone)
+ * @SHELL_BLUETOOTH_TYPE_MODEM: a modem
+ * @SHELL_BLUETOOTH_TYPE_COMPUTER: a computer, can be a laptop, a wearable computer, etc.
+ * @SHELL_BLUETOOTH_TYPE_NETWORK: a network device, such as a router
+ * @SHELL_BLUETOOTH_TYPE_HEADSET: a headset (usually a hands-free device)
+ * @SHELL_BLUETOOTH_TYPE_HEADPHONES: headphones (covers two ears)
+ * @SHELL_BLUETOOTH_TYPE_OTHER_AUDIO: another type of audio device
+ * @SHELL_BLUETOOTH_TYPE_KEYBOARD: a keyboard
+ * @SHELL_BLUETOOTH_TYPE_MOUSE: a mouse
+ * @SHELL_BLUETOOTH_TYPE_CAMERA: a camera (still or moving)
+ * @SHELL_BLUETOOTH_TYPE_PRINTER: a printer
+ * @SHELL_BLUETOOTH_TYPE_JOYPAD: a joypad, joystick, or other game controller
+ * @SHELL_BLUETOOTH_TYPE_TABLET: a drawing tablet
+ * @SHELL_BLUETOOTH_TYPE_VIDEO: a video device, such as a webcam
+ * @SHELL_BLUETOOTH_TYPE_REMOTE_CONTROL: a remote control
+ * @SHELL_BLUETOOTH_TYPE_SCANNER: a scanner
+ * @SHELL_BLUETOOTH_TYPE_DISPLAY: a display
+ * @SHELL_BLUETOOTH_TYPE_WEARABLE: a wearable computer
+ * @SHELL_BLUETOOTH_TYPE_TOY: a toy or game
+ * @SHELL_BLUETOOTH_TYPE_SPEAKERS: audio speaker or speakers
+ *
+ * The type of a ShellBluetooth device. See also %SHELL_BLUETOOTH_TYPE_INPUT and %SHELL_BLUETOOTH_TYPE_AUDIO
+ **/
+typedef enum {
+       SHELL_BLUETOOTH_TYPE_ANY                = 1 << 0,
+       SHELL_BLUETOOTH_TYPE_PHONE              = 1 << 1,
+       SHELL_BLUETOOTH_TYPE_MODEM              = 1 << 2,
+       SHELL_BLUETOOTH_TYPE_COMPUTER           = 1 << 3,
+       SHELL_BLUETOOTH_TYPE_NETWORK            = 1 << 4,
+       SHELL_BLUETOOTH_TYPE_HEADSET            = 1 << 5,
+       SHELL_BLUETOOTH_TYPE_HEADPHONES = 1 << 6,
+       SHELL_BLUETOOTH_TYPE_OTHER_AUDIO        = 1 << 7,
+       SHELL_BLUETOOTH_TYPE_KEYBOARD           = 1 << 8,
+       SHELL_BLUETOOTH_TYPE_MOUSE              = 1 << 9,
+       SHELL_BLUETOOTH_TYPE_CAMERA             = 1 << 10,
+       SHELL_BLUETOOTH_TYPE_PRINTER            = 1 << 11,
+       SHELL_BLUETOOTH_TYPE_JOYPAD             = 1 << 12,
+       SHELL_BLUETOOTH_TYPE_TABLET             = 1 << 13,
+       SHELL_BLUETOOTH_TYPE_VIDEO              = 1 << 14,
+       SHELL_BLUETOOTH_TYPE_REMOTE_CONTROL     = 1 << 15,
+       SHELL_BLUETOOTH_TYPE_SCANNER            = 1 << 16,
+       SHELL_BLUETOOTH_TYPE_DISPLAY            = 1 << 17,
+       SHELL_BLUETOOTH_TYPE_WEARABLE           = 1 << 18,
+       SHELL_BLUETOOTH_TYPE_TOY                = 1 << 19,
+       SHELL_BLUETOOTH_TYPE_SPEAKERS           = 1 << 20,
+} ShellBluetoothType;
+
+#define _SHELL_BLUETOOTH_TYPE_NUM_TYPES 21
+
+/**
+ * SHELL_BLUETOOTH_TYPE_INPUT:
+ *
+ * Use this value to select any ShellBluetooth input device where a #ShellBluetoothType enum is required.
+ */
+#define SHELL_BLUETOOTH_TYPE_INPUT (SHELL_BLUETOOTH_TYPE_KEYBOARD | SHELL_BLUETOOTH_TYPE_MOUSE | 
SHELL_BLUETOOTH_TYPE_TABLET | SHELL_BLUETOOTH_TYPE_JOYPAD)
+/**
+ * SHELL_BLUETOOTH_TYPE_AUDIO:
+ *
+ * Use this value to select any ShellBluetooth audio device where a #ShellBluetoothType enum is required.
+ */
+#define SHELL_BLUETOOTH_TYPE_AUDIO (SHELL_BLUETOOTH_TYPE_HEADSET | SHELL_BLUETOOTH_TYPE_HEADPHONES | 
SHELL_BLUETOOTH_TYPE_OTHER_AUDIO | SHELL_BLUETOOTH_TYPE_SPEAKERS)
+
+/**
+ * ShellBluetoothColumn:
+ * @SHELL_BLUETOOTH_COLUMN_PROXY: a #GDBusProxy object
+ * @SHELL_BLUETOOTH_COLUMN_PROPERTIES: Used to be #GDBusProxy object for DBus.Properties, now always %NULL
+ * @SHELL_BLUETOOTH_COLUMN_ADDRESS: a string representing a ShellBluetooth address
+ * @SHELL_BLUETOOTH_COLUMN_ALIAS: a string to use for display (the name of the device, or its address if the 
name is not known). Only available for devices.
+ * @SHELL_BLUETOOTH_COLUMN_NAME: a string representing the device or adapter's name
+ * @SHELL_BLUETOOTH_COLUMN_TYPE: the #ShellBluetoothType of the device. Only available for devices.
+ * @SHELL_BLUETOOTH_COLUMN_ICON: a string representing the icon name for the device. Only available for 
devices.
+ * @SHELL_BLUETOOTH_COLUMN_DEFAULT: whether the adapter is the default one. Only available for adapters.
+ * @SHELL_BLUETOOTH_COLUMN_PAIRED: whether the device is paired to its parent adapter. Only available for 
devices.
+ * @SHELL_BLUETOOTH_COLUMN_TRUSTED: whether the device is trusted. Only available for devices.
+ * @SHELL_BLUETOOTH_COLUMN_CONNECTED: whether the device is connected. Only available for devices.
+ * @SHELL_BLUETOOTH_COLUMN_DISCOVERABLE: whether the adapter is discoverable/visible. Only available for 
adapters.
+ * @SHELL_BLUETOOTH_COLUMN_DISCOVERING: whether the adapter is discovering. Only available for adapters.
+ * @SHELL_BLUETOOTH_COLUMN_LEGACYPAIRING: whether the device does not support ShellBluetooth 2.1 Simple 
Secure Pairing. Only available for devices.
+ * @SHELL_BLUETOOTH_COLUMN_POWERED: whether the adapter is powered. Only available for adapters.
+ * @SHELL_BLUETOOTH_COLUMN_SERVICES: an array of service names and #ShellBluetoothStatus connection statuses.
+ * @SHELL_BLUETOOTH_COLUMN_UUIDS: a string array of human-readable UUIDs.
+ *
+ * A column identifier to pass to shell_bluetooth_chooser_get_selected_device_info().
+ **/
+typedef enum {
+       SHELL_BLUETOOTH_COLUMN_PROXY,
+       SHELL_BLUETOOTH_COLUMN_PROPERTIES,
+       SHELL_BLUETOOTH_COLUMN_ADDRESS,
+       SHELL_BLUETOOTH_COLUMN_ALIAS,
+       SHELL_BLUETOOTH_COLUMN_NAME,
+       SHELL_BLUETOOTH_COLUMN_TYPE,
+       SHELL_BLUETOOTH_COLUMN_ICON,
+       SHELL_BLUETOOTH_COLUMN_DEFAULT,
+       SHELL_BLUETOOTH_COLUMN_PAIRED,
+       SHELL_BLUETOOTH_COLUMN_TRUSTED,
+       SHELL_BLUETOOTH_COLUMN_CONNECTED,
+       SHELL_BLUETOOTH_COLUMN_DISCOVERABLE,
+       SHELL_BLUETOOTH_COLUMN_DISCOVERING,
+       SHELL_BLUETOOTH_COLUMN_LEGACYPAIRING,
+       SHELL_BLUETOOTH_COLUMN_POWERED,
+       SHELL_BLUETOOTH_COLUMN_SERVICES,
+       SHELL_BLUETOOTH_COLUMN_UUIDS,
+} ShellBluetoothColumn;
+
+#define _SHELL_BLUETOOTH_NUM_COLUMNS (SHELL_BLUETOOTH_COLUMN_UUIDS + 1)
+
+/**
+ * ShellBluetoothStatus:
+ * @SHELL_BLUETOOTH_STATUS_INVALID: whether the status has been set yet
+ * @SHELL_BLUETOOTH_STATUS_DISCONNECTED: whether the service is disconnected
+ * @SHELL_BLUETOOTH_STATUS_CONNECTED: whether the service is connected
+ * @SHELL_BLUETOOTH_STATUS_CONNECTING: whether the service is connecting
+ * @SHELL_BLUETOOTH_STATUS_PLAYING: whether the service is playing (only used by the audio service)
+ *
+ * The connection status of a service on a particular device. Note that @SHELL_BLUETOOTH_STATUS_CONNECTING 
and @SHELL_BLUETOOTH_STATUS_PLAYING might not be available for all services.
+ **/
+typedef enum {
+       SHELL_BLUETOOTH_STATUS_INVALID = 0,
+       SHELL_BLUETOOTH_STATUS_DISCONNECTED,
+       SHELL_BLUETOOTH_STATUS_CONNECTED,
+       SHELL_BLUETOOTH_STATUS_CONNECTING,
+       SHELL_BLUETOOTH_STATUS_PLAYING
+} ShellBluetoothStatus;
diff --git a/src/shell-bluetooth-pin.c b/src/shell-bluetooth-pin.c
new file mode 100644
index 0000000000..3f134e74b9
--- /dev/null
+++ b/src/shell-bluetooth-pin.c
@@ -0,0 +1,239 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <libudev.h>
+#include <shell-bluetooth-enums.h>
+#include <shell-bluetooth-utils.h>
+
+#include "shell-bluetooth-pin.h"
+
+#define PIN_CODE_DB "pin-code-database.xml"
+#define MAX_DIGITS_PIN_PREFIX "max:"
+
+char *
+oui_to_vendor (const char *oui)
+{
+       struct udev *udev = NULL;
+       struct udev_hwdb *hwdb = NULL;
+       struct udev_list_entry *list, *l;
+       char *modalias = NULL;
+       char *vendor = NULL;
+
+       if (oui == NULL ||
+           strlen (oui) < 8)
+               return NULL;
+
+       udev = udev_new ();
+       if (udev == NULL)
+               goto bail;
+
+       hwdb = udev_hwdb_new (udev);
+       if (hwdb == NULL)
+               goto bail;
+
+       modalias = g_strdup_printf ("OUI:%c%c%c%c%c%c",
+                                   g_ascii_toupper (oui[0]),
+                                   g_ascii_toupper (oui[1]),
+                                   g_ascii_toupper (oui[3]),
+                                   g_ascii_toupper (oui[4]),
+                                   g_ascii_toupper (oui[6]),
+                                   g_ascii_toupper (oui[7]));
+
+       list = udev_hwdb_get_properties_list_entry (hwdb, modalias, 0);
+
+       udev_list_entry_foreach (l, list) {
+               const char *name = udev_list_entry_get_name (l);
+
+               if (g_strcmp0 (name, "ID_OUI_FROM_DATABASE") == 0) {
+                       vendor = g_strdup (udev_list_entry_get_value (l));
+                       break;
+               }
+       }
+
+bail:
+       g_clear_pointer (&modalias, g_free);
+       g_clear_pointer (&hwdb, udev_hwdb_unref);
+       g_clear_pointer (&udev, udev_unref);
+
+       return vendor;
+}
+
+#define TYPE_IS(x, r) {                                \
+       if (g_str_equal(type, x)) return r;     \
+}
+
+static guint string_to_type(const char *type)
+{
+       TYPE_IS ("any", SHELL_BLUETOOTH_TYPE_ANY);
+       TYPE_IS ("mouse", SHELL_BLUETOOTH_TYPE_MOUSE);
+       TYPE_IS ("tablet", SHELL_BLUETOOTH_TYPE_TABLET);
+       TYPE_IS ("keyboard", SHELL_BLUETOOTH_TYPE_KEYBOARD);
+       TYPE_IS ("headset", SHELL_BLUETOOTH_TYPE_HEADSET);
+       TYPE_IS ("headphones", SHELL_BLUETOOTH_TYPE_HEADPHONES);
+       TYPE_IS ("audio", SHELL_BLUETOOTH_TYPE_OTHER_AUDIO);
+       TYPE_IS ("printer", SHELL_BLUETOOTH_TYPE_PRINTER);
+       TYPE_IS ("network", SHELL_BLUETOOTH_TYPE_NETWORK);
+       TYPE_IS ("joypad", SHELL_BLUETOOTH_TYPE_JOYPAD);
+
+       g_warning ("unhandled type '%s'", type);
+       return SHELL_BLUETOOTH_TYPE_ANY;
+}
+
+typedef struct {
+       char *ret_pin;
+       guint max_digits;
+       guint type;
+       const char *address;
+       const char *name;
+       char *vendor;
+       gboolean confirm;
+} PinParseData;
+
+static void
+pin_db_parse_start_tag (GMarkupParseContext *ctx,
+                       const gchar         *element_name,
+                       const gchar        **attr_names,
+                       const gchar        **attr_values,
+                       gpointer             data,
+                       GError             **error)
+{
+       PinParseData *pdata = (PinParseData *) data;
+
+       if (pdata->ret_pin != NULL || pdata->max_digits != 0)
+               return;
+       if (g_str_equal (element_name, "device") == FALSE)
+               return;
+
+       while (*attr_names && *attr_values) {
+               if (g_str_equal (*attr_names, "type")) {
+                       guint type;
+
+                       type = string_to_type (*attr_values);
+                       if (type != SHELL_BLUETOOTH_TYPE_ANY && type != pdata->type)
+                               return;
+               } else if (g_str_equal (*attr_names, "oui")) {
+                       if (g_str_has_prefix (pdata->address, *attr_values) == FALSE)
+                               return;
+               } else if (g_str_equal (*attr_names, "vendor")) {
+                       if (*attr_values == NULL || pdata->vendor == NULL)
+                               return;
+                       if (strstr (pdata->vendor, *attr_values) == NULL)
+                               return;
+               } else if (g_str_equal (*attr_names, "name")) {
+                       if (*attr_values == NULL || pdata->name == NULL)
+                               return;
+                       if (strstr (pdata->name, *attr_values) == NULL)
+                               return;
+                       pdata->confirm = FALSE;
+               } else if (g_str_equal (*attr_names, "pin")) {
+                       if (g_str_has_prefix (*attr_values, MAX_DIGITS_PIN_PREFIX) != FALSE) {
+                               pdata->max_digits = strtoul (*attr_values + strlen (MAX_DIGITS_PIN_PREFIX), 
NULL, 0);
+                               g_assert (pdata->max_digits > 0 && pdata->max_digits < PIN_NUM_DIGITS);
+                       } else {
+                               pdata->ret_pin = g_strdup (*attr_values);
+                       }
+                       return;
+               }
+
+               ++attr_names;
+               ++attr_values;
+       }
+}
+
+char *
+get_pincode_for_device (guint       type,
+                       const char *address,
+                       const char *name,
+                       guint      *max_digits,
+                       gboolean   *confirm)
+{
+       GMarkupParseContext *ctx;
+       GMarkupParser parser = { pin_db_parse_start_tag, NULL, NULL, NULL, NULL };
+       PinParseData *data;
+       char *buf;
+       gsize buf_len;
+       GError *err = NULL;
+       char *tmp_vendor, *ret_pin;
+
+       g_return_val_if_fail (address != NULL, NULL);
+
+       g_debug ("Getting pincode for device '%s' (type: %s address: %s)",
+                name ? name : "", shell_bluetooth_type_to_string (type), address);
+
+       /* Load the PIN database and split it in lines */
+       if (!g_file_get_contents(PIN_CODE_DB, &buf, &buf_len, NULL)) {
+               char *filename;
+
+               filename = g_build_filename(GNOME_SHELL_DATADIR, PIN_CODE_DB, NULL);
+               if (!g_file_get_contents(filename, &buf, &buf_len, NULL)) {
+                       g_warning("Could not load "PIN_CODE_DB);
+                       g_free (filename);
+                       return NULL;
+               }
+               g_free (filename);
+       }
+
+       data = g_new0 (PinParseData, 1);
+       data->type = type;
+       data->address = address;
+       data->name = name;
+       data->confirm = TRUE;
+
+       tmp_vendor = oui_to_vendor (address);
+       if (tmp_vendor)
+               data->vendor = g_ascii_strdown (tmp_vendor, -1);
+       g_free (tmp_vendor);
+
+       ctx = g_markup_parse_context_new (&parser, 0, data, NULL);
+
+       if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err)) {
+               g_warning ("Failed to parse '%s': %s\n", PIN_CODE_DB, err->message);
+               g_error_free (err);
+       }
+
+       g_markup_parse_context_free (ctx);
+       g_free (buf);
+
+       if (max_digits != NULL)
+               *max_digits = data->max_digits;
+       if (confirm != NULL)
+               *confirm = data->confirm;
+
+       g_debug ("Got pin '%s' (max digits: %d, confirm: %d) for device '%s' (type: %s address: %s, vendor: 
%s)",
+                data->ret_pin, data->max_digits, data->confirm,
+                name ? name : "", shell_bluetooth_type_to_string (type), address, data->vendor);
+
+       g_free (data->vendor);
+       ret_pin = data->ret_pin;
+       g_free (data);
+
+       return ret_pin;
+}
+
diff --git a/src/shell-bluetooth-pin.h b/src/shell-bluetooth-pin.h
new file mode 100644
index 0000000000..31ddf909f7
--- /dev/null
+++ b/src/shell-bluetooth-pin.h
@@ -0,0 +1,36 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#pragma once
+
+#include <glib.h>
+
+#define PIN_NUM_DIGITS 6
+
+char *oui_to_vendor (const char *oui);
+char *get_pincode_for_device (guint       type,
+                             const char *address,
+                             const char *name,
+                             guint      *max_digits,
+                             gboolean   *confirm);
+
diff --git a/src/shell-bluetooth-utils.c b/src/shell-bluetooth-utils.c
new file mode 100644
index 0000000000..f979dcaacd
--- /dev/null
+++ b/src/shell-bluetooth-utils.c
@@ -0,0 +1,430 @@
+/*
+ *
+ *  BlueZ - ShellBluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2011  Bastien Nocera <hadess hadess net>
+ *  Copyright (C) 2010       Giovanni Campagna <scampa giovanni gmail com>
+ *
+ *
+ *  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.1 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/**
+ * SECTION:bluetooth-utils
+ * @short_description: ShellBluetooth utility functions
+ * @stability: Stable
+ * @include: bluetooth-utils.h
+ *
+ * Those helper functions are used throughout the ShellBluetooth
+ * management utilities.
+ **/
+
+#include <config.h>
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include "shell-bluetooth-utils.h"
+#include "shell-enum-types.h"
+
+/**
+ * shell_bluetooth_type_to_string:
+ * @type: a #ShellBluetoothType
+ *
+ * Returns a human-readable string representation of @type usable for display to users. Do not free the 
return value.
+ * The returned string is already translated with gettext().
+ *
+ * Return value: a string.
+ **/
+const gchar *
+shell_bluetooth_type_to_string (ShellBluetoothType type)
+{
+       switch (type) {
+       case SHELL_BLUETOOTH_TYPE_PHONE:
+               return _("Phone");
+       case SHELL_BLUETOOTH_TYPE_MODEM:
+               return _("Modem");
+       case SHELL_BLUETOOTH_TYPE_COMPUTER:
+               return _("Computer");
+       case SHELL_BLUETOOTH_TYPE_NETWORK:
+               return _("Network");
+       case SHELL_BLUETOOTH_TYPE_HEADSET:
+               /* translators: a hands-free headset, a combination of a single speaker with a microphone */
+               return _("Headset");
+       case SHELL_BLUETOOTH_TYPE_HEADPHONES:
+               return _("Headphones");
+       case SHELL_BLUETOOTH_TYPE_OTHER_AUDIO:
+               return _("Audio device");
+       case SHELL_BLUETOOTH_TYPE_KEYBOARD:
+               return _("Keyboard");
+       case SHELL_BLUETOOTH_TYPE_MOUSE:
+               return _("Mouse");
+       case SHELL_BLUETOOTH_TYPE_CAMERA:
+               return _("Camera");
+       case SHELL_BLUETOOTH_TYPE_PRINTER:
+               return _("Printer");
+       case SHELL_BLUETOOTH_TYPE_JOYPAD:
+               return _("Joypad");
+       case SHELL_BLUETOOTH_TYPE_TABLET:
+               return _("Tablet");
+       case SHELL_BLUETOOTH_TYPE_VIDEO:
+               return _("Video device");
+       case SHELL_BLUETOOTH_TYPE_REMOTE_CONTROL:
+               return _("Remote control");
+       case SHELL_BLUETOOTH_TYPE_SCANNER:
+               return _("Scanner");
+       case SHELL_BLUETOOTH_TYPE_DISPLAY:
+               return _("Display");
+       case SHELL_BLUETOOTH_TYPE_WEARABLE:
+               return _("Wearable");
+       case SHELL_BLUETOOTH_TYPE_TOY:
+               return _("Toy");
+       }
+
+       return _("Unknown");
+}
+
+/**
+ * shell_bluetooth_type_to_filter_string:
+ * @type: a #ShellBluetoothType
+ *
+ * Returns a human-readable string representation of @type usable for display to users,
+ * when type filters are displayed. Do not free the return value.
+ * The returned string is already translated with gettext().
+ *
+ * Return value: a string.
+ **/
+const gchar *
+shell_bluetooth_type_to_filter_string (ShellBluetoothType type)
+{
+       switch (type) {
+       case SHELL_BLUETOOTH_TYPE_ANY:
+               return _("All types");
+       default:
+               return shell_bluetooth_type_to_string (type);
+       }
+
+       g_assert_not_reached ();
+}
+
+/**
+ * shell_bluetooth_verify_address:
+ * @bdaddr: a string representing a ShellBluetooth address
+ *
+ * Returns whether the string is a valid ShellBluetooth address. This does not contact the device in any way.
+ *
+ * Return value: %TRUE if the address is valid, %FALSE if not.
+ **/
+gboolean
+shell_bluetooth_verify_address (const char *bdaddr)
+{
+       guint i;
+
+       g_return_val_if_fail (bdaddr != NULL, FALSE);
+
+       if (strlen (bdaddr) != 17)
+               return FALSE;
+
+       for (i = 0; i < 17; i++) {
+               if (((i + 1) % 3) == 0) {
+                       if (bdaddr[i] != ':')
+                               return FALSE;
+                       continue;
+               }
+               if (g_ascii_isxdigit (bdaddr[i]) == FALSE)
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+/**
+ * shell_bluetooth_class_to_type:
+ * @class: a ShellBluetooth device class
+ *
+ * Returns the type of device corresponding to the given @class value.
+ *
+ * Return value: a #ShellBluetoothType.
+ **/
+ShellBluetoothType
+shell_bluetooth_class_to_type (guint32 class)
+{
+       switch ((class & 0x1f00) >> 8) {
+       case 0x01:
+               return SHELL_BLUETOOTH_TYPE_COMPUTER;
+       case 0x02:
+               switch ((class & 0xfc) >> 2) {
+               case 0x01:
+               case 0x02:
+               case 0x03:
+               case 0x05:
+                       return SHELL_BLUETOOTH_TYPE_PHONE;
+               case 0x04:
+                       return SHELL_BLUETOOTH_TYPE_MODEM;
+               }
+               break;
+       case 0x03:
+               return SHELL_BLUETOOTH_TYPE_NETWORK;
+       case 0x04:
+               switch ((class & 0xfc) >> 2) {
+               case 0x01:
+               case 0x02:
+                       return SHELL_BLUETOOTH_TYPE_HEADSET;
+               case 0x05:
+                       return SHELL_BLUETOOTH_TYPE_SPEAKERS;
+               case 0x06:
+                       return SHELL_BLUETOOTH_TYPE_HEADPHONES;
+               case 0x0b: /* VCR */
+               case 0x0c: /* Video Camera */
+               case 0x0d: /* Camcorder */
+                       return SHELL_BLUETOOTH_TYPE_VIDEO;
+               default:
+                       return SHELL_BLUETOOTH_TYPE_OTHER_AUDIO;
+               }
+               break;
+       case 0x05:
+               switch ((class & 0xc0) >> 6) {
+               case 0x00:
+                       switch ((class & 0x1e) >> 2) {
+                       case 0x01:
+                       case 0x02:
+                               return SHELL_BLUETOOTH_TYPE_JOYPAD;
+                       case 0x03:
+                               return SHELL_BLUETOOTH_TYPE_REMOTE_CONTROL;
+                       }
+                       break;
+               case 0x01:
+                       return SHELL_BLUETOOTH_TYPE_KEYBOARD;
+               case 0x02:
+                       switch ((class & 0x1e) >> 2) {
+                       case 0x05:
+                               return SHELL_BLUETOOTH_TYPE_TABLET;
+                       default:
+                               return SHELL_BLUETOOTH_TYPE_MOUSE;
+                       }
+               }
+               break;
+       case 0x06:
+               if (class & 0x80)
+                       return SHELL_BLUETOOTH_TYPE_PRINTER;
+               if (class & 0x40)
+                       return SHELL_BLUETOOTH_TYPE_SCANNER;
+               if (class & 0x20)
+                       return SHELL_BLUETOOTH_TYPE_CAMERA;
+               if (class & 0x10)
+                       return SHELL_BLUETOOTH_TYPE_DISPLAY;
+               break;
+       case 0x07:
+               return SHELL_BLUETOOTH_TYPE_WEARABLE;
+       case 0x08:
+               return SHELL_BLUETOOTH_TYPE_TOY;
+       }
+
+       return 0;
+}
+
+/**
+ * shell_bluetooth_appearance_to_type:
+ * @appearance: a ShellBluetooth device appearance
+ *
+ * Returns the type of device corresponding to the given @appearance value,
+ * as usually found in the GAP service.
+ *
+ * Return value: a #ShellBluetoothType.
+ **/
+ShellBluetoothType
+shell_bluetooth_appearance_to_type (guint16 appearance)
+{
+       switch ((appearance & 0xffc0) >> 6) {
+       case 0x01:
+               return SHELL_BLUETOOTH_TYPE_PHONE;
+       case 0x02:
+               return SHELL_BLUETOOTH_TYPE_COMPUTER;
+       case 0x05:
+               return SHELL_BLUETOOTH_TYPE_DISPLAY;
+       case 0x0a:
+               return SHELL_BLUETOOTH_TYPE_OTHER_AUDIO;
+       case 0x0b:
+               return SHELL_BLUETOOTH_TYPE_SCANNER;
+       case 0x0f: /* HID Generic */
+               switch (appearance & 0x3f) {
+               case 0x01:
+                       return SHELL_BLUETOOTH_TYPE_KEYBOARD;
+               case 0x02:
+                       return SHELL_BLUETOOTH_TYPE_MOUSE;
+               case 0x03:
+               case 0x04:
+                       return SHELL_BLUETOOTH_TYPE_JOYPAD;
+               case 0x05:
+                       return SHELL_BLUETOOTH_TYPE_TABLET;
+               case 0x08:
+                       return SHELL_BLUETOOTH_TYPE_SCANNER;
+               }
+               break;
+       }
+
+       return 0;
+
+}
+
+static const char *
+uuid16_custom_to_string (guint uuid16, const char *uuid)
+{
+       switch (uuid16) {
+       case 0x2:
+               return "SyncMLClient";
+       case 0x5601:
+               return "Nokia SyncML Server";
+       default:
+               g_debug ("Unhandled custom UUID %s (0x%x)", uuid, uuid16);
+               return NULL;
+       }
+}
+
+/* Short names from Table 2 at:
+ * https://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm */
+static const char *
+uuid16_to_string (guint uuid16, const char *uuid)
+{
+       switch (uuid16) {
+       case SHELL_BLUETOOTH_UUID_SPP:
+               return "SerialPort";
+       case SHELL_BLUETOOTH_UUID_DUN:
+               return "DialupNetworking";
+       case SHELL_BLUETOOTH_UUID_IRMC:
+               return "IrMCSync";
+       case SHELL_BLUETOOTH_UUID_OPP:
+               return "OBEXObjectPush";
+       case SHELL_BLUETOOTH_UUID_FTP:
+               return "OBEXFileTransfer";
+       case SHELL_BLUETOOTH_UUID_HSP:
+               return "HSP";
+       case SHELL_BLUETOOTH_UUID_A2DP_SOURCE:
+               return "AudioSource";
+       case SHELL_BLUETOOTH_UUID_A2DP_SINK:
+               return "AudioSink";
+       case SHELL_BLUETOOTH_UUID_AVRCP_TARGET:
+               return "A/V_RemoteControlTarget";
+       case SHELL_BLUETOOTH_UUID_A2DP:
+               return "AdvancedAudioDistribution";
+       case SHELL_BLUETOOTH_UUID_AVRCP_CONTROL:
+               return "A/V_RemoteControl";
+       case SHELL_BLUETOOTH_UUID_HSP_AG:
+               return "Headset_-_AG";
+       case SHELL_BLUETOOTH_UUID_PAN_PANU:
+               return "PANU";
+       case SHELL_BLUETOOTH_UUID_PAN_NAP:
+               return "NAP";
+       case SHELL_BLUETOOTH_UUID_PAN_GN:
+               return "GN";
+       case SHELL_BLUETOOTH_UUID_HFP_HF:
+               return "Handsfree";
+       case SHELL_BLUETOOTH_UUID_HFP_AG:
+               return "HandsfreeAudioGateway";
+       case SHELL_BLUETOOTH_UUID_HID:
+       case 0x1812:
+               return "HumanInterfaceDeviceService";
+       case SHELL_BLUETOOTH_UUID_SAP:
+               return "SIM_Access";
+       case SHELL_BLUETOOTH_UUID_PBAP:
+               return "Phonebook_Access_-_PSE";
+       case SHELL_BLUETOOTH_UUID_GENERIC_AUDIO:
+               return "GenericAudio";
+       case SHELL_BLUETOOTH_UUID_SDP: /* ServiceDiscoveryServerServiceClassID */
+       case SHELL_BLUETOOTH_UUID_PNP: /* PnPInformation */
+               /* Those are ignored */
+               return NULL;
+       case SHELL_BLUETOOTH_UUID_GENERIC_NET:
+               return "GenericNetworking";
+       case SHELL_BLUETOOTH_UUID_VDP_SOURCE:
+               return "VideoSource";
+       case 0x8e771303:
+       case 0x8e771301:
+               return "SEMC HLA";
+       case 0x8e771401:
+               return "SEMC Watch Phone";
+       default:
+               g_debug ("Unhandled UUID %s (0x%x)", uuid, uuid16);
+               return NULL;
+       }
+}
+
+/**
+ * shell_bluetooth_uuid_to_string:
+ * @uuid: a string representing a ShellBluetooth UUID
+ *
+ * Returns a string representing a human-readable (but not usable for display to users) version of the 
@uuid. Do not free the return value.
+ *
+ * Return value: a string.
+ **/
+const char *
+shell_bluetooth_uuid_to_string (const char *uuid)
+{
+       char **parts;
+       guint uuid16;
+       gboolean is_custom = FALSE;
+
+       if (g_str_has_suffix (uuid, "-0000-1000-8000-0002ee000002") != FALSE)
+               is_custom = TRUE;
+
+       parts = g_strsplit (uuid, "-", -1);
+       if (parts == NULL || parts[0] == NULL) {
+               g_strfreev (parts);
+               return NULL;
+       }
+
+       uuid16 = g_ascii_strtoull (parts[0], NULL, 16);
+       g_strfreev (parts);
+       if (uuid16 == 0)
+               return NULL;
+
+       if (is_custom == FALSE)
+               return uuid16_to_string (uuid16, uuid);
+       return uuid16_custom_to_string (uuid16, uuid);
+}
+
+/**
+ * shell_bluetooth_send_to_address:
+ * @address: Remote device to use
+ * @alias: Remote device's name
+ *
+ * Start a GUI application for transfering files over ShellBluetooth.
+ **/
+void
+shell_bluetooth_send_to_address (const char *address,
+                          const char *alias)
+{
+       GPtrArray *a;
+       GError *err = NULL;
+
+       a = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
+
+       g_ptr_array_add (a, g_strdup ("bluetooth-sendto"));
+       if (address != NULL)
+               g_ptr_array_add (a, g_strdup_printf ("--device=%s", address));
+       if (address != NULL && alias != NULL)
+               g_ptr_array_add (a, g_strdup_printf ("--name=%s", alias));
+       g_ptr_array_add (a, NULL);
+
+       if (g_spawn_async(NULL, (char **) a->pdata, NULL,
+                         G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err) == FALSE) {
+               g_printerr("Couldn't execute command: %s\n", err->message);
+               g_error_free (err);
+       }
+
+       g_ptr_array_free (a, TRUE);
+}
+
diff --git a/src/shell-bluetooth-utils.h b/src/shell-bluetooth-utils.h
new file mode 100644
index 0000000000..4c9a88e877
--- /dev/null
+++ b/src/shell-bluetooth-utils.h
@@ -0,0 +1,68 @@
+/*
+ *
+ *  BlueZ - ShellBluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2011  Bastien Nocera <hadess hadess net>
+ *  Copyright (C) 2010       Giovanni Campagna <scampa giovanni gmail com>
+ *
+ *
+ *  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.1 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+#include <shell-bluetooth-enums.h>
+
+/*
+ * The profile UUID list is provided by the ShellBluetooth SIG:
+ * https://www.bluetooth.com/specifications/assigned-numbers/service-discovery
+ */
+#define SHELL_BLUETOOTH_UUID_SPP               0x1101
+#define SHELL_BLUETOOTH_UUID_DUN               0x1103
+#define SHELL_BLUETOOTH_UUID_IRMC              0x1104
+#define SHELL_BLUETOOTH_UUID_OPP               0x1105
+#define SHELL_BLUETOOTH_UUID_FTP               0x1106
+#define SHELL_BLUETOOTH_UUID_HSP               0x1108
+#define SHELL_BLUETOOTH_UUID_A2DP_SOURCE       0x110A
+#define SHELL_BLUETOOTH_UUID_A2DP_SINK 0x110B
+#define SHELL_BLUETOOTH_UUID_AVRCP_TARGET      0x110C
+#define SHELL_BLUETOOTH_UUID_A2DP              0x110D
+#define SHELL_BLUETOOTH_UUID_AVRCP_CONTROL     0x110E
+#define SHELL_BLUETOOTH_UUID_HSP_AG            0x1112
+#define SHELL_BLUETOOTH_UUID_PAN_PANU          0x1115
+#define SHELL_BLUETOOTH_UUID_PAN_NAP           0x1116
+#define SHELL_BLUETOOTH_UUID_PAN_GN            0x1117
+#define SHELL_BLUETOOTH_UUID_HFP_HF            0x111E
+#define SHELL_BLUETOOTH_UUID_HFP_AG            0x111F
+#define SHELL_BLUETOOTH_UUID_HID               0x1124
+#define SHELL_BLUETOOTH_UUID_SAP               0x112d
+#define SHELL_BLUETOOTH_UUID_PBAP              0x112F
+#define SHELL_BLUETOOTH_UUID_GENERIC_AUDIO     0x1203
+#define SHELL_BLUETOOTH_UUID_SDP               0x1000
+#define SHELL_BLUETOOTH_UUID_PNP               0x1200
+#define SHELL_BLUETOOTH_UUID_GENERIC_NET       0x1201
+#define SHELL_BLUETOOTH_UUID_VDP_SOURCE        0x1303
+
+ShellBluetoothType  shell_bluetooth_class_to_type         (guint32 class);
+ShellBluetoothType  shell_bluetooth_appearance_to_type    (guint16 appearance);
+const gchar   *shell_bluetooth_type_to_string        (guint type);
+const gchar   *shell_bluetooth_type_to_filter_string (guint type);
+gboolean       shell_bluetooth_verify_address        (const char *bdaddr);
+const char    *shell_bluetooth_uuid_to_string        (const char *uuid);
+
+void shell_bluetooth_send_to_address (const char *address,
+                               const char *alias);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]