Logo Search packages:      
Sourcecode: obex-data-server version File versions  Download package

ods-bluez.c

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
 *
 * Copyright (C) 2007-2008 Tadas Dailyda <tadas@dailyda.com>
 *
 * Licensed under the GNU General Public License Version 2
 *
 * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "config.h"

#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include <glib.h>
#include <glib/gprintf.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>

#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>

#include "ods-bluez.h"
#include "ods-common.h"
#include "ods-error.h"

static void     ods_bluez_class_init      (OdsBluezClass    *klass);
static void     ods_bluez_init                  (OdsBluez         *bluez);
static void     ods_bluez_finalize        (GObject          *object);

#define ODS_BLUEZ_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ODS_TYPE_BLUEZ, OdsBluezPrivate))

struct OdsBluezPrivate
{
      gboolean    initialized;
      DBusGProxy  *manager_proxy; /* for manager interface */
      DBusGProxy  *adapter_proxy; /* for default adapter interface */
};

typedef struct GetClientSocketData_ {
      OdsBluezFunc      cb;
      gpointer          data;
      gchar             *address;
      gchar             *uuid;
} GetClientSocketData;

G_DEFINE_TYPE (OdsBluez, ods_bluez, G_TYPE_OBJECT)

/**
 * ods_bluez_class_init:
 * @klass: The OdsBluezClass
 **/
static void
ods_bluez_class_init (OdsBluezClass *klass)
{
      GObjectClass *object_class = G_OBJECT_CLASS (klass);
      
      object_class->finalize = ods_bluez_finalize;
      
      g_type_class_add_private (klass, sizeof (OdsBluezPrivate));
      
      GError *error = NULL;

      /* Init the DBus connection, per-klass */
      klass->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
      if (klass->connection == NULL)
      {
            g_warning("Unable to connect to dbus: %s", error->message);
            g_clear_error (&error);
            return;
      }
}

static void
default_adapter_changed_cb (DBusGProxy *proxy, const gchar *new_path,
                                          OdsBluez *bluez)
{
      OdsBluezClass *klass = ODS_BLUEZ_GET_CLASS (bluez);
      
      g_object_unref (bluez->priv->adapter_proxy);
      bluez->priv->adapter_proxy = dbus_g_proxy_new_for_name (klass->connection,
                                                                                          "org.bluez",
                                                                                          new_path,
                                                                                          "org.bluez.Adapter");
}

/**
 * ods_bluez_init:
 * @bluez: This class instance
 **/
static void
ods_bluez_init (OdsBluez *bluez)
{
      GError            *error = NULL;
      gchar       *adapter_object;
      
      OdsBluezClass *klass = ODS_BLUEZ_GET_CLASS (bluez);
      bluez->priv = ODS_BLUEZ_GET_PRIVATE (bluez);
      
      bluez->priv->manager_proxy = dbus_g_proxy_new_for_name (klass->connection,
                                                                                          "org.bluez", 
                                                                                          "/org/bluez", 
                                                                                          "org.bluez.Manager");
      if (!dbus_g_proxy_call (bluez->priv->manager_proxy, "DefaultAdapter", &error, 
                                                G_TYPE_INVALID,
                                                G_TYPE_STRING, &adapter_object, 
                                                G_TYPE_INVALID)) {
            g_warning("Unable to connect to dbus: %s", error->message);
            g_clear_error (&error);
            bluez->priv->initialized = FALSE;
            return;
      }
      /* Connect to DefaultAdapterChanged signal */
      dbus_g_proxy_add_signal (bluez->priv->manager_proxy, "DefaultAdapterChanged",
                                                G_TYPE_STRING, G_TYPE_INVALID);
      dbus_g_proxy_connect_signal (bluez->priv->manager_proxy, 
                                                      "DefaultAdapterChanged",
                                                      G_CALLBACK (default_adapter_changed_cb),
                                                      bluez, NULL);
      
      bluez->priv->adapter_proxy = dbus_g_proxy_new_for_name (klass->connection,
                                                                                          "org.bluez",
                                                                                          adapter_object,
                                                                                          "org.bluez.Adapter");
      bluez->priv->initialized = TRUE;
      g_free (adapter_object);
}

/**
 * ods_bluez_finalize:
 * @object: The object to finalize
 *
 * Finalize object
 **/
static void
ods_bluez_finalize (GObject *object)
{
      OdsBluez *bluez;
      
      g_return_if_fail (object != NULL);
      g_return_if_fail (ODS_IS_BLUEZ (object));

      bluez = ODS_BLUEZ (object);

      g_return_if_fail (bluez->priv != NULL);
      
      if (G_IS_OBJECT (bluez->priv->manager_proxy))
            g_object_unref (G_OBJECT (bluez->priv->manager_proxy));
      if (G_IS_OBJECT (bluez->priv->adapter_proxy))
            g_object_unref (G_OBJECT (bluez->priv->adapter_proxy));

      G_OBJECT_CLASS (ods_bluez_parent_class)->finalize (object);
}

/**
 * ods_bluez_new:
 *
 * Return value: a new OdsBluez object.
 **/
OdsBluez *
ods_bluez_new ()
{
      OdsBluez *bluez;
      bluez = g_object_new (ODS_TYPE_BLUEZ, NULL);
      return ODS_BLUEZ (bluez);
}

/**
 * ods_bluez_is_initialized:
 * @bluez: OdsBluez instance
 *
 * Checks if object was initialized succesfully. Might not be initialized
 * if Bluez DBus interface is not available or there are no adapters
 * connected.
 * 
 * Return value: TRUE for success, FALSE otherwise.
 **/
gboolean
ods_bluez_is_initialized (OdsBluez *bluez)
{
      return bluez->priv->initialized;
}

static void
cb_data_free (GetClientSocketData *cb_data)
{
      g_free (cb_data->address);
      g_free (cb_data->uuid);
      g_free (cb_data);
}

static gboolean
client_socket_connect_cb (GIOChannel *io_channel, GIOCondition cond,
                                          GetClientSocketData *cb_data)
{
      OdsBluezFunc      cb = cb_data->cb;
      gint              fd = -1;
      GError                  *error = NULL;
      
      g_message ("Connect complete");
      if (cond & G_IO_OUT) {
            fd = g_io_channel_unix_get_fd (io_channel);
      } else {
            g_set_error (&error, ODS_ERROR, ODS_ERROR_CONNECTION_ATTEMPT_FAILED,
                                    "Connect failed");
      }
      
      cb (fd, error, cb_data->data);
      cb_data_free (cb_data);
      g_clear_error (&error);
      g_io_channel_unref (io_channel);
      return FALSE;
}

static void
rfcomm_connect (GetClientSocketData *cb_data, gint channel)
{
      OdsBluezFunc            cb = cb_data->cb;
    GError                    *error = NULL;
    struct sockaddr_rc  addr;
    int                             fd = -1;
    GIOChannel                *io_channel;
    
    /* Create socket and start connecting */
    fd = socket (AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
    if (fd < 0) {
            g_set_error (&error, ODS_ERROR, ODS_ERROR_CONNECTION_ATTEMPT_FAILED,
                        "Could not create socket");
            goto err;
    }

    memset (&addr, 0, sizeof(addr));
    /* destination address */
    addr.rc_family  = AF_BLUETOOTH;
    addr.rc_channel = channel;
    str2ba (cb_data->address, &addr.rc_bdaddr);
    
    g_message("Connecting to %s using channel %d", cb_data->address, channel);

    /* Use non-blocking connect */
    fcntl (fd, F_SETFL, O_NONBLOCK);
    io_channel = g_io_channel_unix_new (fd);
      
    if (connect (fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
            /* BlueZ returns EAGAIN eventhough it should return EINPROGRESS */
            if (!(errno == EAGAIN || errno == EINPROGRESS)) {
                g_set_error (&error, ODS_ERROR, 
                                    ODS_ERROR_CONNECTION_ATTEMPT_FAILED, "Connect failed");
                goto err;
            }
            
            g_message ("Connect in progress");
            g_io_add_watch (io_channel, 
                                  G_IO_OUT | G_IO_ERR | G_IO_NVAL | G_IO_HUP,
                                  (GIOFunc) client_socket_connect_cb, cb_data);
            return;
    } else {
            /* Connect succeeded with first try */
      g_message ("Connect on first try");
            client_socket_connect_cb (io_channel, G_IO_OUT, cb_data);
            return;
    }

err:
    if (fd >= 0)
      close (fd);
    cb (-1, error, cb_data->data);
    cb_data_free (cb_data);
    g_clear_error (&error);
}


static void
get_remote_service_record_cb (DBusGProxy *proxy, DBusGProxyCall *call,
                              GetClientSocketData *cb_data)
{
      OdsBluezFunc            cb = cb_data->cb;
      GError                        *error = NULL;
      GArray                        *record_array = NULL;
      sdp_record_t            *sdp_record = NULL;
      gint                    scanned;
      sdp_list_t              *protos = NULL;
      gint                    channel = -1;
      
      if (!dbus_g_proxy_end_call (proxy, call, &error,
                                                DBUS_TYPE_G_UCHAR_ARRAY, &record_array,
                                                G_TYPE_INVALID)) {
            /* Remote device doesn't have service record with specified UUID */
            g_clear_error (&error);
            g_set_error (&error, ODS_ERROR, ODS_ERROR_NOT_SUPPORTED,
                                    "Remote device does not provide requested service");
            goto err;
      }
      
      sdp_record = sdp_extract_pdu ((uint8_t *)record_array->data, &scanned);
      
      /* get channel for this service */
      if (sdp_get_access_protos (sdp_record, &protos) != 0) {
            g_set_error (&error, ODS_ERROR, ODS_ERROR_FAILED,
                                    "Could not get service channel");
            goto err;
      }
      
      channel = sdp_get_proto_port (protos, RFCOMM_UUID);

      if (protos) {
            sdp_list_foreach (protos, (sdp_list_func_t)sdp_list_free, 0);
            sdp_list_free (protos, 0);
      }
      if (sdp_record)
            sdp_record_free (sdp_record);
      
      rfcomm_connect (cb_data, channel);
      g_array_free (record_array, TRUE);
      return;

err:
      if (record_array != NULL)
            g_array_free (record_array, TRUE);
      cb (-1, error, cb_data->data);
      cb_data_free (cb_data);
      g_clear_error (&error);
}

static void
get_remote_service_handles_cb (DBusGProxy *proxy, DBusGProxyCall *call,
                                                GetClientSocketData *cb_data)
{
      OdsBluezFunc      cb = cb_data->cb;
      gboolean          ret;
      GError                  *error = NULL;
      GArray                  *handle_array = NULL;
      guint32                 service_handle = 0;
      
      ret = dbus_g_proxy_end_call (proxy, call, &error,
                                                DBUS_TYPE_G_UINT_ARRAY, &handle_array,
                                                G_TYPE_INVALID);
      
      /* check if we were looking for Nokia specific FTP service and failed */
      if (ret && handle_array->len == 0 && !strcmp (cb_data->uuid, OBEX_NOKIAFTP_UUID)) {
            g_free (cb_data->uuid);
            cb_data->uuid = g_strdup (OBEX_FTP_UUID);
            dbus_g_proxy_begin_call (proxy, 
                                                "GetRemoteServiceHandles",
                                                (DBusGProxyCallNotify) get_remote_service_handles_cb,
                                                cb_data, NULL,
                                                G_TYPE_STRING, cb_data->address,
                                                G_TYPE_STRING, cb_data->uuid,
                                                G_TYPE_INVALID);
            return;
      }
      /* service search failed */
      if (!ret || handle_array->len == 0) {
            g_clear_error (&error);
            g_set_error (&error, ODS_ERROR, ODS_ERROR_CONNECTION_ATTEMPT_FAILED,
                                    "Service search failed");
            cb (-1, error, cb_data->data);
            cb_data_free (cb_data);
            g_clear_error (&error);
            goto out;
      }

      memcpy(&service_handle, handle_array->data, sizeof(service_handle));

      /* Now get service record */
      dbus_g_proxy_begin_call (proxy,
                                          "GetRemoteServiceRecord",
                                          (DBusGProxyCallNotify) get_remote_service_record_cb,
                                          cb_data, NULL,
                                          G_TYPE_STRING, cb_data->address,
                                          G_TYPE_UINT, service_handle,
                                          G_TYPE_INVALID);

out:
      if (handle_array != NULL)
            g_array_free (handle_array, TRUE);
}

/**
 * ods_bluez_get_client_socket:
 * @bluez: OdsBluez instance
 * @address: target Bluetooth address
 * @uuid:
 * @func:
 * @data:
 *
 * Connects client RFCOMM socket
 * 
 * Return value: 
 **/
void
ods_bluez_get_client_socket (OdsBluez *bluez, 
                                                const gchar *address,
                                                const gchar *uuid,
                                                gint channel,
                                                OdsBluezFunc func,
                                                gpointer data)
{
      GetClientSocketData *cb_data;
      
      cb_data = g_new0 (GetClientSocketData, 1);
      cb_data->cb = func;
      cb_data->address = g_strdup (address);
      cb_data->data = data;
      /* From Johan Hedberg:
       *
       * some Nokia Symbian phones have two OBEX FTP services: one
       * identified with the normal UUID and another with a Nokia specific
       * 128 bit UUID. The service found behind the normal identifier is
       * very limited in features on these phones while the other one
       * supports full OBEX FTP (don't ask me why).
       */
      /* if FTP was requested, use NOKIAFTP instead, 
       * if it isn't found we retreat to FTP in get_remote_service_handles_cb */
      if (!strcmp (uuid, OBEX_FTP_UUID))
            cb_data->uuid = g_strdup (OBEX_NOKIAFTP_UUID);
      else
            cb_data->uuid = g_strdup (uuid);
      
      /* Discover channel for needed service only if we don't know it yet */  
      if (channel == 0) {
            /* find services that match our UUID */
            dbus_g_proxy_begin_call (bluez->priv->adapter_proxy, 
                                                "GetRemoteServiceHandles",
                                                (DBusGProxyCallNotify) get_remote_service_handles_cb,
                                                cb_data, NULL,
                                                G_TYPE_STRING, cb_data->address,
                                                G_TYPE_STRING, cb_data->uuid,
                                                G_TYPE_INVALID);
      } else { 
            rfcomm_connect (cb_data, channel);
      }
}

/**
 * ods_bluez_get_server_socket:
 * @bluez: OdsBluez instance
 * @address: source Bluetooth address
 * @channel: RFCOMM channel to bind to
 *
 * Opens server RFCOMM socket
 * 
 * Return value: opened socket (-1 on failure).
 **/
gint
ods_bluez_get_server_socket (OdsBluez *bluez, 
                                                const gchar *address, guint8 channel)
{
      struct sockaddr_rc addr;
      gint fd = -1;

      fd = socket (AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
      if (fd < 0)
            goto err;

      memset (&addr, 0, sizeof(addr));
      addr.rc_family  = AF_BLUETOOTH;
      addr.rc_channel = channel;
      str2ba (address, &addr.rc_bdaddr);

      if (bind (fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
            goto err;
      }

      if (listen (fd, 1) < 0) {
            goto err;
      }

      return fd;
err:
      if (fd >= 0)
            close (fd);
      return -1;
}

static guint32
add_bin_service_record (DBusGProxy *database_proxy, sdp_record_t *rec)
{
      sdp_buf_t   buf;
      GError            *error = NULL;
      GArray            *byte_array = NULL;
      guint32           record_handle;
      guint32           ret; 
      
      sdp_gen_record_pdu (rec, &buf);
      byte_array = g_array_new (FALSE, FALSE, sizeof (guint8));
      byte_array->len = buf.data_size;
      byte_array->data = (gchar*)buf.data; 
      /* Add binary service record */
      if (!dbus_g_proxy_call (database_proxy,
                                          "AddServiceRecord", &error,
                                          DBUS_TYPE_G_UCHAR_ARRAY, byte_array,
                                          G_TYPE_INVALID,
                                          G_TYPE_UINT, &record_handle,
                                          G_TYPE_INVALID)) {
            g_warning (error->message);
            g_clear_error (&error);
            ret = 0;
      } else {
            ret = record_handle;
      }
      
      g_array_free (byte_array, FALSE);
      g_free (buf.data);
      return ret;
}

static guint32
add_service_record_internal (DBusGProxy *database_proxy, gint service)
{
      guint32 ret;
      /* vars that differ according to service */
      guint8 chan;
      guint16 svclass_id_;
      guint16 profile_id_;
      gchar *desc;
      /* --- */
      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
      uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
      sdp_profile_desc_t profile[1];
      sdp_list_t *aproto, *proto[3];
      sdp_data_t *channel;
      /* only for OPP */
      uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xFF };
      void *dtds[sizeof(formats)], *values[sizeof(formats)];
      guint32 i;
      uint8_t dtd = SDP_UINT8;
      sdp_data_t *sflist;
      /* --- */
      sdp_record_t rec;
      
      switch (service) {
            case ODS_SERVICE_OPP:
                  chan = ODS_OPP_RFCOMM_CHANNEL;
                  svclass_id_ = OBEX_OBJPUSH_SVCLASS_ID;
                  profile_id_ = OBEX_OBJPUSH_PROFILE_ID;
                  desc = "OBEX Object Push";
                  break;
            case ODS_SERVICE_FTP:
                  chan = ODS_FTP_RFCOMM_CHANNEL;
                  svclass_id_ = OBEX_FILETRANS_SVCLASS_ID;
                  profile_id_ = OBEX_FILETRANS_PROFILE_ID;
                  desc = "OBEX File Transfer";
                  break;
            case ODS_SERVICE_PBAP:
                  chan = ODS_PBAP_RFCOMM_CHANNEL;
                  svclass_id_ = PBAP_PSE_SVCLASS_ID;
                  profile_id_ = PBAP_PSE_PROFILE_ID;
                  desc = "OBEX Phonebook Access";
                  break;
            default:
                  return 0;
      }
      memset (&rec, 0, sizeof(sdp_record_t));
      rec.handle = 0xffffffff;
      
      sdp_uuid16_create (&root_uuid, PUBLIC_BROWSE_GROUP);
      root = sdp_list_append (0, &root_uuid);
      sdp_set_browse_groups (&rec, root);
      
      sdp_uuid16_create (&svclass_uuid, svclass_id_);
      svclass_id = sdp_list_append (0, &svclass_uuid);
      sdp_set_service_classes (&rec, svclass_id);
      
      sdp_uuid16_create (&profile[0].uuid, profile_id_);
      profile[0].version = 0x0100;
      pfseq = sdp_list_append (0, profile);
      sdp_set_profile_descs (&rec, pfseq);
      
      sdp_uuid16_create (&l2cap_uuid, L2CAP_UUID);
      proto[0] = sdp_list_append (0, &l2cap_uuid);
      apseq = sdp_list_append (0, proto[0]);
      
      sdp_uuid16_create (&rfcomm_uuid, RFCOMM_UUID);
      proto[1] = sdp_list_append (0, &rfcomm_uuid);
      channel = sdp_data_alloc (SDP_UINT8, &chan);
      proto[1] = sdp_list_append (proto[1], channel);
      apseq = sdp_list_append (apseq, proto[1]);
      
      sdp_uuid16_create (&obex_uuid, OBEX_UUID);
      proto[2] = sdp_list_append (0, &obex_uuid);
      apseq = sdp_list_append (apseq, proto[2]);
      
      aproto = sdp_list_append (0, apseq);
      sdp_set_access_protos (&rec, aproto);
      
      if (service == ODS_SERVICE_OPP) {
            for (i = 0; i < sizeof(formats); i++) {
                dtds[i] = &dtd;
                values[i] = &formats[i];
            }
            sflist = sdp_seq_alloc (dtds, values, sizeof(formats));
            sdp_attr_add (&rec, SDP_ATTR_SUPPORTED_FORMATS_LIST, sflist);
      }
      
      sdp_set_info_attr (&rec, desc, 0, 0);
      
      ret = add_bin_service_record (database_proxy, &rec);

      sdp_list_free (root, NULL);
      sdp_list_free (svclass_id, NULL);
      sdp_list_free (pfseq, NULL);
      sdp_list_free (apseq, NULL);
      sdp_list_free (aproto, NULL);
      sdp_list_free (proto[0], NULL);
      sdp_list_free (proto[1], NULL);
      sdp_list_free (proto[2], NULL);
      sdp_data_free (channel);
      sdp_list_free (rec.attrlist, (sdp_free_func_t) sdp_data_free);
      sdp_list_free (rec.pattern, free);
      
      return ret;
}

static DBusGProxy*
get_database_proxy (OdsBluez *bluez, const gchar *device)
{
      GError            *error = NULL;
      gchar       *adapter_object = NULL;
      DBusGProxy  *database_proxy;
      
      OdsBluezClass *klass = ODS_BLUEZ_GET_CLASS (bluez);
      
      /* Get database object proxy according to selected adapter */
      if (!strcmp (device, "00:00:00:00:00:00")) {
            g_message ("Default adapter");
            database_proxy = dbus_g_proxy_new_for_name (klass->connection,
                                                                              "org.bluez",
                                                                              "/org/bluez",
                                                                              "org.bluez.Database");
      } else {
            if (!dbus_g_proxy_call (bluez->priv->manager_proxy, "FindAdapter", &error, 
                                                      G_TYPE_STRING, device,
                                                      G_TYPE_INVALID,
                                                      G_TYPE_STRING, &adapter_object, 
                                                      G_TYPE_INVALID)) {
                  g_warning ("DBus error (FindAdapter): %s", error->message);
                  g_clear_error (&error);
                  return NULL;
            }
            
            database_proxy = dbus_g_proxy_new_for_name (klass->connection,
                                                                              "org.bluez",
                                                                              adapter_object,
                                                                              "org.bluez.Database");
      }
      
      g_free (adapter_object);
      return database_proxy;
}

/**
 * ods_bluez_add_service_record:
 * @bluez: OdsBluez instance
 * @device: Bluetooth address of chosen adapter (or BDADDR_ANY for default adapter)
 * @service: service for which to add record (any of ODS_SERVICE_)
 *
 * Adds SDP service record
 * 
 * Return value: record handle (0 on failure).
 **/
guint32
ods_bluez_add_service_record (OdsBluez *bluez, const gchar *device, gint service)
{
      DBusGProxy  *database_proxy;
      guint32           record_handle = 0;
      
      database_proxy = get_database_proxy (bluez, device);
      record_handle = add_service_record_internal (database_proxy,
                                                                              service);

      if (database_proxy)
            g_object_unref (database_proxy);
      return record_handle;
}

/**
 * ods_bluez_remove_service_record:
 * @bluez: OdsBluez instance
 * @device: Bluetooth address of adapter record belongs to
 * @record_handle: handle of SDP record to remove
 *
 * Removes SDP service record
 * 
 * Return value:
 **/
void
ods_bluez_remove_service_record (OdsBluez *bluez, const gchar *device,
                                                      guint32 record_handle)
{
      GError            *error = NULL;
      DBusGProxy  *database_proxy;
      
      database_proxy = get_database_proxy (bluez, device);
      if (!dbus_g_proxy_call (database_proxy, "RemoveServiceRecord", &error, 
                                                      G_TYPE_UINT, record_handle,
                                                      G_TYPE_INVALID,
                                                      G_TYPE_INVALID)) {
            g_warning ("DBus error (RemoveServiceRecord): %s", error->message);
            g_clear_error (&error);
      }
      
      
      if (database_proxy)
            g_object_unref (database_proxy);
}

Generated by  Doxygen 1.6.0   Back to index