Bluez 5.43 Upgrade: Apply Tizen patches 79/121679/1 accepted/tizen_common accepted/tizen_ivi accepted/tizen_mobile accepted/tizen_tv accepted/tizen_wearable accepted/tizen/common/20170330.151746 accepted/tizen/ivi/20170330.223808 accepted/tizen/mobile/20170330.223718 accepted/tizen/tv/20170330.223738 accepted/tizen/unified/20170330.223834 accepted/tizen/wearable/20170330.223751 submit/tizen/20170330.042352
authorh.sandeep <h.sandeep@samsung.com>
Thu, 2 Feb 2017 11:19:57 +0000 (16:49 +0530)
committerh.sandeep <h.sandeep@samsung.com>
Tue, 28 Mar 2017 11:03:31 +0000 (16:33 +0530)
Change-Id: I5d5a9d9e2e7811af3dd3878b4f740e1f1a019d51
Signed-off-by: h.sandeep <h.sandeep@samsung.com>
191 files changed:
Makefile.am
Makefile.obexd
Makefile.plugins
Makefile.tools
android/bas.c [new file with mode: 0755]
android/bas.h [new file with mode: 0755]
android/dis.c [new file with mode: 0755]
android/dis.h [new file with mode: 0755]
android/hog.c [new file with mode: 0755]
android/hog.h [new file with mode: 0755]
android/sco-ipc-api.txt [new file with mode: 0755]
android/scpp.c [new file with mode: 0755]
android/scpp.h [new file with mode: 0755]
android/socket-api.txt [new file with mode: 0755]
attrib/att.c
attrib/att.h
attrib/gatt-service.c
attrib/gatt-service.h
attrib/gatt.c
attrib/gatt.h
attrib/interactive.c
client/main.c
configure.ac
doc/adapter-api.txt
doc/coding-style.txt [new file with mode: 0755]
doc/device-api.txt
doc/gatt-api.txt
doc/maintainer-guidelines.txt [new file with mode: 0755]
doc/mgmt-api.txt
doc/network-api.txt
doc/oob-api.txt [new file with mode: 0755]
doc/profile-api.txt
doc/settings-storage.txt
gdbus/gdbus.h
gdbus/object.c
gobex/gobex-apparam.c
gobex/gobex-apparam.h
gobex/gobex-header.c
gobex/gobex.c
lib/bluetooth.h
lib/hci.c
lib/hci.h
lib/hci_lib.h
lib/l2cap.h
lib/mgmt.h
lib/uuid.h
monitor/broadcom.c
monitor/control.c
monitor/control.h
monitor/display.h
monitor/main.c
monitor/packet.c
monitor/packet.h
obexd/client/bluetooth.c
obexd/client/manager.c
obexd/client/mns-tizen.c [new file with mode: 0755]
obexd/client/mns-tizen.h [new file with mode: 0755]
obexd/client/opp.c
obexd/client/pbap.c
obexd/client/session.c
obexd/client/session.h
obexd/client/transfer.c
obexd/plugins/ftp.c
obexd/plugins/messages-tizen.c [new file with mode: 0755]
obexd/plugins/messages-tracker.c [new file with mode: 0755]
obexd/plugins/pbap.c
obexd/plugins/phonebook-dummy.c
obexd/plugins/phonebook-ebook.c [new file with mode: 0755]
obexd/plugins/phonebook-tizen.c [new file with mode: 0755]
obexd/plugins/phonebook-tracker.c [new file with mode: 0755]
obexd/plugins/syncevolution.c [new file with mode: 0755]
obexd/src/main.c
obexd/src/manager.c
obexd/src/obex.c
obexd/src/obexd.h
obexd/src/org.bluez.obex.service
packaging/bluez.spec
plugins/dbusoob.c [new file with mode: 0755]
plugins/neard.c
plugins/policy.c
profile.h [new file with mode: 0755]
profiles/alert/server.c [new file with mode: 0755]
profiles/audio/a2dp.c
profiles/audio/avctp.c
profiles/audio/avctp.h
profiles/audio/avdtp.c
profiles/audio/avdtp.h
profiles/audio/avrcp.c
profiles/audio/media.c
profiles/audio/player.c
profiles/audio/player.h
profiles/audio/sink.c
profiles/cyclingspeed/cyclingspeed.c [new file with mode: 0755]
profiles/gap/gas.c
profiles/heartrate/heartrate.c [new file with mode: 0755]
profiles/input/device.c
profiles/input/device.h
profiles/input/manager.c
profiles/input/server.c
profiles/network/bnep.c
profiles/network/bnep.h
profiles/network/connection.c
profiles/network/server.c
profiles/proximity/immalert.c [new file with mode: 0755]
profiles/proximity/immalert.h [new file with mode: 0755]
profiles/proximity/linkloss.c [new file with mode: 0755]
profiles/proximity/linkloss.h [new file with mode: 0755]
profiles/proximity/main.c [new file with mode: 0755]
profiles/proximity/manager.c [new file with mode: 0755]
profiles/proximity/manager.h [new file with mode: 0755]
profiles/proximity/monitor.c [new file with mode: 0755]
profiles/proximity/monitor.h [new file with mode: 0755]
profiles/proximity/reporter.c [new file with mode: 0755]
profiles/proximity/reporter.h [new file with mode: 0755]
profiles/tds/manager.c [new file with mode: 0755]
profiles/tds/tds.c [new file with mode: 0755]
profiles/tds/tds.h [new file with mode: 0755]
profiles/thermometer/thermometer.c [new file with mode: 0755]
profiles/time/server.c [new file with mode: 0755]
src/adapter.c
src/adapter.h
src/adapter_le_vsc_features.c [new file with mode: 0755]
src/adapter_le_vsc_features.h [new file with mode: 0755]
src/attio.h [new file with mode: 0755]
src/attrib-server.c
src/attrib-server.h
src/bluetooth.conf
src/bluetooth.service.in
src/device.c
src/device.h
src/eir.c
src/eir.h
src/gatt-client.c
src/gatt-database.c
src/hcid.h
src/log.c
src/log.h
src/main.c
src/main_hive.conf [new file with mode: 0755]
src/main_ivi.conf [new file with mode: 0755]
src/main_m.conf [new file with mode: 0755]
src/main_w.conf [new file with mode: 0755]
src/oob.c [new file with mode: 0755]
src/oob.h [new file with mode: 0755]
src/profile.c
src/profile.h
src/sdp-xml.c
src/service.c
src/shared/att.c
src/shared/att.h
src/shared/btsnoop.c
src/shared/btsnoop.h
src/shared/crypto.c
src/shared/gatt-client.c
src/shared/gatt-client.h
src/shared/gatt-db.c
src/shared/gatt-db.h
src/shared/gatt-server.c
src/shared/gatt-server.h
src/storage.c
src/storage.h
test/advertisement-example [new file with mode: 0755]
test/exchange-business-cards [new file with mode: 0755]
test/get-managed-objects [new file with mode: 0755]
test/get-obex-capabilities [new file with mode: 0755]
test/list-folders [new file with mode: 0755]
test/simple-obex-agent [new file with mode: 0755]
tools/bdaddr.1 [new file with mode: 0755]
tools/bneptest.c
tools/btgatt-client.c
tools/btsnoop.c
tools/example.psr [new file with mode: 0755]
tools/gatt-example [new file with mode: 0755]
tools/hciattach.c
tools/hciattach.h
tools/hciattach_bcm43xx.c
tools/hciattach_sprd.c [new file with mode: 0755]
tools/hciattach_sprd.h [new file with mode: 0755]
tools/hciattach_ti.c
tools/hciconfig.c
tools/hcidump.c
tools/hcitool.c
tools/ibeacon.c
tools/l2test.c
tools/parse_companies.pl [new file with mode: 0755]
tools/parser/l2cap.h
tools/pskey_get.c [new file with mode: 0755]
tools/sdptool.c
tools/test-runner.c
tools/update_compids.sh [new file with mode: 0755]
tools/valgrind.supp [new file with mode: 0755]

index ce7fd0f..7a8ad8d 100755 (executable)
@@ -182,6 +182,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
                        src/agent.h src/agent.c \
                        src/error.h src/error.c \
                        src/adapter.h src/adapter.c \
+                       src/adapter_le_vsc_features.h src/adapter_le_vsc_features.c \
                        src/profile.h src/profile.c \
                        src/service.h src/service.c \
                        src/gatt-client.h src/gatt-client.c \
@@ -191,7 +192,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
 src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
                        gdbus/libgdbus-internal.la \
                        src/libshared-glib.la \
-                       @BACKTRACE_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ -ldl -lrt
+                       @BACKTRACE_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @LIBXML_LIBS@ @INIPARSER_LIBS@ -ldl -lrt
 src_bluetoothd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic \
                                -Wl,--version-script=$(srcdir)/src/bluetooth.ver
 
@@ -213,7 +214,7 @@ CLEANFILES += $(builtin_files) src/bluetooth.service
 man_MANS = src/bluetoothd.8
 
 EXTRA_DIST += src/genbuiltin src/bluetooth.conf \
-                       src/main.conf profiles/network/network.conf \
+                       src/main_m.conf src/main_w.conf profiles/network/network.conf \
                        profiles/input/input.conf profiles/proximity/proximity.conf
 
 test_scripts =
@@ -266,7 +267,7 @@ EXTRA_DIST += doc/btsnoop.txt
 
 EXTRA_DIST += tools/magic.btsnoop
 
-AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@
+AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@ @LIBXML_CFLAGS@ @INIPARSER_CFLAGS@
 
 AM_CPPFLAGS = -I$(builddir)/lib
 
@@ -421,6 +422,16 @@ unit_test_gattrib_LDADD = lib/libbluetooth-internal.la \
                        src/libshared-glib.la \
                        @GLIB_LIBS@ @DBUS_LIBS@ -ldl -lrt
 
+if MIDI
+unit_tests += unit/test-midi
+unit_test_midi_CFLAGS = $(AM_CFLAGS) @ALSA_CFLAGS@ -DMIDI_TEST
+unit_test_midi_SOURCES = unit/test-midi.c \
+                       profiles/midi/libmidi.h \
+                       profiles/midi/libmidi.c
+unit_test_midi_LDADD = src/libshared-glib.la \
+                       @GLIB_LIBS@ @ALSA_LIBS@
+endif
+
 if MAINTAINER_MODE
 noinst_PROGRAMS += $(unit_tests)
 endif
index 42135a9..05a3b6b 100755 (executable)
@@ -82,11 +82,13 @@ obexd_src_obexd_SOURCES = $(btio_sources) $(gobex_sources) \
                        obexd/src/map_ap.h
 obexd_src_obexd_LDADD = lib/libbluetooth-internal.la \
                        gdbus/libgdbus-internal.la \
-                       @ICAL_LIBS@ @DBUS_LIBS@ @GLIB_LIBS@ -ldl
+                       @ICAL_LIBS@ @DBUS_LIBS@ @GLIB_LIBS@ \
+                       @LIBXML_LIBS@ @INIPARSER_LIBS@ -ldl
 
 obexd_src_obexd_LDFLAGS = -Wl,--export-dynamic
 
 obexd_src_obexd_CFLAGS = $(AM_CFLAGS) @GLIB_CFLAGS@ @DBUS_CFLAGS@ \
+                               @LIBXML_CFLAGS@ @INIPARSER_CFLAGS@ \
                                @ICAL_CFLAGS@ -DOBEX_PLUGIN_BUILTIN \
                                -DPLUGINDIR=\""$(obex_plugindir)"\" \
                                -fPIC -D_FILE_OFFSET_BITS=64 -pie
index 53e80fe..d0b81d0 100755 (executable)
@@ -2,32 +2,46 @@
 builtin_modules += hostname
 builtin_sources += plugins/hostname.c
 
+if TIZEN_UNUSED_PLUGIN
 builtin_modules += wiimote
 builtin_sources += plugins/wiimote.c
+endif
 
+if AUTOPAIR
 builtin_modules += autopair
 builtin_sources += plugins/autopair.c
+endif
 
 builtin_modules += policy
 builtin_sources += plugins/policy.c
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+builtin_modules += dbusoob
+builtin_sources += plugins/dbusoob.c \
+                       src/oob.h src/oob.c
+#endif
+
 if MAINTAINER_MODE
 builtin_modules += gatt_example
 builtin_sources += plugins/gatt-example.c
 endif
 
 if EXPERIMENTAL
+if TIZEN_UNUSED_PLUGIN
 builtin_modules += neard
 builtin_sources += plugins/neard.c
+endif
 
+if TIZEN_SAP_PLUGIN
 builtin_modules += sap
 builtin_sources += profiles/sap/main.c profiles/sap/manager.h \
                        profiles/sap/manager.c profiles/sap/server.h \
                        profiles/sap/server.c profiles/sap/sap.h \
                        profiles/sap/sap-u8500.c
 
-noinst_LIBRARIES += profiles/sap/libsap.a
-profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c
+#noinst_LIBRARIES += profiles/sap/libsap.a
+#profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c
+endif
 endif
 
 builtin_modules += a2dp
@@ -52,12 +66,18 @@ builtin_sources += profiles/network/manager.c \
                        profiles/network/connection.h \
                        profiles/network/connection.c
 
+#if WEARABLE
+#builtin_modules +=
+#builtin_sources +=
+#else
+if TIZEN_HID_PLUGIN
 builtin_modules += input
 builtin_sources += profiles/input/manager.c \
                        profiles/input/server.h profiles/input/server.c \
                        profiles/input/device.h profiles/input/device.c \
                        profiles/input/hidp_defs.h
 
+if TIZEN_UNUSED_PLUGIN
 builtin_modules += hog
 builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \
                        profiles/input/hog-lib.c profiles/input/hog-lib.h \
@@ -67,8 +87,12 @@ builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \
                        profiles/input/suspend.h profiles/input/suspend-none.c
 
 EXTRA_DIST += profiles/input/suspend-dummy.c
+endif
+endif
+#endif
 
 if EXPERIMENTAL
+if TIZEN_HEALTH_PLUGIN
 builtin_modules += health
 builtin_sources += profiles/health/mcap.h profiles/health/mcap.c \
                        profiles/health/hdp_main.c profiles/health/hdp_types.h \
@@ -77,7 +101,9 @@ builtin_sources += profiles/health/mcap.h profiles/health/mcap.c \
                        profiles/health/hdp.h profiles/health/hdp.c \
                        profiles/health/hdp_util.h profiles/health/hdp_util.c
 endif
+endif
 
+if TIZEN_HEALTH_PLUGIN
 builtin_modules += gap
 builtin_sources += profiles/gap/gas.c
 
@@ -86,11 +112,52 @@ builtin_sources += profiles/scanparam/scan.c
 
 builtin_modules += deviceinfo
 builtin_sources += profiles/deviceinfo/deviceinfo.c
+endif
+
+if EXPERIMENTAL
+if TIZEN_UNUSED_PLUGIN
+if TIZEN_PROXIMITY_PLUGIN
+builtin_modules += proximity
+builtin_sources += profiles/proximity/main.c profiles/proximity/manager.h \
+                       profiles/proximity/manager.c \
+                       profiles/proximity/monitor.h \
+                       profiles/proximity/monitor.c \
+                       profiles/proximity/reporter.h \
+                       profiles/proximity/reporter.c \
+                       profiles/proximity/linkloss.h \
+                       profiles/proximity/linkloss.c \
+                       profiles/proximity/immalert.h \
+                       profiles/proximity/immalert.c
+endif
+
+
+if TIZEN_TDS_PLUGIN
+builtin_modules += tds
+builtin_sources += profiles/tds/manager.c profiles/tds/tds.h \
+                       profiles/tds/tds.c
+endif
+
+builtin_modules += alert
+builtin_sources += profiles/alert/server.c
+
+builtin_modules += time
+builtin_sources += profiles/time/server.c
+
+builtin_modules += thermometer
+builtin_sources += profiles/thermometer/thermometer.c
+
+builtin_modules += heartrate
+builtin_sources += profiles/heartrate/heartrate.c
+
+builtin_modules += cyclingspeed
+builtin_sources += profiles/cyclingspeed/cyclingspeed.c
+endif
+endif
 
 if SIXAXIS
 plugin_LTLIBRARIES += plugins/sixaxis.la
 plugins_sixaxis_la_SOURCES = plugins/sixaxis.c
 plugins_sixaxis_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
-                                               -no-undefined @UDEV_LIBS@
-plugins_sixaxis_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @UDEV_CFLAGS@
+                                               -no-undefined @UDEV_LIBS@ @LIBXML_LIBS@ @INIPARSER_LIBS@
+plugins_sixaxis_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @UDEV_CFLAGS@ @LIBXML_CFLAGS@ @INIPARSER_CFLAGS@
 endif
index 42f17fc..fe6220f 100755 (executable)
@@ -169,7 +169,7 @@ tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
                                                tools/hciattach_sprd.c \
                                                tools/pskey_get.c \
                                                tools/hciattach_bcm43xx.c
-tools_hciattach_LDADD = lib/libbluetooth-internal.la
+tools_hciattach_LDADD = lib/libbluetooth-internal.la @LIBXML_LIBS@ @INIPARSER_LIBS@
 
 tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c
 tools_hciconfig_LDADD = lib/libbluetooth-internal.la
diff --git a/android/bas.c b/android/bas.c
new file mode 100755 (executable)
index 0000000..dcbf9de
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can rebastribute 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 bastributed 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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+
+#include "android/bas.h"
+
+#define ATT_NOTIFICATION_HEADER_SIZE 3
+#define ATT_READ_RESPONSE_HEADER_SIZE 1
+
+struct bt_bas {
+       int ref_count;
+       GAttrib *attrib;
+       struct gatt_primary *primary;
+       uint16_t handle;
+       uint16_t ccc_handle;
+       guint id;
+       struct queue *gatt_op;
+};
+
+struct gatt_request {
+       unsigned int id;
+       struct bt_bas *bas;
+       void *user_data;
+};
+
+static void destroy_gatt_req(struct gatt_request *req)
+{
+       queue_remove(req->bas->gatt_op, req);
+       bt_bas_unref(req->bas);
+       free(req);
+}
+
+static void bas_free(struct bt_bas *bas)
+{
+       bt_bas_detach(bas);
+
+       g_free(bas->primary);
+       queue_destroy(bas->gatt_op, (void *) destroy_gatt_req);
+       g_free(bas);
+}
+
+struct bt_bas *bt_bas_new(void *primary)
+{
+       struct bt_bas *bas;
+
+       bas = g_try_new0(struct bt_bas, 1);
+       if (!bas)
+               return NULL;
+
+       bas->gatt_op = queue_new();
+       if (!bas->gatt_op) {
+               bas_free(bas);
+               return NULL;
+       }
+
+       if (primary)
+               bas->primary = g_memdup(primary, sizeof(*bas->primary));
+
+       return bt_bas_ref(bas);
+}
+
+struct bt_bas *bt_bas_ref(struct bt_bas *bas)
+{
+       if (!bas)
+               return NULL;
+
+       __sync_fetch_and_add(&bas->ref_count, 1);
+
+       return bas;
+}
+
+void bt_bas_unref(struct bt_bas *bas)
+{
+       if (!bas)
+               return;
+
+       if (__sync_sub_and_fetch(&bas->ref_count, 1))
+               return;
+
+       bas_free(bas);
+}
+
+static struct gatt_request *create_request(struct bt_bas *bas,
+                                                       void *user_data)
+{
+       struct gatt_request *req;
+
+       req = new0(struct gatt_request, 1);
+       if (!req)
+               return NULL;
+
+       req->user_data = user_data;
+       req->bas = bt_bas_ref(bas);
+
+       return req;
+}
+
+static bool set_and_store_gatt_req(struct bt_bas *bas,
+                                               struct gatt_request *req,
+                                               unsigned int id)
+{
+       req->id = id;
+       return queue_push_head(bas->gatt_op, req);
+}
+
+static void write_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
+                                       const uint8_t *value, size_t vlen,
+                                       GAttribResultFunc func,
+                                       gpointer user_data)
+{
+       struct gatt_request *req;
+       unsigned int id;
+
+       req = create_request(bas, user_data);
+       if (!req)
+               return;
+
+       id = gatt_write_char(attrib, handle, value, vlen, func, req);
+
+       if (set_and_store_gatt_req(bas, req, id))
+               return;
+
+       error("bas: Could not write characteristic");
+       g_attrib_cancel(attrib, id);
+       free(req);
+
+}
+
+static void read_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
+                               GAttribResultFunc func, gpointer user_data)
+{
+       struct gatt_request *req;
+       unsigned int id;
+
+       req = create_request(bas, user_data);
+       if (!req)
+               return;
+
+       id = gatt_read_char(attrib, handle, func, req);
+
+       if (set_and_store_gatt_req(bas, req, id))
+               return;
+
+       error("bas: Could not read characteristic");
+       g_attrib_cancel(attrib, id);
+       free(req);
+}
+
+static void discover_char(struct bt_bas *bas, GAttrib *attrib,
+                                               uint16_t start, uint16_t end,
+                                               bt_uuid_t *uuid, gatt_cb_t func,
+                                               gpointer user_data)
+{
+       struct gatt_request *req;
+       unsigned int id;
+
+       req = create_request(bas, user_data);
+       if (!req)
+               return;
+
+       id = gatt_discover_char(attrib, start, end, uuid, func, req);
+
+       if (set_and_store_gatt_req(bas, req, id))
+               return;
+
+       error("bas: Could not discover characteristic");
+       g_attrib_cancel(attrib, id);
+       free(req);
+}
+
+static void discover_desc(struct bt_bas *bas, GAttrib *attrib,
+                               uint16_t start, uint16_t end, bt_uuid_t *uuid,
+                               gatt_cb_t func, gpointer user_data)
+{
+       struct gatt_request *req;
+       unsigned int id;
+
+       req = create_request(bas, user_data);
+       if (!req)
+               return;
+
+       id = gatt_discover_desc(attrib, start, end, uuid, func, req);
+       if (set_and_store_gatt_req(bas, req, id))
+               return;
+
+       error("bas: Could not discover descriptor");
+       g_attrib_cancel(attrib, id);
+       free(req);
+}
+
+static void notification_cb(const guint8 *pdu, guint16 len, gpointer user_data)
+{
+       DBG("Battery Level at %u", pdu[ATT_NOTIFICATION_HEADER_SIZE]);
+}
+
+static void read_value_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                       gpointer user_data)
+{
+       DBG("Battery Level at %u", pdu[ATT_READ_RESPONSE_HEADER_SIZE]);
+}
+
+static void ccc_written_cb(guint8 status, const guint8 *pdu,
+                                       guint16 plen, gpointer user_data)
+{
+       struct gatt_request *req = user_data;
+       struct bt_bas *bas = req->user_data;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("Write Scan Refresh CCC failed: %s",
+                                               att_ecode2str(status));
+               return;
+       }
+
+       DBG("Battery Level: notification enabled");
+
+       bas->id = g_attrib_register(bas->attrib, ATT_OP_HANDLE_NOTIFY,
+                                       bas->handle, notification_cb, bas,
+                                       NULL);
+}
+
+static void write_ccc(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
+                                                       void *user_data)
+{
+       uint8_t value[2];
+
+       put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+
+       write_char(bas, attrib, handle, value, sizeof(value), ccc_written_cb,
+                                                               user_data);
+}
+
+static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct gatt_request *req = user_data;
+       struct bt_bas *bas = req->user_data;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("Error reading CCC value: %s", att_ecode2str(status));
+               return;
+       }
+
+       write_ccc(bas, bas->attrib, bas->ccc_handle, bas);
+}
+
+static void discover_descriptor_cb(uint8_t status, GSList *descs,
+                                                               void *user_data)
+{
+       struct gatt_request *req = user_data;
+       struct bt_bas *bas = req->user_data;
+       struct gatt_desc *desc;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("Discover descriptors failed: %s", att_ecode2str(status));
+               return;
+       }
+
+       /* There will be only one descriptor on list and it will be CCC */
+       desc = descs->data;
+       bas->ccc_handle = desc->handle;
+
+       read_char(bas, bas->attrib, desc->handle, ccc_read_cb, bas);
+}
+
+static void bas_discovered_cb(uint8_t status, GSList *chars, void *user_data)
+{
+       struct gatt_request *req = user_data;
+       struct bt_bas *bas = req->user_data;
+       struct gatt_char *chr;
+       uint16_t start, end;
+       bt_uuid_t uuid;
+
+       destroy_gatt_req(req);
+
+       if (status) {
+               error("Battery: %s", att_ecode2str(status));
+               return;
+       }
+
+       chr = chars->data;
+       bas->handle = chr->value_handle;
+
+       DBG("Battery handle: 0x%04x", bas->handle);
+
+       read_char(bas, bas->attrib, bas->handle, read_value_cb, bas);
+
+       start = chr->value_handle + 1;
+       end = bas->primary->range.end;
+
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+       discover_desc(bas, bas->attrib, start, end, &uuid,
+                                               discover_descriptor_cb, bas);
+}
+
+bool bt_bas_attach(struct bt_bas *bas, void *attrib)
+{
+       if (!bas || bas->attrib || !bas->primary)
+               return false;
+
+       bas->attrib = g_attrib_ref(attrib);
+
+       if (bas->handle > 0)
+               return true;
+
+       discover_char(bas, bas->attrib, bas->primary->range.start,
+                                       bas->primary->range.end, NULL,
+                                       bas_discovered_cb, bas);
+
+       return true;
+}
+
+static void cancel_gatt_req(struct gatt_request *req)
+{
+       if (g_attrib_cancel(req->bas->attrib, req->id))
+               destroy_gatt_req(req);
+}
+
+void bt_bas_detach(struct bt_bas *bas)
+{
+       if (!bas || !bas->attrib)
+               return;
+
+       if (bas->id > 0) {
+               g_attrib_unregister(bas->attrib, bas->id);
+               bas->id = 0;
+       }
+
+       queue_foreach(bas->gatt_op, (void *) cancel_gatt_req, NULL);
+       g_attrib_unref(bas->attrib);
+       bas->attrib = NULL;
+}
diff --git a/android/bas.h b/android/bas.h
new file mode 100755 (executable)
index 0000000..3e175b5
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can rebastribute 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 bastributed 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
+ *
+ */
+
+struct bt_bas;
+
+struct bt_bas *bt_bas_new(void *primary);
+
+struct bt_bas *bt_bas_ref(struct bt_bas *bas);
+void bt_bas_unref(struct bt_bas *bas);
+
+bool bt_bas_attach(struct bt_bas *bas, void *gatt);
+void bt_bas_detach(struct bt_bas *bas);
diff --git a/android/dis.c b/android/dis.c
new file mode 100755 (executable)
index 0000000..75dbe3d
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ *  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 <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+
+#include "android/dis.h"
+
+#define PNP_ID_SIZE    7
+
+struct bt_dis {
+       int                     ref_count;
+       uint16_t                handle;
+       uint8_t                 source;
+       uint16_t                vendor;
+       uint16_t                product;
+       uint16_t                version;
+       GAttrib                 *attrib;        /* GATT connection */
+       struct gatt_primary     *primary;       /* Primary details */
+       bt_dis_notify           notify;
+       void                    *notify_data;
+       struct queue            *gatt_op;
+};
+
+struct characteristic {
+       struct gatt_char        attr;   /* Characteristic */
+       struct bt_dis           *d;     /* deviceinfo where the char belongs */
+};
+
+struct gatt_request {
+       unsigned int id;
+       struct bt_dis *dis;
+       void *user_data;
+};
+
+static void destroy_gatt_req(struct gatt_request *req)
+{
+       queue_remove(req->dis->gatt_op, req);
+       bt_dis_unref(req->dis);
+       free(req);
+}
+
+static void dis_free(struct bt_dis *dis)
+{
+       bt_dis_detach(dis);
+
+       g_free(dis->primary);
+       queue_destroy(dis->gatt_op, (void *) destroy_gatt_req);
+       g_free(dis);
+}
+
+struct bt_dis *bt_dis_new(void *primary)
+{
+       struct bt_dis *dis;
+
+       dis = g_try_new0(struct bt_dis, 1);
+       if (!dis)
+               return NULL;
+
+       dis->gatt_op = queue_new();
+       if (!dis->gatt_op) {
+               dis_free(dis);
+               return NULL;
+       }
+
+       if (primary)
+               dis->primary = g_memdup(primary, sizeof(*dis->primary));
+
+       return bt_dis_ref(dis);
+}
+
+struct bt_dis *bt_dis_ref(struct bt_dis *dis)
+{
+       if (!dis)
+               return NULL;
+
+       __sync_fetch_and_add(&dis->ref_count, 1);
+
+       return dis;
+}
+
+void bt_dis_unref(struct bt_dis *dis)
+{
+       if (!dis)
+               return;
+
+       if (__sync_sub_and_fetch(&dis->ref_count, 1))
+               return;
+
+       dis_free(dis);
+}
+
+static struct gatt_request *create_request(struct bt_dis *dis,
+                                                       void *user_data)
+{
+       struct gatt_request *req;
+
+       req = new0(struct gatt_request, 1);
+       if (!req)
+               return NULL;
+
+       req->user_data = user_data;
+       req->dis = bt_dis_ref(dis);
+
+       return req;
+}
+
+static bool set_and_store_gatt_req(struct bt_dis *dis,
+                                               struct gatt_request *req,
+                                               unsigned int id)
+{
+       req->id = id;
+       return queue_push_head(dis->gatt_op, req);
+}
+
+static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct gatt_request *req = user_data;
+       struct bt_dis *dis = req->user_data;
+       uint8_t value[PNP_ID_SIZE];
+       ssize_t vlen;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("Error reading PNP_ID value: %s", att_ecode2str(status));
+               return;
+       }
+
+       vlen = dec_read_resp(pdu, len, value, sizeof(value));
+       if (vlen < 0) {
+               error("Error reading PNP_ID: Protocol error");
+               return;
+       }
+
+       if (vlen < 7) {
+               error("Error reading PNP_ID: Invalid pdu length received");
+               return;
+       }
+
+       dis->source = value[0];
+       dis->vendor = get_le16(&value[1]);
+       dis->product = get_le16(&value[3]);
+       dis->version = get_le16(&value[5]);
+
+       DBG("source: 0x%02X vendor: 0x%04X product: 0x%04X version: 0x%04X",
+                       dis->source, dis->vendor, dis->product, dis->version);
+
+       if (dis->notify)
+               dis->notify(dis->source, dis->vendor, dis->product,
+                                               dis->version, dis->notify_data);
+}
+
+static void read_char(struct bt_dis *dis, GAttrib *attrib, uint16_t handle,
+                               GAttribResultFunc func, gpointer user_data)
+{
+       struct gatt_request *req;
+       unsigned int id;
+
+       req = create_request(dis, user_data);
+       if (!req)
+               return;
+
+       id = gatt_read_char(attrib, handle, func, req);
+
+       if (set_and_store_gatt_req(dis, req, id))
+               return;
+
+       error("dis: Could not read characteristic");
+       g_attrib_cancel(attrib, id);
+       free(req);
+}
+
+static void discover_char(struct bt_dis *dis, GAttrib *attrib,
+                                               uint16_t start, uint16_t end,
+                                               bt_uuid_t *uuid, gatt_cb_t func,
+                                               gpointer user_data)
+{
+       struct gatt_request *req;
+       unsigned int id;
+
+       req = create_request(dis, user_data);
+       if (!req)
+               return;
+
+       id = gatt_discover_char(attrib, start, end, uuid, func, req);
+
+       if (set_and_store_gatt_req(dis, req, id))
+               return;
+
+       error("dis: Could not send discover characteristic");
+       g_attrib_cancel(attrib, id);
+       free(req);
+}
+
+static void configure_deviceinfo_cb(uint8_t status, GSList *characteristics,
+                                                               void *user_data)
+{
+       struct gatt_request *req = user_data;
+       struct bt_dis *d = req->user_data;
+       GSList *l;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("Discover deviceinfo characteristics: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       for (l = characteristics; l; l = l->next) {
+               struct gatt_char *c = l->data;
+
+               if (strcmp(c->uuid, PNPID_UUID) == 0) {
+                       d->handle = c->value_handle;
+                       read_char(d, d->attrib, d->handle, read_pnpid_cb, d);
+                       break;
+               }
+       }
+}
+
+bool bt_dis_attach(struct bt_dis *dis, void *attrib)
+{
+       struct gatt_primary *primary = dis->primary;
+
+       if (dis->attrib || !primary)
+               return false;
+
+       dis->attrib = g_attrib_ref(attrib);
+
+       if (!dis->handle)
+               discover_char(dis, dis->attrib, primary->range.start,
+                                               primary->range.end, NULL,
+                                               configure_deviceinfo_cb, dis);
+
+       return true;
+}
+
+static void cancel_gatt_req(struct gatt_request *req)
+{
+       if (g_attrib_cancel(req->dis->attrib, req->id))
+               destroy_gatt_req(req);
+}
+
+void bt_dis_detach(struct bt_dis *dis)
+{
+       if (!dis->attrib)
+               return;
+
+       queue_foreach(dis->gatt_op, (void *) cancel_gatt_req, NULL);
+       g_attrib_unref(dis->attrib);
+       dis->attrib = NULL;
+}
+
+bool bt_dis_set_notification(struct bt_dis *dis, bt_dis_notify func,
+                                                       void *user_data)
+{
+       if (!dis)
+               return false;
+
+       dis->notify = func;
+       dis->notify_data = user_data;
+
+       return true;
+}
diff --git a/android/dis.h b/android/dis.h
new file mode 100755 (executable)
index 0000000..faf27b3
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  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
+ *
+ */
+
+struct bt_dis;
+
+struct bt_dis *bt_dis_new(void *primary);
+
+struct bt_dis *bt_dis_ref(struct bt_dis *dis);
+void bt_dis_unref(struct bt_dis *dis);
+
+bool bt_dis_attach(struct bt_dis *dis, void *gatt);
+void bt_dis_detach(struct bt_dis *dis);
+
+typedef void (*bt_dis_notify) (uint8_t source, uint16_t vendor,
+                                       uint16_t product, uint16_t version,
+                                       void *user_data);
+
+bool bt_dis_set_notification(struct bt_dis *dis, bt_dis_notify func,
+                                                       void *user_data);
diff --git a/android/hog.c b/android/hog.c
new file mode 100755 (executable)
index 0000000..ff77bb3
--- /dev/null
@@ -0,0 +1,1511 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation.
+ *  Copyright (C) 2012  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2012  Nordic Semiconductor Inc.
+ *  Copyright (C) 2012  Instituto Nokia de Tecnologia - INdT
+ *
+ *
+ *  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 <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/shared/util.h"
+#include "src/shared/uhid.h"
+#include "src/shared/queue.h"
+#include "src/log.h"
+
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt.h"
+
+#include "btio/btio.h"
+
+#include "android/scpp.h"
+#include "android/dis.h"
+#include "android/bas.h"
+#include "android/hog.h"
+
+#define HOG_UUID               "00001812-0000-1000-8000-00805f9b34fb"
+
+#define HOG_INFO_UUID          0x2A4A
+#define HOG_REPORT_MAP_UUID    0x2A4B
+#define HOG_REPORT_UUID                0x2A4D
+#define HOG_PROTO_MODE_UUID    0x2A4E
+#define HOG_CONTROL_POINT_UUID 0x2A4C
+
+#define HOG_REPORT_TYPE_INPUT  1
+#define HOG_REPORT_TYPE_OUTPUT 2
+#define HOG_REPORT_TYPE_FEATURE        3
+
+#define HOG_PROTO_MODE_BOOT    0
+#define HOG_PROTO_MODE_REPORT  1
+
+#define HOG_REPORT_MAP_MAX_SIZE        512
+#define HID_INFO_SIZE                  4
+#define ATT_NOTIFICATION_HEADER_SIZE   3
+
+struct bt_hog {
+       int                     ref_count;
+       char                    *name;
+       uint16_t                vendor;
+       uint16_t                product;
+       uint16_t                version;
+       struct gatt_primary     *primary;
+       GAttrib                 *attrib;
+       GSList                  *reports;
+       struct bt_uhid          *uhid;
+       int                     uhid_fd;
+       gboolean                has_report_id;
+       uint16_t                bcdhid;
+       uint8_t                 bcountrycode;
+       uint16_t                proto_mode_handle;
+       uint16_t                ctrlpt_handle;
+       uint8_t                 flags;
+       unsigned int            getrep_att;
+       uint16_t                getrep_id;
+       unsigned int            setrep_att;
+       uint16_t                setrep_id;
+       struct bt_scpp          *scpp;
+       struct bt_dis           *dis;
+       struct queue            *bas;
+       GSList                  *instances;
+       struct queue            *gatt_op;
+};
+
+struct report {
+       struct bt_hog           *hog;
+       uint8_t                 id;
+       uint8_t                 type;
+       uint16_t                ccc_handle;
+       guint                   notifyid;
+       struct gatt_char        *decl;
+       uint16_t                len;
+       uint8_t                 *value;
+};
+
+struct gatt_request {
+       unsigned int id;
+       struct bt_hog *hog;
+       void *user_data;
+};
+
+static struct gatt_request *create_request(struct bt_hog *hog,
+                                                       void *user_data)
+{
+       struct gatt_request *req;
+
+       req = new0(struct gatt_request, 1);
+       if (!req)
+               return NULL;
+
+       req->user_data = user_data;
+       req->hog = bt_hog_ref(hog);
+
+       return req;
+}
+
+static bool set_and_store_gatt_req(struct bt_hog *hog,
+                                               struct gatt_request *req,
+                                               unsigned int id)
+{
+       req->id = id;
+       return queue_push_head(hog->gatt_op, req);
+}
+
+static void destroy_gatt_req(struct gatt_request *req)
+{
+       queue_remove(req->hog->gatt_op, req);
+       bt_hog_unref(req->hog);
+       free(req);
+}
+
+static void write_char(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
+                                       const uint8_t *value, size_t vlen,
+                                       GAttribResultFunc func,
+                                       gpointer user_data)
+{
+       struct gatt_request *req;
+       unsigned int id;
+
+       req = create_request(hog, user_data);
+       if (!req)
+               return;
+
+       id = gatt_write_char(attrib, handle, value, vlen, func, req);
+
+       if (set_and_store_gatt_req(hog, req, id))
+               return;
+
+       error("hog: Could not read char");
+       g_attrib_cancel(attrib, id);
+       free(req);
+}
+
+static void read_char(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
+                               GAttribResultFunc func, gpointer user_data)
+{
+       struct gatt_request *req;
+       unsigned int id;
+
+       req = create_request(hog, user_data);
+       if (!req)
+               return;
+
+       id = gatt_read_char(attrib, handle, func, req);
+
+       if (set_and_store_gatt_req(hog, req, id))
+               return;
+
+       error("hog: Could not read char");
+       g_attrib_cancel(attrib, id);
+       free(req);
+}
+
+static void discover_desc(struct bt_hog *hog, GAttrib *attrib,
+                               uint16_t start, uint16_t end, gatt_cb_t func,
+                               gpointer user_data)
+{
+       struct gatt_request *req;
+       unsigned int id;
+
+       req = create_request(hog, user_data);
+       if (!req)
+               return;
+
+       id = gatt_discover_desc(attrib, start, end, NULL, func, req);
+       if (set_and_store_gatt_req(hog, req, id))
+               return;
+
+       error("hog: Could not discover descriptors");
+       g_attrib_cancel(attrib, id);
+       free(req);
+}
+
+static void discover_char(struct bt_hog *hog, GAttrib *attrib,
+                                               uint16_t start, uint16_t end,
+                                               bt_uuid_t *uuid, gatt_cb_t func,
+                                               gpointer user_data)
+{
+       struct gatt_request *req;
+       unsigned int id;
+
+       req = create_request(hog, user_data);
+       if (!req)
+               return;
+
+       id = gatt_discover_char(attrib, start, end, uuid, func, req);
+
+       if (set_and_store_gatt_req(hog, req, id))
+               return;
+
+       error("hog: Could not discover characteristic");
+       g_attrib_cancel(attrib, id);
+       free(req);
+}
+
+static void discover_primary(struct bt_hog *hog, GAttrib *attrib,
+                                               bt_uuid_t *uuid, gatt_cb_t func,
+                                               gpointer user_data)
+{
+       struct gatt_request *req;
+       unsigned int id;
+
+       req = create_request(hog, user_data);
+       if (!req)
+               return;
+
+       id = gatt_discover_primary(attrib, uuid, func, req);
+
+       if (set_and_store_gatt_req(hog, req, id))
+               return;
+
+       error("hog: Could not send discover primary");
+       g_attrib_cancel(attrib, id);
+       free(req);
+}
+
+static void find_included(struct bt_hog *hog, GAttrib *attrib,
+                                       uint16_t start, uint16_t end,
+                                       gatt_cb_t func, gpointer user_data)
+{
+       struct gatt_request *req;
+       unsigned int id;
+
+       req = create_request(hog, user_data);
+       if (!req)
+               return;
+
+       id = gatt_find_included(attrib, start, end, func, req);
+
+       if (set_and_store_gatt_req(hog, req, id))
+               return;
+
+       error("Could not find included");
+       g_attrib_cancel(attrib, id);
+       free(req);
+}
+
+static void report_value_cb(const guint8 *pdu, guint16 len, gpointer user_data)
+{
+       struct report *report = user_data;
+       struct bt_hog *hog = report->hog;
+       struct uhid_event ev;
+       uint8_t *buf;
+       int err;
+
+       if (len < ATT_NOTIFICATION_HEADER_SIZE) {
+               error("Malformed ATT notification");
+               return;
+       }
+
+       pdu += ATT_NOTIFICATION_HEADER_SIZE;
+       len -= ATT_NOTIFICATION_HEADER_SIZE;
+
+       memset(&ev, 0, sizeof(ev));
+       ev.type = UHID_INPUT;
+       buf = ev.u.input.data;
+
+       if (hog->has_report_id) {
+               buf[0] = report->id;
+               len = MIN(len, sizeof(ev.u.input.data) - 1);
+               memcpy(buf + 1, pdu, len);
+               ev.u.input.size = ++len;
+       } else {
+               len = MIN(len, sizeof(ev.u.input.data));
+               memcpy(buf, pdu, len);
+               ev.u.input.size = len;
+       }
+
+       err = bt_uhid_send(hog->uhid, &ev);
+       if (err < 0) {
+               error("bt_uhid_send: %s (%d)", strerror(-err), -err);
+               return;
+       }
+
+       DBG("HoG report (%u bytes)", ev.u.input.size);
+}
+
+static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
+                                       guint16 plen, gpointer user_data)
+{
+       struct gatt_request *req = user_data;
+       struct report *report = req->user_data;
+       struct bt_hog *hog = report->hog;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("Write report characteristic descriptor failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       report->notifyid = g_attrib_register(hog->attrib,
+                                       ATT_OP_HANDLE_NOTIFY,
+                                       report->decl->value_handle,
+                                       report_value_cb, report, NULL);
+
+       DBG("Report characteristic descriptor written: notifications enabled");
+}
+
+static void write_ccc(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
+                                                       void *user_data)
+{
+       uint8_t value[2];
+
+       put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+
+       write_char(hog, attrib, handle, value, sizeof(value),
+                                       report_ccc_written_cb, user_data);
+}
+
+static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct gatt_request *req = user_data;
+       struct report *report = req->user_data;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("Error reading CCC value: %s", att_ecode2str(status));
+               return;
+       }
+
+       write_ccc(report->hog, report->hog->attrib, report->ccc_handle, report);
+}
+
+static void report_reference_cb(guint8 status, const guint8 *pdu,
+                                       guint16 plen, gpointer user_data)
+{
+       struct gatt_request *req = user_data;
+       struct report *report = req->user_data;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("Read Report Reference descriptor failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       if (plen != 3) {
+               error("Malformed ATT read response");
+               return;
+       }
+
+       report->id = pdu[1];
+       report->type = pdu[2];
+       DBG("Report ID: 0x%02x Report type: 0x%02x", pdu[1], pdu[2]);
+
+       /* Enable notifications only for Input Reports */
+       if (report->type == HOG_REPORT_TYPE_INPUT)
+               read_char(report->hog, report->hog->attrib, report->ccc_handle,
+                                                       ccc_read_cb, report);
+}
+
+static void external_report_reference_cb(guint8 status, const guint8 *pdu,
+                                       guint16 plen, gpointer user_data);
+
+static void discover_external_cb(uint8_t status, GSList *descs, void *user_data)
+{
+       struct gatt_request *req = user_data;
+       struct bt_hog *hog = req->user_data;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("Discover external descriptors failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       for ( ; descs; descs = descs->next) {
+               struct gatt_desc *desc = descs->data;
+
+               read_char(hog, hog->attrib, desc->handle,
+                                               external_report_reference_cb,
+                                               hog);
+       }
+}
+
+static void discover_external(struct bt_hog *hog, GAttrib *attrib,
+                                               uint16_t start, uint16_t end,
+                                               gpointer user_data)
+{
+       bt_uuid_t uuid;
+
+       if (start > end)
+               return;
+
+       bt_uuid16_create(&uuid, GATT_EXTERNAL_REPORT_REFERENCE);
+
+       discover_desc(hog, attrib, start, end, discover_external_cb,
+                                                               user_data);
+}
+
+static void discover_report_cb(uint8_t status, GSList *descs, void *user_data)
+{
+       struct gatt_request *req = user_data;
+       struct report *report = req->user_data;
+       struct bt_hog *hog = report->hog;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("Discover report descriptors failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       for ( ; descs; descs = descs->next) {
+               struct gatt_desc *desc = descs->data;
+
+               switch (desc->uuid16) {
+               case GATT_CLIENT_CHARAC_CFG_UUID:
+                       report->ccc_handle = desc->handle;
+                       break;
+               case GATT_REPORT_REFERENCE:
+                       read_char(hog, hog->attrib, desc->handle,
+                                               report_reference_cb, report);
+                       break;
+               }
+       }
+}
+
+static void discover_report(struct bt_hog *hog, GAttrib *attrib,
+                                               uint16_t start, uint16_t end,
+                                                       gpointer user_data)
+{
+       if (start > end)
+               return;
+
+       discover_desc(hog, attrib, start, end, discover_report_cb, user_data);
+}
+
+static void report_read_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct gatt_request *req = user_data;
+       struct report *report = req->user_data;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("Error reading Report value: %s", att_ecode2str(status));
+               return;
+       }
+
+       if (report->value)
+               g_free(report->value);
+
+       report->value = g_memdup(pdu, len);
+       report->len = len;
+}
+
+static struct report *report_new(struct bt_hog *hog, struct gatt_char *chr)
+{
+       struct report *report;
+
+       report = g_new0(struct report, 1);
+       report->hog = hog;
+       report->decl = g_memdup(chr, sizeof(*chr));
+       hog->reports = g_slist_append(hog->reports, report);
+
+       read_char(hog, hog->attrib, chr->value_handle, report_read_cb, report);
+
+       return report;
+}
+
+static void external_service_char_cb(uint8_t status, GSList *chars,
+                                                               void *user_data)
+{
+       struct gatt_request *req = user_data;
+       struct bt_hog *hog = req->user_data;
+       struct gatt_primary *primary = hog->primary;
+       struct report *report;
+       GSList *l;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               const char *str = att_ecode2str(status);
+               DBG("Discover external service characteristic failed: %s", str);
+               return;
+       }
+
+       for (l = chars; l; l = g_slist_next(l)) {
+               struct gatt_char *chr, *next;
+               uint16_t start, end;
+
+               chr = l->data;
+               next = l->next ? l->next->data : NULL;
+
+               DBG("0x%04x UUID: %s properties: %02x",
+                               chr->handle, chr->uuid, chr->properties);
+
+               report = report_new(hog, chr);
+               start = chr->value_handle + 1;
+               end = (next ? next->handle - 1 : primary->range.end);
+               discover_report(hog, hog->attrib, start, end, report);
+       }
+}
+
+static void external_report_reference_cb(guint8 status, const guint8 *pdu,
+                                       guint16 plen, gpointer user_data)
+{
+       struct gatt_request *req = user_data;
+       struct bt_hog *hog = req->user_data;
+       uint16_t uuid16;
+       bt_uuid_t uuid;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("Read External Report Reference descriptor failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       if (plen != 3) {
+               error("Malformed ATT read response");
+               return;
+       }
+
+       uuid16 = get_le16(&pdu[1]);
+       DBG("External report reference read, external report characteristic "
+                                               "UUID: 0x%04x", uuid16);
+
+       /* Do not discover if is not a Report */
+       if (uuid16 != HOG_REPORT_UUID)
+               return;
+
+       bt_uuid16_create(&uuid, uuid16);
+       discover_char(hog, hog->attrib, 0x0001, 0xffff, &uuid,
+                                       external_service_char_cb, hog);
+}
+
+static int report_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct report *ra = a, *rb = b;
+
+       /* sort by type first.. */
+       if (ra->type != rb->type)
+               return ra->type - rb->type;
+
+       /* skip id check in case of report id 0 */
+       if (!rb->id)
+               return 0;
+
+       /* ..then by id */
+       return ra->id - rb->id;
+}
+
+static struct report *find_report(struct bt_hog *hog, uint8_t type, uint8_t id)
+{
+       struct report cmp;
+       GSList *l;
+
+       cmp.type = type;
+       cmp.id = hog->has_report_id ? id : 0;
+
+       l = g_slist_find_custom(hog->reports, &cmp, report_cmp);
+
+       return l ? l->data : NULL;
+}
+
+static struct report *find_report_by_rtype(struct bt_hog *hog, uint8_t rtype,
+                                                               uint8_t id)
+{
+       uint8_t type;
+
+       switch (rtype) {
+       case UHID_FEATURE_REPORT:
+               type = HOG_REPORT_TYPE_FEATURE;
+               break;
+       case UHID_OUTPUT_REPORT:
+               type = HOG_REPORT_TYPE_OUTPUT;
+               break;
+       case UHID_INPUT_REPORT:
+               type = HOG_REPORT_TYPE_INPUT;
+               break;
+       default:
+               return NULL;
+       }
+
+       return find_report(hog, type, id);
+}
+
+static void output_written_cb(guint8 status, const guint8 *pdu,
+                                       guint16 plen, gpointer user_data)
+{
+       struct gatt_request *req = user_data;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("Write output report failed: %s", att_ecode2str(status));
+               return;
+       }
+}
+
+static void forward_report(struct uhid_event *ev, void *user_data)
+{
+       struct bt_hog *hog = user_data;
+       struct report *report;
+       void *data;
+       int size;
+
+       report = find_report_by_rtype(hog, ev->u.output.rtype,
+                                                       ev->u.output.data[0]);
+       if (!report)
+               return;
+
+       data = ev->u.output.data;
+       size = ev->u.output.size;
+       if (hog->has_report_id && size > 0) {
+               data++;
+               --size;
+       }
+
+       DBG("Sending report type %d ID %d to handle 0x%X", report->type,
+                               report->id, report->decl->value_handle);
+
+       if (hog->attrib == NULL)
+               return;
+
+       if (report->decl->properties & GATT_CHR_PROP_WRITE)
+               write_char(hog, hog->attrib, report->decl->value_handle,
+                               data, size, output_written_cb, hog);
+       else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
+               gatt_write_cmd(hog->attrib, report->decl->value_handle,
+                                               data, size, NULL, NULL);
+}
+
+static void get_feature(struct uhid_event *ev, void *user_data)
+{
+       struct bt_hog *hog = user_data;
+       struct report *report;
+       struct uhid_event rsp;
+       int err;
+
+       memset(&rsp, 0, sizeof(rsp));
+       rsp.type = UHID_FEATURE_ANSWER;
+       rsp.u.feature_answer.id = ev->u.feature.id;
+
+       report = find_report_by_rtype(hog, ev->u.feature.rtype,
+                                                       ev->u.feature.rnum);
+       if (!report) {
+               rsp.u.feature_answer.err = ENOTSUP;
+               goto done;
+       }
+
+       if (!report->value) {
+               rsp.u.feature_answer.err = EIO;
+               goto done;
+       }
+
+       rsp.u.feature_answer.size = report->len;
+       memcpy(rsp.u.feature_answer.data, report->value, report->len);
+
+done:
+       err = bt_uhid_send(hog->uhid, &rsp);
+       if (err < 0)
+               error("bt_uhid_send: %s", strerror(-err));
+}
+
+static void set_report_cb(guint8 status, const guint8 *pdu,
+                                       guint16 plen, gpointer user_data)
+{
+       struct bt_hog *hog = user_data;
+       struct uhid_event rsp;
+       int err;
+
+       hog->setrep_att = 0;
+
+       memset(&rsp, 0, sizeof(rsp));
+       rsp.type = UHID_SET_REPORT_REPLY;
+       rsp.u.set_report_reply.id = hog->setrep_id;
+       rsp.u.set_report_reply.err = status;
+
+       if (status != 0)
+               error("Error setting Report value: %s", att_ecode2str(status));
+
+       err = bt_uhid_send(hog->uhid, &rsp);
+       if (err < 0)
+               error("bt_uhid_send: %s", strerror(-err));
+}
+
+static void set_report(struct uhid_event *ev, void *user_data)
+{
+       struct bt_hog *hog = user_data;
+       struct report *report;
+       void *data;
+       int size;
+       int err;
+
+       /* uhid never sends reqs in parallel; if there's a req, it timed out */
+       if (hog->setrep_att) {
+               g_attrib_cancel(hog->attrib, hog->setrep_att);
+               hog->setrep_att = 0;
+       }
+
+       hog->setrep_id = ev->u.set_report.id;
+
+       report = find_report_by_rtype(hog, ev->u.set_report.rtype,
+                                                       ev->u.set_report.rnum);
+       if (!report) {
+               err = ENOTSUP;
+               goto fail;
+       }
+
+       data = ev->u.set_report.data;
+       size = ev->u.set_report.size;
+       if (hog->has_report_id && size > 0) {
+               data++;
+               --size;
+       }
+
+       DBG("Sending report type %d ID %d to handle 0x%X", report->type,
+                               report->id, report->decl->value_handle);
+
+       if (hog->attrib == NULL)
+               return;
+
+       hog->setrep_att = gatt_write_char(hog->attrib,
+                                               report->decl->value_handle,
+                                               data, size, set_report_cb,
+                                               hog);
+       if (!hog->setrep_att) {
+               err = ENOMEM;
+               goto fail;
+       }
+
+       return;
+fail:
+       /* cancel the request on failure */
+       set_report_cb(err, NULL, 0, hog);
+}
+
+static void get_report_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct bt_hog *hog = user_data;
+       struct uhid_event rsp;
+       int err;
+
+       hog->getrep_att = 0;
+
+       memset(&rsp, 0, sizeof(rsp));
+       rsp.type = UHID_GET_REPORT_REPLY;
+       rsp.u.get_report_reply.id = hog->getrep_id;
+
+       if (status != 0) {
+               error("Error reading Report value: %s", att_ecode2str(status));
+               goto exit;
+       }
+
+       if (len == 0) {
+               error("Error reading Report, length %d", len);
+               status = EIO;
+               goto exit;
+       }
+
+       if (pdu[0] != 0x0b) {
+               error("Error reading Report, invalid response: %02x", pdu[0]);
+               status = EPROTO;
+               goto exit;
+       }
+
+       --len;
+       ++pdu;
+       if (hog->has_report_id && len > 0) {
+               --len;
+               ++pdu;
+       }
+
+       rsp.u.get_report_reply.size = len;
+       memcpy(rsp.u.get_report_reply.data, pdu, len);
+
+exit:
+       rsp.u.get_report_reply.err = status;
+       err = bt_uhid_send(hog->uhid, &rsp);
+       if (err < 0)
+               error("bt_uhid_send: %s", strerror(-err));
+}
+
+static void get_report(struct uhid_event *ev, void *user_data)
+{
+       struct bt_hog *hog = user_data;
+       struct report *report;
+       guint8 err;
+
+       /* uhid never sends reqs in parallel; if there's a req, it timed out */
+       if (hog->getrep_att) {
+               g_attrib_cancel(hog->attrib, hog->getrep_att);
+               hog->getrep_att = 0;
+       }
+
+       hog->getrep_id = ev->u.get_report.id;
+
+       report = find_report_by_rtype(hog, ev->u.get_report.rtype,
+                                                       ev->u.get_report.rnum);
+       if (!report) {
+               err = ENOTSUP;
+               goto fail;
+       }
+
+       hog->getrep_att = gatt_read_char(hog->attrib,
+                                               report->decl->value_handle,
+                                               get_report_cb, hog);
+       if (!hog->getrep_att) {
+               err = ENOMEM;
+               goto fail;
+       }
+
+       return;
+
+fail:
+       /* cancel the request on failure */
+       get_report_cb(err, NULL, 0, hog);
+}
+
+static bool get_descriptor_item_info(uint8_t *buf, ssize_t blen, ssize_t *len,
+                                                               bool *is_long)
+{
+       if (!blen)
+               return false;
+
+       *is_long = (buf[0] == 0xfe);
+
+       if (*is_long) {
+               if (blen < 3)
+                       return false;
+
+               /*
+                * long item:
+                * byte 0 -> 0xFE
+                * byte 1 -> data size
+                * byte 2 -> tag
+                * + data
+                */
+
+               *len = buf[1] + 3;
+       } else {
+               uint8_t b_size;
+
+               /*
+                * short item:
+                * byte 0[1..0] -> data size (=0, 1, 2, 4)
+                * byte 0[3..2] -> type
+                * byte 0[7..4] -> tag
+                * + data
+                */
+
+               b_size = buf[0] & 0x03;
+               *len = (b_size ? 1 << (b_size - 1) : 0) + 1;
+       }
+
+       /* item length should be no more than input buffer length */
+       return *len <= blen;
+}
+
+static char *item2string(char *str, uint8_t *buf, uint8_t len)
+{
+       char *p = str;
+       int i;
+
+       /*
+        * Since long item tags are not defined except for vendor ones, we
+        * just ensure that short items are printed properly (up to 5 bytes).
+        */
+       for (i = 0; i < 6 && i < len; i++)
+               p += sprintf(p, " %02x", buf[i]);
+
+       /*
+        * If there are some data left, just add continuation mark to indicate
+        * this.
+        */
+       if (i < len)
+               sprintf(p, " ...");
+
+       return str;
+}
+
+static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       struct gatt_request *req = user_data;
+       struct bt_hog *hog = req->user_data;
+       uint8_t value[HOG_REPORT_MAP_MAX_SIZE];
+       struct uhid_event ev;
+       ssize_t vlen;
+       char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */
+       int i, err;
+       GError *gerr = NULL;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("Report Map read failed: %s", att_ecode2str(status));
+               return;
+       }
+
+       vlen = dec_read_resp(pdu, plen, value, sizeof(value));
+       if (vlen < 0) {
+               error("ATT protocol error");
+               return;
+       }
+
+       DBG("Report MAP:");
+       for (i = 0; i < vlen;) {
+               ssize_t ilen = 0;
+               bool long_item = false;
+
+               if (get_descriptor_item_info(&value[i], vlen - i, &ilen,
+                                                               &long_item)) {
+                       /* Report ID is short item with prefix 100001xx */
+                       if (!long_item && (value[i] & 0xfc) == 0x84)
+                               hog->has_report_id = TRUE;
+
+                       DBG("\t%s", item2string(itemstr, &value[i], ilen));
+
+                       i += ilen;
+               } else {
+                       error("Report Map parsing failed at %d", i);
+
+                       /* Just print remaining items at once and break */
+                       DBG("\t%s", item2string(itemstr, &value[i], vlen - i));
+                       break;
+               }
+       }
+
+       /* create uHID device */
+       memset(&ev, 0, sizeof(ev));
+       ev.type = UHID_CREATE;
+
+       bt_io_get(g_attrib_get_channel(hog->attrib), &gerr,
+                       BT_IO_OPT_SOURCE, ev.u.create.phys,
+                       BT_IO_OPT_DEST, ev.u.create.uniq,
+                       BT_IO_OPT_INVALID);
+       if (gerr) {
+               error("Failed to connection details: %s", gerr->message);
+               g_error_free(gerr);
+               return;
+       }
+
+       strcpy((char *) ev.u.create.name, hog->name);
+       ev.u.create.vendor = hog->vendor;
+       ev.u.create.product = hog->product;
+       ev.u.create.version = hog->version;
+       ev.u.create.country = hog->bcountrycode;
+       ev.u.create.bus = BUS_BLUETOOTH;
+       ev.u.create.rd_data = value;
+       ev.u.create.rd_size = vlen;
+
+       err = bt_uhid_send(hog->uhid, &ev);
+       if (err < 0) {
+               error("bt_uhid_send: %s", strerror(-err));
+               return;
+       }
+
+       bt_uhid_register(hog->uhid, UHID_OUTPUT, forward_report, hog);
+       bt_uhid_register(hog->uhid, UHID_FEATURE, get_feature, hog);
+       bt_uhid_register(hog->uhid, UHID_GET_REPORT, get_report, hog);
+       bt_uhid_register(hog->uhid, UHID_SET_REPORT, set_report, hog);
+}
+
+static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       struct gatt_request *req = user_data;
+       struct bt_hog *hog = req->user_data;
+       uint8_t value[HID_INFO_SIZE];
+       ssize_t vlen;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("HID Information read failed: %s",
+                                               att_ecode2str(status));
+               return;
+       }
+
+       vlen = dec_read_resp(pdu, plen, value, sizeof(value));
+       if (vlen != 4) {
+               error("ATT protocol error");
+               return;
+       }
+
+       hog->bcdhid = get_le16(&value[0]);
+       hog->bcountrycode = value[2];
+       hog->flags = value[3];
+
+       DBG("bcdHID: 0x%04X bCountryCode: 0x%02X Flags: 0x%02X",
+                       hog->bcdhid, hog->bcountrycode, hog->flags);
+}
+
+static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       struct gatt_request *req = user_data;
+       struct bt_hog *hog = req->user_data;
+       uint8_t value;
+       ssize_t vlen;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               error("Protocol Mode characteristic read failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       vlen = dec_read_resp(pdu, plen, &value, sizeof(value));
+       if (vlen < 0) {
+               error("ATT protocol error");
+               return;
+       }
+
+       if (value == HOG_PROTO_MODE_BOOT) {
+               uint8_t nval = HOG_PROTO_MODE_REPORT;
+
+               DBG("HoG is operating in Boot Procotol Mode");
+
+               gatt_write_cmd(hog->attrib, hog->proto_mode_handle, &nval,
+                                               sizeof(nval), NULL, NULL);
+       } else if (value == HOG_PROTO_MODE_REPORT)
+               DBG("HoG is operating in Report Protocol Mode");
+}
+
+static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data)
+{
+       struct gatt_request *req = user_data;
+       struct bt_hog *hog = req->user_data;
+       struct gatt_primary *primary = hog->primary;
+       bt_uuid_t report_uuid, report_map_uuid, info_uuid;
+       bt_uuid_t proto_mode_uuid, ctrlpt_uuid;
+       struct report *report;
+       GSList *l;
+       uint16_t info_handle = 0, proto_mode_handle = 0;
+
+       destroy_gatt_req(req);
+
+       if (status != 0) {
+               const char *str = att_ecode2str(status);
+               DBG("Discover all characteristics failed: %s", str);
+               return;
+       }
+
+       bt_uuid16_create(&report_uuid, HOG_REPORT_UUID);
+       bt_uuid16_create(&report_map_uuid, HOG_REPORT_MAP_UUID);
+       bt_uuid16_create(&info_uuid, HOG_INFO_UUID);
+       bt_uuid16_create(&proto_mode_uuid, HOG_PROTO_MODE_UUID);
+       bt_uuid16_create(&ctrlpt_uuid, HOG_CONTROL_POINT_UUID);
+
+       for (l = chars; l; l = g_slist_next(l)) {
+               struct gatt_char *chr, *next;
+               bt_uuid_t uuid;
+               uint16_t start, end;
+
+               chr = l->data;
+               next = l->next ? l->next->data : NULL;
+
+               DBG("0x%04x UUID: %s properties: %02x",
+                               chr->handle, chr->uuid, chr->properties);
+
+               bt_string_to_uuid(&uuid, chr->uuid);
+
+               start = chr->value_handle + 1;
+               end = (next ? next->handle - 1 : primary->range.end);
+
+               if (bt_uuid_cmp(&uuid, &report_uuid) == 0) {
+                       report = report_new(hog, chr);
+                       discover_report(hog, hog->attrib, start, end, report);
+               } else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) {
+                       read_char(hog, hog->attrib, chr->value_handle,
+                                               report_map_read_cb, hog);
+                       discover_external(hog, hog->attrib, start, end, hog);
+               } else if (bt_uuid_cmp(&uuid, &info_uuid) == 0)
+                       info_handle = chr->value_handle;
+               else if (bt_uuid_cmp(&uuid, &proto_mode_uuid) == 0)
+                       proto_mode_handle = chr->value_handle;
+               else if (bt_uuid_cmp(&uuid, &ctrlpt_uuid) == 0)
+                       hog->ctrlpt_handle = chr->value_handle;
+       }
+
+       if (proto_mode_handle) {
+               hog->proto_mode_handle = proto_mode_handle;
+               read_char(hog, hog->attrib, proto_mode_handle,
+                                               proto_mode_read_cb, hog);
+       }
+
+       if (info_handle)
+               read_char(hog, hog->attrib, info_handle, info_read_cb, hog);
+}
+
+static void report_free(void *data)
+{
+       struct report *report = data;
+
+       g_free(report->value);
+       g_free(report->decl);
+       g_free(report);
+}
+
+static void cancel_gatt_req(struct gatt_request *req)
+{
+       if (g_attrib_cancel(req->hog->attrib, req->id))
+               destroy_gatt_req(req);
+}
+
+static void hog_free(void *data)
+{
+       struct bt_hog *hog = data;
+
+       bt_hog_detach(hog);
+
+       queue_destroy(hog->bas, (void *) bt_bas_unref);
+       g_slist_free_full(hog->instances, hog_free);
+
+       bt_scpp_unref(hog->scpp);
+       bt_dis_unref(hog->dis);
+       bt_uhid_unref(hog->uhid);
+       g_slist_free_full(hog->reports, report_free);
+       g_free(hog->name);
+       g_free(hog->primary);
+       queue_destroy(hog->gatt_op, (void *) destroy_gatt_req);
+       g_free(hog);
+}
+
+struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
+                                       uint16_t product, uint16_t version,
+                                       void *primary)
+{
+       return bt_hog_new(-1, name, vendor, product, version, primary);
+}
+
+struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
+                                       uint16_t product, uint16_t version,
+                                       void *primary)
+{
+       struct bt_hog *hog;
+
+       hog = g_try_new0(struct bt_hog, 1);
+       if (!hog)
+               return NULL;
+
+       hog->gatt_op = queue_new();
+       hog->bas = queue_new();
+
+       if (fd < 0)
+               hog->uhid = bt_uhid_new_default();
+       else
+               hog->uhid = bt_uhid_new(fd);
+
+       hog->uhid_fd = fd;
+
+       if (!hog->gatt_op || !hog->bas || !hog->uhid) {
+               hog_free(hog);
+               return NULL;
+       }
+
+       hog->name = g_strdup(name);
+       hog->vendor = vendor;
+       hog->product = product;
+       hog->version = version;
+
+       if (primary)
+               hog->primary = g_memdup(primary, sizeof(*hog->primary));
+
+       return bt_hog_ref(hog);
+}
+
+struct bt_hog *bt_hog_ref(struct bt_hog *hog)
+{
+       if (!hog)
+               return NULL;
+
+       __sync_fetch_and_add(&hog->ref_count, 1);
+
+       return hog;
+}
+
+void bt_hog_unref(struct bt_hog *hog)
+{
+       if (!hog)
+               return;
+
+       if (__sync_sub_and_fetch(&hog->ref_count, 1))
+               return;
+
+       hog_free(hog);
+}
+
+static void find_included_cb(uint8_t status, GSList *services, void *user_data)
+{
+       struct gatt_request *req = user_data;
+       GSList *l;
+
+       DBG("");
+
+       destroy_gatt_req(req);
+
+       if (status) {
+               const char *str = att_ecode2str(status);
+               DBG("Find included failed: %s", str);
+               return;
+       }
+
+       for (l = services; l; l = l->next) {
+               struct gatt_included *include = l->data;
+
+               DBG("included: handle %x, uuid %s",
+                       include->handle, include->uuid);
+       }
+}
+
+static void hog_attach_scpp(struct bt_hog *hog, struct gatt_primary *primary)
+{
+       if (hog->scpp) {
+               bt_scpp_attach(hog->scpp, hog->attrib);
+               return;
+       }
+
+       hog->scpp = bt_scpp_new(primary);
+       if (hog->scpp)
+               bt_scpp_attach(hog->scpp, hog->attrib);
+}
+
+static void dis_notify(uint8_t source, uint16_t vendor, uint16_t product,
+                                       uint16_t version, void *user_data)
+{
+       struct bt_hog *hog = user_data;
+
+       hog->vendor = vendor;
+       hog->product = product;
+       hog->version = version;
+}
+
+static void hog_attach_dis(struct bt_hog *hog, struct gatt_primary *primary)
+{
+       if (hog->dis) {
+               bt_dis_attach(hog->dis, hog->attrib);
+               return;
+       }
+
+       hog->dis = bt_dis_new(primary);
+       if (hog->dis) {
+               bt_dis_set_notification(hog->dis, dis_notify, hog);
+               bt_dis_attach(hog->dis, hog->attrib);
+       }
+}
+
+static void hog_attach_bas(struct bt_hog *hog, struct gatt_primary *primary)
+{
+       struct bt_bas *instance;
+
+       instance = bt_bas_new(primary);
+       if (!instance)
+               return;
+
+       bt_bas_attach(instance, hog->attrib);
+       queue_push_head(hog->bas, instance);
+}
+
+static void hog_attach_hog(struct bt_hog *hog, struct gatt_primary *primary)
+{
+       struct bt_hog *instance;
+
+       if (!hog->primary) {
+               hog->primary = g_memdup(primary, sizeof(*primary));
+               discover_char(hog, hog->attrib, primary->range.start,
+                                               primary->range.end, NULL,
+                                               char_discovered_cb, hog);
+               find_included(hog, hog->attrib, primary->range.start,
+                               primary->range.end, find_included_cb, hog);
+               return;
+       }
+
+       instance = bt_hog_new(hog->uhid_fd, hog->name, hog->vendor,
+                                       hog->product, hog->version, primary);
+       if (!instance)
+               return;
+
+       find_included(instance, hog->attrib, primary->range.start,
+                       primary->range.end, find_included_cb, instance);
+
+       bt_hog_attach(instance, hog->attrib);
+       hog->instances = g_slist_append(hog->instances, instance);
+}
+
+static void primary_cb(uint8_t status, GSList *services, void *user_data)
+{
+       struct gatt_request *req = user_data;
+       struct bt_hog *hog = req->user_data;
+       struct gatt_primary *primary;
+       GSList *l;
+
+       DBG("");
+
+       destroy_gatt_req(req);
+
+       if (status) {
+               const char *str = att_ecode2str(status);
+               DBG("Discover primary failed: %s", str);
+               return;
+       }
+
+       if (!services) {
+               DBG("No primary service found");
+               return;
+       }
+
+       for (l = services; l; l = l->next) {
+               primary = l->data;
+
+               if (strcmp(primary->uuid, SCAN_PARAMETERS_UUID) == 0) {
+                       hog_attach_scpp(hog, primary);
+                       continue;
+               }
+
+               if (strcmp(primary->uuid, DEVICE_INFORMATION_UUID) == 0) {
+                       hog_attach_dis(hog, primary);
+                       continue;
+               }
+
+               if (strcmp(primary->uuid, BATTERY_UUID) == 0) {
+                       hog_attach_bas(hog, primary);
+                       continue;
+               }
+
+               if (strcmp(primary->uuid, HOG_UUID) == 0)
+                       hog_attach_hog(hog, primary);
+       }
+}
+
+bool bt_hog_attach(struct bt_hog *hog, void *gatt)
+{
+       struct gatt_primary *primary = hog->primary;
+       GSList *l;
+
+       if (hog->attrib)
+               return false;
+
+       hog->attrib = g_attrib_ref(gatt);
+
+       if (!primary) {
+               discover_primary(hog, hog->attrib, NULL, primary_cb, hog);
+               return true;
+       }
+
+       if (hog->scpp)
+               bt_scpp_attach(hog->scpp, gatt);
+
+       if (hog->dis)
+               bt_dis_attach(hog->dis, gatt);
+
+       queue_foreach(hog->bas, (void *) bt_bas_attach, gatt);
+
+       for (l = hog->instances; l; l = l->next) {
+               struct bt_hog *instance = l->data;
+
+               bt_hog_attach(instance, gatt);
+       }
+
+       if (hog->reports == NULL) {
+               discover_char(hog, hog->attrib, primary->range.start,
+                                               primary->range.end, NULL,
+                                               char_discovered_cb, hog);
+               return true;
+       }
+
+       for (l = hog->reports; l; l = l->next) {
+               struct report *r = l->data;
+
+               r->notifyid = g_attrib_register(hog->attrib,
+                                       ATT_OP_HANDLE_NOTIFY,
+                                       r->decl->value_handle,
+                                       report_value_cb, r, NULL);
+       }
+
+       return true;
+}
+
+void bt_hog_detach(struct bt_hog *hog)
+{
+       GSList *l;
+
+       if (!hog->attrib)
+               return;
+
+       queue_foreach(hog->bas, (void *) bt_bas_detach, NULL);
+
+       for (l = hog->instances; l; l = l->next) {
+               struct bt_hog *instance = l->data;
+
+               bt_hog_detach(instance);
+       }
+
+       for (l = hog->reports; l; l = l->next) {
+               struct report *r = l->data;
+
+               if (r->notifyid > 0) {
+                       g_attrib_unregister(hog->attrib, r->notifyid);
+                       r->notifyid = 0;
+               }
+       }
+
+       if (hog->scpp)
+               bt_scpp_detach(hog->scpp);
+
+       if (hog->dis)
+               bt_dis_detach(hog->dis);
+
+       queue_foreach(hog->gatt_op, (void *) cancel_gatt_req, NULL);
+       g_attrib_unref(hog->attrib);
+       hog->attrib = NULL;
+}
+
+int bt_hog_set_control_point(struct bt_hog *hog, bool suspend)
+{
+       uint8_t value = suspend ? 0x00 : 0x01;
+
+       if (hog->attrib == NULL)
+               return -ENOTCONN;
+
+       if (hog->ctrlpt_handle == 0)
+               return -ENOTSUP;
+
+       gatt_write_cmd(hog->attrib, hog->ctrlpt_handle, &value,
+                                       sizeof(value), NULL, NULL);
+
+       return 0;
+}
+
+int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type)
+{
+       struct report *report;
+       GSList *l;
+
+       if (!hog)
+               return -EINVAL;
+
+       if (!hog->attrib)
+               return -ENOTCONN;
+
+       report = find_report(hog, type, 0);
+       if (!report)
+               return -ENOTSUP;
+
+       DBG("hog: Write report, handle 0x%X", report->decl->value_handle);
+
+       if (report->decl->properties & GATT_CHR_PROP_WRITE)
+               write_char(hog, hog->attrib, report->decl->value_handle,
+                               data, size, output_written_cb, hog);
+
+       if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
+               gatt_write_cmd(hog->attrib, report->decl->value_handle,
+                                               data, size, NULL, NULL);
+
+       for (l = hog->instances; l; l = l->next) {
+               struct bt_hog *instance = l->data;
+
+               bt_hog_send_report(instance, data, size, type);
+       }
+
+       return 0;
+}
diff --git a/android/hog.h b/android/hog.h
new file mode 100755 (executable)
index 0000000..2a9b899
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  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
+ *
+ */
+
+struct bt_hog;
+
+struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
+                                       uint16_t product, uint16_t version,
+                                       void *primary);
+
+struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
+                                       uint16_t product, uint16_t version,
+                                       void *primary);
+
+struct bt_hog *bt_hog_ref(struct bt_hog *hog);
+void bt_hog_unref(struct bt_hog *hog);
+
+bool bt_hog_attach(struct bt_hog *hog, void *gatt);
+void bt_hog_detach(struct bt_hog *hog);
+
+int bt_hog_set_control_point(struct bt_hog *hog, bool suspend);
+int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type);
diff --git a/android/sco-ipc-api.txt b/android/sco-ipc-api.txt
new file mode 100755 (executable)
index 0000000..27d5ef2
--- /dev/null
@@ -0,0 +1,37 @@
+Bluetooth SCO Audio Plugin
+==========================
+
+The SCO Audio Plugin communicate through abstract socket name
+"\0bluez_sco_socket".
+
+       .----SCO----.                             .--Android--.
+       |   Plugin  |                             |   Daemon  |
+       |           |          Command            |           |
+       |           | --------------------------> |           |
+       |           |                             |           |
+       |           | <-------------------------- |           |
+       |           |          Response           |           |
+       |           |                             |           |
+       |           |                             |           |
+       |           |                             |           |
+       '-----------'                             '-----------'
+
+
+       SCO HAL                               Daemon
+       ----------------------------------------------------
+
+       call get_fd()                    --> Get SCO socket fd
+       return get_fd()                  <-- Return SCO socket fd and mtu
+
+SCO Audio Service (ID 0)
+========================
+
+       Opcode 0x00 - Error response
+
+               Response parameters: Status (1 octet)
+
+       Opcode 0x01 - Get SCO fd command
+
+               Command parameters: Remote address (6 octets)
+               Response parameters: MTU (2 octets)
+                                    File descriptor (inline)
diff --git a/android/scpp.c b/android/scpp.c
new file mode 100755 (executable)
index 0000000..f8f81f3
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Nordic Semiconductor Inc.
+ *  Copyright (C) 2012  Instituto Nokia de Tecnologia - INdT
+ *
+ *
+ *  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 <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt.h"
+
+#include "android/scpp.h"
+
+#define SCAN_INTERVAL_WIN_UUID         0x2A4F
+#define SCAN_REFRESH_UUID              0x2A31
+
+#define SCAN_INTERVAL          0x0060
+#define SCAN_WINDOW            0x0030
+#define SERVER_REQUIRES_REFRESH        0x00
+
+struct bt_scpp {
+       int ref_count;
+       GAttrib *attrib;
+       struct gatt_primary *primary;
+       uint16_t interval;
+       uint16_t window;
+       uint16_t iwhandle;
+       uint16_t refresh_handle;
+       guint refresh_cb_id;
+       struct queue *gatt_op;
+};
+
+static void discover_char(struct bt_scpp *scpp, GAttrib *attrib,
+                                               uint16_t start, uint16_t end,
+                                               bt_uuid_t *uuid, gatt_cb_t func,
+                                               gpointer user_data)
+{
+       unsigned int id;
+
+       id = gatt_discover_char(attrib, start, end, uuid, func, user_data);
+
+       if (queue_push_head(scpp->gatt_op, UINT_TO_PTR(id)))
+               return;
+
+       error("scpp: Could not discover characteristic");
+       g_attrib_cancel(attrib, id);
+}
+
+static void discover_desc(struct bt_scpp *scpp, GAttrib *attrib,
+                               uint16_t start, uint16_t end, bt_uuid_t *uuid,
+                               gatt_cb_t func, gpointer user_data)
+{
+       unsigned int id;
+
+       id = gatt_discover_desc(attrib, start, end, uuid, func, user_data);
+
+       if (queue_push_head(scpp->gatt_op, UINT_TO_PTR(id)))
+               return;
+
+       error("scpp: Could not discover descriptor");
+       g_attrib_cancel(attrib, id);
+}
+
+static void write_char(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle,
+                                       const uint8_t *value, size_t vlen,
+                                       GAttribResultFunc func,
+                                       gpointer user_data)
+{
+       unsigned int id;
+
+       id = gatt_write_char(attrib, handle, value, vlen, func, user_data);
+
+       if (queue_push_head(scan->gatt_op, UINT_TO_PTR(id)))
+               return;
+
+       error("scpp: Could not read char");
+       g_attrib_cancel(attrib, id);
+}
+
+static void scpp_free(struct bt_scpp *scan)
+{
+       bt_scpp_detach(scan);
+
+       g_free(scan->primary);
+       queue_destroy(scan->gatt_op, NULL); /* cleared in bt_scpp_detach */
+       g_free(scan);
+}
+
+struct bt_scpp *bt_scpp_new(void *primary)
+{
+       struct bt_scpp *scan;
+
+       scan = g_try_new0(struct bt_scpp, 1);
+       if (!scan)
+               return NULL;
+
+       scan->interval = SCAN_INTERVAL;
+       scan->window = SCAN_WINDOW;
+
+       scan->gatt_op = queue_new();
+       if (!scan->gatt_op) {
+               scpp_free(scan);
+               return NULL;
+       }
+
+       if (primary)
+               scan->primary = g_memdup(primary, sizeof(*scan->primary));
+
+       return bt_scpp_ref(scan);
+}
+
+struct bt_scpp *bt_scpp_ref(struct bt_scpp *scan)
+{
+       if (!scan)
+               return NULL;
+
+       __sync_fetch_and_add(&scan->ref_count, 1);
+
+       return scan;
+}
+
+void bt_scpp_unref(struct bt_scpp *scan)
+{
+       if (!scan)
+               return;
+
+       if (__sync_sub_and_fetch(&scan->ref_count, 1))
+               return;
+
+       scpp_free(scan);
+}
+
+static void write_scan_params(GAttrib *attrib, uint16_t handle,
+                                       uint16_t interval, uint16_t window)
+{
+       uint8_t value[4];
+
+       put_le16(interval, &value[0]);
+       put_le16(window, &value[2]);
+
+       gatt_write_cmd(attrib, handle, value, sizeof(value), NULL, NULL);
+}
+
+static void refresh_value_cb(const uint8_t *pdu, uint16_t len,
+                                               gpointer user_data)
+{
+       struct bt_scpp *scan = user_data;
+
+       DBG("Server requires refresh: %d", pdu[3]);
+
+       if (pdu[3] == SERVER_REQUIRES_REFRESH)
+               write_scan_params(scan->attrib, scan->iwhandle, scan->interval,
+                                                               scan->window);
+}
+
+static void ccc_written_cb(guint8 status, const guint8 *pdu,
+                                       guint16 plen, gpointer user_data)
+{
+       struct bt_scpp *scan = user_data;
+
+       if (status != 0) {
+               error("Write Scan Refresh CCC failed: %s",
+                                               att_ecode2str(status));
+               return;
+       }
+
+       DBG("Scan Refresh: notification enabled");
+
+       scan->refresh_cb_id = g_attrib_register(scan->attrib,
+                               ATT_OP_HANDLE_NOTIFY, scan->refresh_handle,
+                               refresh_value_cb, scan, NULL);
+}
+
+static void write_ccc(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle,
+                                                               void *user_data)
+{
+       uint8_t value[2];
+
+       put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+
+       write_char(scan, attrib, handle, value, sizeof(value), ccc_written_cb,
+                                                               user_data);
+}
+
+static void discover_descriptor_cb(uint8_t status, GSList *descs,
+                                                               void *user_data)
+{
+       struct bt_scpp *scan = user_data;
+       struct gatt_desc *desc;
+
+       if (status != 0) {
+               error("Discover descriptors failed: %s", att_ecode2str(status));
+               return;
+       }
+
+       /* There will be only one descriptor on list and it will be CCC */
+       desc = descs->data;
+
+       write_ccc(scan, scan->attrib, desc->handle, scan);
+}
+
+static void refresh_discovered_cb(uint8_t status, GSList *chars,
+                                                               void *user_data)
+{
+       struct bt_scpp *scan = user_data;
+       struct gatt_char *chr;
+       uint16_t start, end;
+       bt_uuid_t uuid;
+
+       if (status) {
+               error("Scan Refresh %s", att_ecode2str(status));
+               return;
+       }
+
+       if (!chars) {
+               DBG("Scan Refresh not supported");
+               return;
+       }
+
+       chr = chars->data;
+
+       DBG("Scan Refresh handle: 0x%04x", chr->value_handle);
+
+       start = chr->value_handle + 1;
+       end = scan->primary->range.end;
+
+       if (start > end)
+               return;
+
+       scan->refresh_handle = chr->value_handle;
+
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+       discover_desc(scan, scan->attrib, start, end, &uuid,
+                                       discover_descriptor_cb, user_data);
+}
+
+static void iwin_discovered_cb(uint8_t status, GSList *chars, void *user_data)
+{
+       struct bt_scpp *scan = user_data;
+       struct gatt_char *chr;
+
+       if (status) {
+               error("Discover Scan Interval Window: %s",
+                                               att_ecode2str(status));
+               return;
+       }
+
+       chr = chars->data;
+       scan->iwhandle = chr->value_handle;
+
+       DBG("Scan Interval Window handle: 0x%04x", scan->iwhandle);
+
+       write_scan_params(scan->attrib, scan->iwhandle, scan->interval,
+                                                               scan->window);
+}
+
+bool bt_scpp_attach(struct bt_scpp *scan, void *attrib)
+{
+       bt_uuid_t iwin_uuid, refresh_uuid;
+
+       if (!scan || scan->attrib || !scan->primary)
+               return false;
+
+       scan->attrib = g_attrib_ref(attrib);
+
+       if (scan->iwhandle)
+               write_scan_params(scan->attrib, scan->iwhandle, scan->interval,
+                                                               scan->window);
+       else {
+               bt_uuid16_create(&iwin_uuid, SCAN_INTERVAL_WIN_UUID);
+               discover_char(scan, scan->attrib, scan->primary->range.start,
+                                       scan->primary->range.end, &iwin_uuid,
+                                       iwin_discovered_cb, scan);
+       }
+
+       if (scan->refresh_handle)
+               scan->refresh_cb_id = g_attrib_register(scan->attrib,
+                               ATT_OP_HANDLE_NOTIFY, scan->refresh_handle,
+                               refresh_value_cb, scan, NULL);
+       else {
+               bt_uuid16_create(&refresh_uuid, SCAN_REFRESH_UUID);
+               discover_char(scan, scan->attrib, scan->primary->range.start,
+                                       scan->primary->range.end, &refresh_uuid,
+                                       refresh_discovered_cb, scan);
+       }
+
+       return true;
+}
+
+static void cancel_gatt_req(void *data, void *user_data)
+{
+       unsigned int id = PTR_TO_UINT(data);
+       struct bt_scpp *scan = user_data;
+
+       g_attrib_cancel(scan->attrib, id);
+}
+
+void bt_scpp_detach(struct bt_scpp *scan)
+{
+       if (!scan || !scan->attrib)
+               return;
+
+       if (scan->refresh_cb_id > 0) {
+               g_attrib_unregister(scan->attrib, scan->refresh_cb_id);
+               scan->refresh_cb_id = 0;
+       }
+
+       queue_foreach(scan->gatt_op, cancel_gatt_req, scan);
+       g_attrib_unref(scan->attrib);
+       scan->attrib = NULL;
+}
+
+bool bt_scpp_set_interval(struct bt_scpp *scan, uint16_t value)
+{
+       if (!scan)
+               return false;
+
+       /* TODO: Check valid range */
+
+       scan->interval = value;
+
+       return true;
+}
+
+bool bt_scpp_set_window(struct bt_scpp *scan, uint16_t value)
+{
+       if (!scan)
+               return false;
+
+       /* TODO: Check valid range */
+
+       scan->window = value;
+
+       return true;
+}
diff --git a/android/scpp.h b/android/scpp.h
new file mode 100755 (executable)
index 0000000..048fb9f
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can rescpptribute 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 scpptributed 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
+ *
+ */
+
+struct bt_scpp;
+
+struct bt_scpp *bt_scpp_new(void *primary);
+
+struct bt_scpp *bt_scpp_ref(struct bt_scpp *scan);
+void bt_scpp_unref(struct bt_scpp *scan);
+
+bool bt_scpp_attach(struct bt_scpp *scan, void *gatt);
+void bt_scpp_detach(struct bt_scpp *scan);
+
+bool bt_scpp_set_interval(struct bt_scpp *scan, uint16_t value);
+bool bt_scpp_set_window(struct bt_scpp *scan, uint16_t value);
diff --git a/android/socket-api.txt b/android/socket-api.txt
new file mode 100755 (executable)
index 0000000..9f622f9
--- /dev/null
@@ -0,0 +1,61 @@
+Android Socket protocol for Bluetooth
+=====================================
+
+Since Android switched from BlueZ (where sockets where nicely implemented) to
+Bluedroid user space stack there is a need to emulate bluetooth sockets.
+
+Android Bluetooth Socket Hardware Abstraction Layer (HAL) bt_sock.h has
+only 2 functions:
+
+static btsock_interface_t sock_if = {
+       sizeof(sock_if),
+       sock_listen,
+       sock_connect
+};
+
+with following parameters:
+
+sock_listen(btsock_type_t type, const char *service_name,
+               const uint8_t *uuid, int chan, int *sock_fd, int flags)
+sock_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type,
+               const uint8_t *uuid, int chan, int *sock_fd, int flags)
+
+socket type RFCOMM is only supported at the moment. uuid and channel used
+to decide where to connect.
+
+sockfd is used to return socket fd to Android framework. It is used to inform
+framework when remote device is connected.
+
+listen()
+========
+
+Listens on RFCOMM socket, socket channel is either found based on uuid or
+channel parameter used directly. Returns sock_fd to Android framework.
+
+Through this sock_fd channel number as (int) needs to be written right after
+listen() succeeds.
+
+When remote device is connected to this socket we shall send accept signal
+through sock_fd
+
+connect()
+=========
+
+Connects to remote device specified in bd_addr parameter. Socket channel is
+found by SDP search of remote device by supplied uuid. Returns sock_fd to
+Android framework.
+
+Through this sock_fd channel number as (int) needs to be written right after
+connects() succeeds.
+
+When remote device is connected to this socket we shall send connect signal
+through sock_fd
+
+The format of connect/accept signal is shown below:
+
+struct hal_sock_connect_signal {
+       short   size;
+       uint8_t bdaddr[6];
+       int     channel;
+       int     status;
+} __attribute__((packed));
index 826b3c1..fef602c 100755 (executable)
@@ -129,11 +129,18 @@ struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len)
 
        return list;
 }
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid)
+#else
 static void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid)
+#endif
 {
        if (type == BT_UUID16)
                bt_uuid16_create(uuid, get_le16(val));
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       else if (type == BT_UUID32)
+               bt_uuid32_create(uuid, get_le32(val));
+#endif
        else {
                uint128_t u128;
 
@@ -153,6 +160,10 @@ uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
 
        if (uuid->type == BT_UUID16)
                uuid_len = 2;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       else if (uuid->type == BT_UUID32)
+               uuid_len = 4;
+#endif
        else if (uuid->type == BT_UUID128)
                uuid_len = 16;
        else
@@ -187,6 +198,10 @@ uint16_t dec_read_by_grp_req(const uint8_t *pdu, size_t len, uint16_t *start,
 
        if (len == (min_len + 2))
                type = BT_UUID16;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       else if (len == (min_len + 4))
+               type = BT_UUID32;
+#endif
        else if (len == (min_len + 16))
                type = BT_UUID128;
        else
index 2311aaf..c32f9ff 100755 (executable)
@@ -106,6 +106,26 @@ struct att_range {
        uint16_t end;
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid);
+
+static inline bt_uuid_t att_get_uuid(const void *ptr, uint8_t len)
+{
+       bt_uuid_t uuid;
+       if (len == 2) {
+               //return att_get_uuid16(ptr);
+               get_uuid(BT_UUID16, ptr, &uuid);
+       } else if (len == 4) {
+               //return att_get_uuid32(ptr);
+               get_uuid(BT_UUID32, ptr, &uuid);
+       } else {
+               //return att_get_uuid128(ptr);
+               get_uuid(BT_UUID128, ptr, &uuid);
+       }
+       return uuid;
+}
+#endif
+
 struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len);
 void att_data_list_free(struct att_data_list *list);
 
index 629d9cf..e434d3b 100755 (executable)
@@ -81,17 +81,26 @@ static GSList *parse_opts(gatt_option opt1, va_list args)
        while (opt != GATT_OPT_INVALID) {
                switch (opt) {
                case GATT_OPT_CHR_UUID16:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               case GATT_OPT_DESC_UUID16:
+#endif
                        bt_uuid16_create(&info->uuid, va_arg(args, int));
                        /* characteristic declaration and value */
                        info->num_attrs += 2;
                        break;
                case GATT_OPT_CHR_UUID:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               case GATT_OPT_DESC_UUID:
+#endif
                        memcpy(&info->uuid, va_arg(args, bt_uuid_t *),
                                                        sizeof(bt_uuid_t));
                        /* characteristic declaration and value */
                        info->num_attrs += 2;
                        break;
                case GATT_OPT_CHR_PROPS:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               case GATT_OPT_DESC_PROPS:
+#endif
                        info->props = va_arg(args, int);
 
                        if (info->props & (GATT_CHR_PROP_NOTIFY |
@@ -103,6 +112,9 @@ static GSList *parse_opts(gatt_option opt1, va_list args)
                         * descriptor, but it is not supported yet. */
                        break;
                case GATT_OPT_CHR_VALUE_CB:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               case GATT_OPT_DESC_VALUE_CB:
+#endif
                        cb = g_new0(struct attrib_cb, 1);
                        cb->event = va_arg(args, attrib_event_t);
                        cb->fn = va_arg(args, void *);
@@ -130,7 +142,12 @@ static GSList *parse_opts(gatt_option opt1, va_list args)
                }
 
                opt = va_arg(args, gatt_option);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               if (opt == GATT_OPT_CHR_UUID16 || opt == GATT_OPT_CHR_UUID ||
+                       opt == GATT_OPT_DESC_UUID16 || opt == GATT_OPT_DESC_UUID) {
+#else
                if (opt == GATT_OPT_CHR_UUID16 || opt == GATT_OPT_CHR_UUID) {
+#endif
                        info = g_new0(struct gatt_info, 1);
                        l = g_slist_append(l, info);
                }
@@ -268,6 +285,10 @@ static gboolean add_characteristic(struct btd_adapter *adapter,
        if (info->value_handle != NULL)
                *info->value_handle = a->handle;
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+/* Since old attrib service implementation add descriptor by default
+  * if notification and indication properties are set, As per new gatt server implemenation
+  * CCCD are added by the application*/
        /* client characteristic configuration descriptor */
        if (info->props & (GATT_CHR_PROP_NOTIFY | GATT_CHR_PROP_INDICATE)) {
                uint8_t cfg_val[2];
@@ -283,6 +304,7 @@ static gboolean add_characteristic(struct btd_adapter *adapter,
                if (info->ccc_handle != NULL)
                        *info->ccc_handle = a->handle;
        }
+#endif
 
        *handle = h;
 
index 728d3a8..5a8ac38 100755 (executable)
@@ -31,8 +31,20 @@ typedef enum {
        /* a uint16 value */
        GATT_OPT_CHR_UUID16,
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* bt_uuid_t* value */
+       GATT_OPT_DESC_UUID,
+
+       /* a uint16 value */
+       GATT_OPT_DESC_UUID16,
+#endif
+
        GATT_OPT_CHR_PROPS,
        GATT_OPT_CHR_VALUE_CB,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       GATT_OPT_DESC_PROPS,
+       GATT_OPT_DESC_VALUE_CB,
+#endif
        GATT_OPT_CHR_AUTHENTICATION,
        GATT_OPT_CHR_AUTHORIZATION,
 
index 480f874..72457ce 100755 (executable)
@@ -185,6 +185,10 @@ static void put_uuid_le(const bt_uuid_t *uuid, void *dst)
 {
        if (uuid->type == BT_UUID16)
                put_le16(uuid->value.u16, dst);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       else if (uuid->type == BT_UUID32)
+               put_le32(uuid->value.u32, dst);
+#endif
        else
                /* Convert from 128-bit BE to LE */
                bswap_128(&uuid->value.u128, dst);
@@ -197,6 +201,13 @@ static void get_uuid128(uint8_t type, const void *val, bt_uuid_t *uuid)
 
                bt_uuid16_create(&uuid16, get_le16(val));
                bt_uuid_to_uuid128(&uuid16, uuid);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       } else if (type == BT_UUID32) {
+               bt_uuid_t uuid32;
+
+               bt_uuid32_create(&uuid32, get_le32(val));
+               bt_uuid_to_uuid128(&uuid32, uuid);
+#endif
        } else {
                uint128_t u128;
 
@@ -841,6 +852,45 @@ done:
        long_read->func(status, rpdu, rlen, long_read->user_data);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+guint gatt_read_char_by_offset(GAttrib *attrib, uint16_t handle, uint16_t offset,
+                               GAttribResultFunc func, gpointer user_data)
+{
+       uint8_t *buf;
+       size_t buflen;
+       guint16 plen;
+       guint id;
+       struct read_long_data *long_read;
+
+       long_read = g_try_new0(struct read_long_data, 1);
+
+       if (long_read == NULL)
+               return 0;
+
+       long_read->attrib = g_attrib_ref(attrib);
+       long_read->func = func;
+       long_read->user_data = user_data;
+       long_read->handle = handle;
+
+       buf = g_attrib_get_buffer(attrib, &buflen);
+       if (offset > 0)
+               plen = enc_read_blob_req(handle, offset, buf, buflen);
+       else
+               plen = enc_read_req(handle, buf, buflen);
+
+       id = g_attrib_send(attrib, 0, buf, plen, read_char_helper,
+                                               long_read, read_long_destroy);
+       if (id == 0)
+               g_free(long_read);
+       else {
+               __sync_fetch_and_add(&long_read->ref, 1);
+               long_read->id = id;
+       }
+
+       return id;
+}
+#endif
+
 guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func,
                                                        gpointer user_data)
 {
index 63b2940..0d998c9 100755 (executable)
@@ -120,3 +120,8 @@ guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func,
 gboolean gatt_parse_record(const sdp_record_t *rec,
                                        uuid_t *prim_uuid, uint16_t *psm,
                                        uint16_t *start, uint16_t *end);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+guint gatt_read_char_by_offset(GAttrib *attrib, uint16_t handle, uint16_t offset,
+                               GAttribResultFunc func, gpointer user_data);
+#endif
index 7d4786a..62a25fa 100755 (executable)
@@ -559,7 +559,9 @@ static void cmd_char_desc(int argcp, char **argvp)
 static void cmd_read_hnd(int argcp, char **argvp)
 {
        int handle;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       int offset = 0;
+#endif
        if (conn_state != STATE_CONNECTED) {
                failed("Disconnected\n");
                return;
@@ -575,8 +577,18 @@ static void cmd_read_hnd(int argcp, char **argvp)
                error("Invalid handle: %s\n", argvp[1]);
                return;
        }
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (argcp > 2) {
+               offset = strtohandle(argvp[2]);
+               if (offset < 0) {
+                       error("Invalid Offset: %s\n", argvp[2]);
+                       return;
+               }
+       }
+       gatt_read_char_by_offset(attrib, handle, offset, char_read_cb, attrib);
+#else
        gatt_read_char(attrib, handle, char_read_cb, attrib);
+#endif
 }
 
 static void cmd_read_uuid(int argcp, char **argvp)
@@ -796,8 +808,13 @@ static struct {
                "Characteristics Discovery" },
        { "char-desc",          cmd_char_desc,  "[start hnd] [end hnd]",
                "Characteristics Descriptor Discovery" },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { "char-read-hnd",      cmd_read_hnd,   "<handle> [offset]",
+               "Characteristics Value/Descriptor Read by handle" },
+#else
        { "char-read-hnd",      cmd_read_hnd,   "<handle>",
                "Characteristics Value/Descriptor Read by handle" },
+#endif
        { "char-read-uuid",     cmd_read_uuid,  "<UUID> [start hnd] [end hnd]",
                "Characteristics Value/Descriptor Read by UUID" },
        { "char-write-req",     cmd_char_write, "<handle> <new value>",
index e1198a8..bbf4cd3 100755 (executable)
@@ -808,6 +808,9 @@ static void cmd_show(const char *arg)
        print_uuids(proxy);
        print_property(proxy, "Modalias");
        print_property(proxy, "Discovering");
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       print_property(proxy, "Advertising");
+#endif
 }
 
 static void cmd_select(const char *arg)
@@ -1368,6 +1371,9 @@ static void cmd_info(const char *arg)
        print_property(proxy, "LegacyPairing");
        print_uuids(proxy);
        print_property(proxy, "Modalias");
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       print_property(proxy, "ManufacturerDataLen");
+#endif
        print_property(proxy, "ManufacturerData");
        print_property(proxy, "ServiceData");
        print_property(proxy, "RSSI");
index d469500..af589ab 100755 (executable)
@@ -34,13 +34,8 @@ AC_PROG_LIBTOOL
 
 if (test "$USE_MAINTAINER_MODE" = "yes"); then
        AC_CHECK_PROG(enable_coverage, [lcov], [yes], [no])
-       AC_CHECK_PROG(enable_dbus_run_session, [dbus-run-session], [yes])
-       AC_CHECK_PROG(enable_valgrind, [valgrind], [yes])
-       AC_CHECK_HEADERS(valgrind/memcheck.h)
 fi
 AM_CONDITIONAL(COVERAGE, test "${enable_coverage}" = "yes")
-AM_CONDITIONAL(DBUS_RUN_SESSION, test "${enable_dbus_run_session}" = "yes")
-AM_CONDITIONAL(VALGRIND, test "${enable_valgrind}" = "yes")
 
 MISC_FLAGS
 
@@ -79,6 +74,14 @@ PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.6, dummy=yes,
 AC_SUBST(DBUS_CFLAGS)
 AC_SUBST(DBUS_LIBS)
 
+PKG_CHECK_MODULES(LIBXML, libxml-2.0)
+AC_SUBST(LIBXML_CFLAGS)
+AC_SUBST(LIBXML_LIBS)
+
+PKG_CHECK_MODULES(INIPARSER, iniparser)
+AC_SUBST(INIPARSER_CFLAGS)
+AC_SUBST(INIPARSER_LIBS)
+
 AC_ARG_WITH([dbusconfdir], AC_HELP_STRING([--with-dbusconfdir=DIR],
                                [path to D-Bus configuration directory]),
                                        [path_dbusconfdir=${withval}])
@@ -180,6 +183,18 @@ AC_ARG_ENABLE(cups, AC_HELP_STRING([--disable-cups],
                 [disable CUPS printer support]), [enable_cups=${enableval}])
 AM_CONDITIONAL(CUPS, test "${enable_cups}" != "no")
 
+
+AC_ARG_ENABLE(midi, AC_HELP_STRING([--enable-midi],
+                [enable MIDI support]), [enable_midi=${enableval}])
+AM_CONDITIONAL(MIDI, test "${enable_midi}" = "no")
+
+if (test "${enable_midi}" = "yes"); then
+       PKG_CHECK_MODULES(ALSA, alsa, dummy=no,
+                               AC_MSG_ERROR(ALSA lib is required for MIDI support))
+       AC_SUBST(ALSA_CFLAGS)
+       AC_SUBST(ALSA_LIBS)
+fi
+
 AC_ARG_ENABLE(obex, AC_HELP_STRING([--disable-obex],
                [disable OBEX profile support]), [enable_obex=${enableval}])
                                
@@ -245,6 +260,39 @@ AC_ARG_ENABLE(experimental, AC_HELP_STRING([--enable-experimental],
                                        [enable_experimental=${enableval}])
 AM_CONDITIONAL(EXPERIMENTAL, test "${enable_experimental}" = "yes")
 
+# Start of TIZEN_FEATURE_BLUEZ_MODIFY
+AC_ARG_ENABLE(wearable, AC_HELP_STRING([--enable-wearable],
+                       [enable wearable profile]), [enable_wearable=${enableval}])
+AM_CONDITIONAL(WEARABLE, test "${enable_wearable}" = "yes")
+
+AC_ARG_ENABLE(autopair, AC_HELP_STRING([--enable-autopair],
+                       [Enable Autopair Plugin]), [enable_autopair=${enableval}])
+AM_CONDITIONAL(AUTOPAIR, test "${enable_autopair}" = "yes")
+
+AC_ARG_ENABLE(hid, AC_HELP_STRING([--enable-hid],
+                       [Enable HID Plugin]), [enable_hid=${enableval}])
+AM_CONDITIONAL(TIZEN_HID_PLUGIN, test "${enable_hid}" = "yes")
+
+AM_CONDITIONAL(TIZEN_HEALTH_PLUGIN, test "${enable_health}" = "yes")
+
+AC_ARG_ENABLE(proximity, AC_HELP_STRING([--enable-proximity],
+                  [Enable PROXIMITY Plugin]), [enable_proximity=${enableval}])
+AM_CONDITIONAL(TIZEN_PROXIMITY_PLUGIN, test "${enable_proximity}" = "yes")
+
+AC_ARG_ENABLE(tds, AC_HELP_STRING([--enable-tds],
+                  [Enable TDS Plugin]), [enable_tds=${enableval}])
+AM_CONDITIONAL(TIZEN_TDS_PLUGIN, test "${enable_tds}" = "yes")
+
+AC_ARG_ENABLE(tizenunusedplugin, AC_HELP_STRING([--enable-tizenunusedplugin],
+                [Enable Unused Plugin]), [enable_tizenunusedplugin=${enableval}])
+AM_CONDITIONAL(TIZEN_UNUSED_PLUGIN, test "${enable_tizenunusedplugin}" = "yes")
+
+AC_ARG_ENABLE(sap, AC_HELP_STRING([--enable-sap],
+                       [Enable SAP Plugin]), [enable_sap=${enableval}])
+AM_CONDITIONAL(TIZEN_SAP_PLUGIN, test "${enable_sap}" = "yes")
+
+# End of TIZEN_FEATURE_BLUEZ_MODIFY
+
 AC_ARG_ENABLE(deprecated, AC_HELP_STRING([--enable-deprecated],
                        [enable deprecated plugins (BLE services, ...)]),
                                        [enable_deprecated=${enableval}])
index 08de6cd..e37781e 100755 (executable)
@@ -109,6 +109,159 @@ Methods           void StartDiscovery()
                                         org.bluez.Error.NotSupported
                                         org.bluez.Error.Failed
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               void StartCustomDiscovery(string pattern)
+
+                       This method starts the device discovery session with
+                       parameter. The valid paramter strings are "BREDR",
+                       "LE" or "LE_BREDR" which will perform the inquiry for
+                       appropriate types. This includes an inquiry procedure
+                       and remote device name resolving. Use StopDiscovery
+                       to release the sessions acquired.
+
+                       This process will start creating Device objects as
+                       new devices are discovered.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                        org.bluez.Error.Failed
+
+               void StartLEDiscovery()
+
+                       This method starts the LE device discovery session.
+                       General discovery and active scan is default.
+                       Use StopLEDiscovery to release the sessions
+                       acquired.
+
+                       This process will start emitting DeviceFound and
+                       PropertyChanged "LEDiscovering" signals.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                        org.bluez.Error.Failed
+
+               void StopLEDiscovery()
+
+                       This method will cancel any previous StartLEDiscovery
+                       transaction.
+
+                       Note that a discovery procedure is shared between all
+                       discovery sessions thus calling StopLEDiscovery will only
+                       release a single session.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                        org.bluez.Error.Failed
+                                        org.bluez.Error.NotAuthorized
+
+               void SetAdvertising(boolean enable)
+
+                       This method is used to set LE advertising on a
+                       controller that supports it.
+
+                       This process will emit PropertyChanged "Advertising"
+                       signal.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                       org.bluez.Error.InvalidArguments
+                                       org.bluez.Error.Failed
+
+               void SetAdvertisingParameters(uint32 interval_min,
+                                               uint32 interval_max, uint32 filter_policy,
+                                               uint32 type)
+
+                       This method allows for setting the Low Energy
+                       advertising interval and advertising filter policy.
+                       It is only supported on controller with LE support.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                       org.bluez.Error.Failed
+
+               void SetAdvertisingData(array{byte} value)
+
+                       This method is used to set LE advertising data on a
+                       controller that supports it.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                       org.bluez.Error.InvalidArguments
+                                       org.bluez.Error.Failed
+
+               void SetScanParameters(uint32 type, uint32 interval, uint32 window)
+
+                       This method allows for setting the Low Energy
+                       scan interval and window.
+                       It is only supported on controller with LE support.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                       org.bluez.Error.Failed
+
+               void SetScanRespData(array{byte} value)
+
+                       This method is used to set LE scan response data on
+                       a controller that supports it.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                       org.bluez.Error.InvalidArguments
+                                       org.bluez.Error.Failed
+
+               void AddDeviceWhiteList(string address, uint32 address_type)
+
+                       This method is used to add LE device to White List for given
+                       address.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                       org.bluez.Error.InvalidArguments
+                                       org.bluez.Error.Failed
+
+               void RemoveDeviceWhiteList(string address, uint32 address_type)
+
+                       This method is used to remove LE device to White List for given
+                       address.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                       org.bluez.Error.InvalidArguments
+                                       org.bluez.Error.Failed
+
+               void ClearDeviceWhiteList()
+
+                       This method is used to clear LE device to White List
+
+                       Possible errors: org.bluez.Error.NotReady
+                                       org.bluez.Error.Failed
+
+               void SetLePrivacy(boolean enable_privacy)
+
+                       This method is used to set/reset LE privacy feature for the local
+                       adapter when it supports the feature.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                       org.bluez.Error.InvalidArguments
+                                       org.bluez.Error.Failed
+
+               void SetLeStaticRandomAddress(boolean enable_random_address)
+
+                       This method is used to set/reset LE static random address for the local
+                       adapter when it supports the feature.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                       org.bluez.Error.InvalidArguments
+                                       org.bluez.Error.Failed
+
+               void SetManufacturerData(array{byte} value)
+
+                       This method is used to set Manufacturer data on a
+                       controller that supports it.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                       org.bluez.Error.InvalidArguments
+                                       org.bluez.Error.Failed
+
+               void CreateDevice(string address)
+
+                       Creates a new object path for a remote device. This
+                       method will connect to the remote device and retrieve
+                       all SDP records.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+#endif
+
 Properties     string Address [readonly]
 
                        The Bluetooth device address.
@@ -219,3 +372,12 @@ Properties string Address [readonly]
 
                        Local Device ID information in modalias format
                        used by the kernel and udev.
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               boolean LEDiscovering [readonly]
+
+                       Indicates that a device LE discovery procedure is active.
+
+               string Version [readonly]
+
+                        The Bluetooth version.
+#endif
diff --git a/doc/coding-style.txt b/doc/coding-style.txt
new file mode 100755 (executable)
index 0000000..f0bf880
--- /dev/null
@@ -0,0 +1,279 @@
+BlueZ coding style
+******************
+
+Every project has its coding style, and BlueZ is not an exception. This
+document describes the preferred coding style for BlueZ code, in order to keep
+some level of consistency among developers so that code can be easily
+understood and maintained.
+
+First of all, BlueZ coding style must follow every rule for Linux kernel
+(http://www.kernel.org/doc/Documentation/CodingStyle). There also exists a tool
+named checkpatch.pl to help you check the compliance with it. Just type
+"checkpatch.pl --no-tree patch_name" to check your patch. In theory, you need
+to clean up all the warnings and errors except this one: "ERROR: Missing
+Signed-off-by: line(s)". BlueZ does not used Signed-Off lines, so including
+them is actually an error.  In certain circumstances one can ignore the 80
+character per line limit.  This is generally only allowed if the alternative
+would make the code even less readable.
+
+Besides the kernel coding style above, BlueZ has special flavors for its own.
+Some of them are mandatory (marked as 'M'), while some others are optional
+(marked as 'O'), but generally preferred.
+
+M1: Blank line before and after an if/while/do/for statement
+============================================================
+
+There should be a blank line before if statement unless the if is nested and
+not preceded by an expression or variable declaration.
+
+Example:
+1)
+a = 1;
+if (b) {  // wrong
+
+2)
+a = 1
+
+if (b) {
+}
+a = 2; // wrong
+
+3)
+if (a) {
+       if (b)  // correct
+
+4)
+b = 2;
+
+if (a) {       // correct
+
+}
+
+b = 3;
+
+The only exception to this rule applies when a variable is being checked for
+errors as such:
+
+err = stat(filename, &st);
+if (err || !S_ISDIR(st.st_mode))
+       return;
+
+M2: Multiple line comment
+=========================
+
+If your comment has more than one line, please start it from the second line.
+
+Example:
+/*
+ * first line comment  // correct
+ * ...
+ * last line comment
+ */
+
+
+M3: Space before and after operator
+===================================
+
+There should be a space before and after each operator.
+
+Example:
+a + b;  // correct
+
+
+M4: Wrap long lines
+===================
+
+If your condition in if, while, for statement or a function declaration is too
+long to fit in one line, the new line needs to be indented not aligned with the
+body.
+
+Example:
+1)
+if ((adapter->supported_settings & MGMT_SETTING_SSP) &&
+       !(adapter->current_settings & MGMT_SETTING_SSP)) // wrong
+
+2)
+if ((adapter->supported_settings & MGMT_SETTING_SSP) &&
+                               !(adapter->current_settings & MGMT_SETTING_SSP))
+
+3)
+void btd_adapter_register_pin_cb(struct btd_adapter *adapter,
+                                btd_adapter_pin_cb_t cb) // wrong
+
+4)
+void btd_adapter_register_pin_cb(struct btd_adapter *adapter,
+                                                       btd_adapter_pin_cb_t cb)
+
+The referred style for line wrapping is to indent as far as possible to the
+right without hitting the 80 columns limit.
+
+M5: Space when doing type casting
+=================================
+
+There should be a space between new type and variable.
+
+Example:
+1)
+a = (int *)b;  // wrong
+2)
+a = (int *) b;  // correct
+
+
+M6: Don't initialize variable unnecessarily
+===========================================
+
+When declaring a variable, try not to initialize it unless necessary.
+
+Example:
+int i = 1;  // wrong
+
+for (i = 0; i < 3; i++) {
+}
+
+M7: Follow the order of include header elements
+===============================================
+
+When writing an include header the various elements should be in the following
+order:
+       - #includes
+       - forward declarations
+       - #defines
+       - enums
+       - typedefs
+       - function declarations and inline function definitions
+
+M8: Internal headers must not use include guards
+================================================
+
+Any time when creating a new header file with non-public API, that header
+must not contain include guards.
+
+M9: Naming of enums
+===================
+
+Enums must have a descriptive name.  The enum type should be small caps and
+it should not be typedef-ed.  Enum contents should be in CAPITAL letters and
+prefixed by the enum type name.
+
+Example:
+
+enum animal_type {
+       ANIMAL_TYPE_FOUR_LEGS,
+       ANIMAL_TYPE_EIGHT_LEGS,
+       ANIMAL_TYPE_TWO_LEGS,
+};
+
+If the enum contents have values (e.g. from specification) the formatting
+should be as follows:
+
+enum animal_type {
+       ANIMAL_TYPE_FOUR_LEGS =         4,
+       ANIMAL_TYPE_EIGHT_LEGS =        8,
+       ANIMAL_TYPE_TWO_LEGS =          2,
+};
+
+M10: Enum as switch variable
+============================
+
+If the variable of a switch is an enum, you must include all values in
+switch body even if providing default. This is enforced by compiler option
+enabling extra warning in such case. The reason for this is to ensure that if
+later on enum is modified and one forget to change the switch accordingly, the
+compiler will complain the new added type hasn't been handled.
+
+Example:
+
+enum animal_type {
+       ANIMAL_TYPE_FOUR_LEGS =         4,
+       ANIMAL_TYPE_EIGHT_LEGS =        8,
+       ANIMAL_TYPE_TWO_LEGS =          2,
+};
+
+enum animal_type t;
+
+switch (t) { // OK
+case ANIMAL_TYPE_FOUR_LEGS:
+       ...
+       break;
+case ANIMAL_TYPE_EIGHT_LEGS:
+       ...
+       break;
+case ANIMAL_TYPE_TWO_LEGS:
+       ...
+       break;
+default:
+       break;
+}
+
+switch (t) { // Wrong
+case ANIMAL_TYPE_FOUR_LEGS:
+       ...
+       break;
+case ANIMAL_TYPE_TWO_LEGS:
+       ...
+       break;
+default:
+       break;
+}
+
+However if the enum comes from an external header file outside BlueZ, such as
+Android headers, we cannot make any assumption of how the enum is defined and
+this rule might not apply.
+
+M11: Always use parenthesis with sizeof
+=======================================
+
+The expression argument to the sizeof operator should always be in
+parenthesis, too.
+
+Example:
+1)
+memset(stuff, 0, sizeof(*stuff));
+
+2)
+memset(stuff, 0, sizeof *stuff); // Wrong
+
+M12: Use void if function has no parameters
+===========================================
+
+A function with no parameters must use void in the parameter list.
+
+Example:
+1)
+void foo(void)
+{
+}
+
+2)
+void foo()     // Wrong
+{
+}
+
+O1: Try to avoid complex if body
+================================
+
+It's better not to have a complicated statement for if. You may judge its
+contrary condition and return | break | continue | goto ASAP.
+
+Example:
+1)
+if (device) {  // worse
+       memset(&eir_data, 0, sizeof(eir_data));
+       if (eir_len > 0)
+               eir_parse(&eir_data, ev->eir, eir_len);
+       ...
+} else {
+       error("Unable to get device object for %s", addr);
+       return;
+}
+
+2)
+if (!device) {
+       error("Unable to get device object for %s", addr);
+       return;
+}
+
+memset(&eir_data, 0, sizeof(eir_data));
+if (eir_len > 0)
+       eir_parse(&eir_data, ev->eir, eir_len);
+...
index 13b2881..d03906c 100755 (executable)
@@ -81,7 +81,11 @@ Methods              void Connect()
                                         org.bluez.Error.InvalidArguments
                                         org.bluez.Error.NotSupported
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               void Pair(uint8 conn_type)
+#else
                void Pair()
+#endif
 
                        This method will connect to the remote device,
                        initiate pairing and then retrieve all SDP records
@@ -99,6 +103,14 @@ Methods             void Connect()
                        In case there is no application agent and also
                        no default agent present, this method will fail.
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                       conn_type represents the type of the connection to be
+                       used for pairing.
+                       0xFF -> BR/EDR and LE
+                       0x00 -> BR/EDR only
+                       0x01 -> LE only
+#endif
+
                        Possible errors: org.bluez.Error.InvalidArguments
                                         org.bluez.Error.Failed
                                         org.bluez.Error.AlreadyExists
@@ -116,6 +128,29 @@ Methods            void Connect()
                        Possible errors: org.bluez.Error.DoesNotExist
                                         org.bluez.Error.Failed
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               void ConnectLe()
+
+                       This is a generic method to connect GATT of
+                       the remote device supporting LE and have been
+                       flagged as auto-connectable on our side.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                        org.bluez.Error.Failed
+                                        org.bluez.Error.InProgress
+                                        org.bluez.Error.AlreadyConnected
+
+
+               void DisconnectLe()
+
+                       This method disconnects a specific remote device LE
+                       connection by terminating the low-level ACL connection.
+                       The use of this method should be restricted to administrator
+                       use.
+
+                       Possible errors: org.bluez.Error.NotConnected
+#endif
+
 Properties     string Address [readonly]
 
                        The Bluetooth device address of the remote device.
@@ -154,7 +189,11 @@ Properties string Address [readonly]
 
                        Indicates if the remote device is paired.
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               byte Connected [readonly]
+#else
                boolean Connected [readonly]
+#endif
 
                        Indicates if the remote device is currently connected.
                        A PropertiesChanged signal indicate changes to this
@@ -234,3 +273,11 @@ Properties string Address [readonly]
                array{byte} AdvertisingFlags [readonly, experimental]
 
                        The Advertising Data Flags of the remote device.
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               boolean GattConnected [readonly]
+
+                       Indicates if the remote LE device is currently connected.
+                       A PropertiesChanged signal indicate changes to this
+                       status.
+#endif
\ No newline at end of file
index 6c98b87..b29f599 100755 (executable)
@@ -45,6 +45,16 @@ Properties   string UUID [read-only]
                        belongs to. Only present on services from remote
                        devices.
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               array{object} Characteristics [read-only]
+
+                       Array of object paths representing the characteristics
+                       of this service. This property is set only when the
+                       characteristic discovery has been completed, however the
+                       characteristic objects will become available via
+                       ObjectManager as soon as they get discovered.
+#endif
+
                array{object} Includes [read-only]: Not implemented
 
                        Array of object paths representing the included
@@ -153,6 +163,22 @@ Properties string UUID [read-only]
                                "secure-read" (Server only)
                                "secure-write" (Server only)
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               array{object} Descriptors [read-only]
+
+                       Array of object paths representing the descriptors
+                       of this service. This property is set only when the
+                       descriptor discovery has been completed, however the
+                       descriptor objects will become available via
+                       ObjectManager as soon as they get discovered.
+
+               string Unicast [read-only]
+
+                       12:34:56:78:9A:BC remote device address, if address is set then
+                       notifications or indications shall be sent to only "XX_XX_XX_XX_XX_XX"
+                       device otherwise notification or indication shall be multicasted.
+#endif
+
 Characteristic Descriptors hierarchy
 ====================================
 
@@ -342,3 +368,21 @@ Methods            void RegisterApplication(object application, dict options)
 
                        Possible errors: org.bluez.Error.InvalidArguments
                                         org.bluez.Error.DoesNotExist
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               GetService(string uuid)
+
+                       This Reads the service, characterstics and descriptors
+                       that has been previously registered. The string uuid parameter
+                       must match the same value that has been used
+                       on registration.
+
+                       The return values includes,
+                       Key               objectpath
+                       ----              -----------
+                       Service           /serviceX
+                       CharacteristicsX  /serviceX/CharacterisitcX
+                       DescriptorX       /serviceX/CharacterisitcX/DescriptorX
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+#endif
diff --git a/doc/maintainer-guidelines.txt b/doc/maintainer-guidelines.txt
new file mode 100755 (executable)
index 0000000..21162d4
--- /dev/null
@@ -0,0 +1,114 @@
+Maintainer guidelines
+*********************
+
+This document is intended for the maintainers of the BlueZ project. It
+serves as basic guidelines for handling patch review and commit access.
+
+
+Rule 1: Keep the GIT tree clean and linear
+==========================================
+
+The bluetooth.git, bluetooth-next.git and bluez.git trees are not your
+private playground. The history is meant to be clean and linear.
+
+       - NO merges
+       - NO branches
+       - NO tags
+
+If anyone needs testing or work on a feature, clone the tree and do
+it in your own copy. The master trees are off limits.
+
+One advise to avoid any accidental errors in this area to set proper
+options in global ~/.gitconfig or local .git/config files.
+
+       [merge]
+               ff = only
+
+Violations of this rule are not acceptable. This rule is enforced. If
+in doubt ask one of the seasoned maintainers.
+
+
+Rule 2: Enforce clean commit messages
+=====================================
+
+The commit messages are required to be clean and follow style guidelines
+to be consistent.
+
+Commit messages should adhere to a 72 characters by line limit. That
+makes it easy to read them via git log in a terminal window. Exceptions
+to this rule are logs, trace or other verbatim copied information.
+
+Every commit requires full names and email addresses. No synonyms or
+nicknames are allowed. It is also important that the Outlook style
+names with lastname, firstname are not allowed. It is the maintainers
+job to ensure we get proper firstname lastname <email> authorship.
+
+It is also important that the committer itself uses a valid name and
+email address when committing patches. So ensure that either the
+global ~/.gitconfig or local .git/config provides proper values.
+
+       [user]
+               name = Peter Mustermann
+               email = peter@mustermann.de
+
+Commit messages for bluez.git shall not contain Signed-off-by
+signatures. They are not used in userspace and with that it is the
+maintainers job to ensure they do not get committed to the repository.
+
+For bluetooth.git and bluetooth-next.git The Signed-off-by process is
+used and the signatures are required.
+
+Tags like Change-Id generated from Gerrit are never acceptable. It is
+the maintainers job to ensure that these are not committed into the
+repositories.
+
+Violations of this rule create a mess in the tree that can not be
+reversed. If in doubt ask one of the seasoned maintainers.
+
+
+Rule 3: Enforce correct coding style
+====================================
+
+The coding style follows roughly the kernel coding style with any
+exceptions documented in doc/coding-style.txt.
+
+To ensure trivial white-space errors don't get committed, have the
+following in your .gitconfig:
+
+       [apply]
+               whitespace = error
+
+It can also be helpful to use the checkpatch.pl script coming with the
+Linux kernel to do some automated checking. Adding the following to your
+.git/hooks/pre-commit and .git/hooks/pre-applypatch is a simple way to
+do this:
+
+       exec git diff --cached | ~/src/linux/scripts/checkpatch.pl -q \
+               --no-tree --no-signoff --show-types \
+               --ignore CAMELCASE,NEW_TYPEDEFS,INITIALISED_STATIC -
+
+The above assumes that a kernel tree resides in ~/src/linux/.
+
+
+Rule 4: Pay extra attention to adding new files to the tree
+===========================================================
+
+New files that are added to the tree require several things to be
+verified first:
+
+       - Check that the names are acceptible with other maintainers
+       - Ensure that the file modes are correct
+       - Verify that the license & copyright headers are correct
+       - If the file is supposed to be part of the release tarball,
+         make sure that it gets picked up by 'make dist' (particularly
+         important for documentation or other files that are not code)
+
+
+Rule 5: Keep the mailing list in sync with the commit process
+=============================================================
+
+When applying patches, be sure to send a response to the mailing list as
+soon as the code has been pushed to the upstream tree. Usually this
+means one email per patch, however patch-sets may only have one response
+covering the entire set. If applying a subset of a patch-set clearly
+state what was applied in your response.
index 34270dd..6690752 100755 (executable)
@@ -1907,6 +1907,68 @@ Load Identity Resolving Keys Command
        Possible errors:        Invalid Parameters
                                Invalid Index
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+Generate Identity Resolving Key Command
+=======================================
+
+       Command Code:           0x00F6
+       Controller Index:       <controller id>
+       Command Parameters:
+       Return Parameters:
+
+       This command is used to generate identity resolving key for the
+       local device, which will be used at the time of pairing for key
+       distribution and to     generate resolvable private address for local device.
+
+       This command generates a Command Complete event on success
+       or a Command Status event on failure.
+
+       Possible errors:        Failed
+                               Invalid Index
+
+Generate Resolvable Private Address Command
+=======================================
+
+       Command Code:           0x00F5
+       Controller Index:       <controller id>
+       Command Parameters:
+       Return Parameters:      Resolvable Private Address (6 Octets)
+
+       This command is used to generate resolvable private address for the
+       local device when LE privacy is supported and device is having its IRK.
+
+       This command returns generated private address which is resolvable
+       by remote device on success.
+
+       This command generates a Command Complete event on success
+       or a Command Status event on failure.
+
+       Possible errors:        Failed
+                               Invalid Index
+
+Set Random Address Command
+==========================
+
+       Command Code:           0x00F4
+       Controller Index:       <controller id>
+       Command Parameters:     Resolvable Private Address (6 Octets)
+       Return Parameters:
+
+       This command is used to set the random address to local controller.
+       If local device supports LE Privacy then this command will be called to set
+       its Random address while active scanning remote devices and in the case of
+       advertising itself.
+
+       This command intern calls the HCI Set Random address command from kernel
+       space to set the RPA to controller.
+
+       This command generates a Command Complete event on success
+       or a Command Status event on failure.
+
+       Possible errors:        Failed
+                               Invalid Index
+
+#endif
 
 Get Connection Information Command
 ==================================
index 109da28..0fb633c 100755 (executable)
@@ -74,3 +74,17 @@ Methods              void Register(string uuid, string bridge)
 
                        All servers will be automatically unregistered when
                        the calling application terminates.
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               void Disconnect(string address)
+
+                       Disconnect the device from the network device.
+
+                       Possible errors: org.bluez.Error.Failed
+                                        org.bluez.Error.NotConnected
+
+               dict GetProperties(string address)
+
+                       Returns all properties of the specified device.
+                       See the properties section for available properties.
+#endif
diff --git a/doc/oob-api.txt b/doc/oob-api.txt
new file mode 100755 (executable)
index 0000000..d838712
--- /dev/null
@@ -0,0 +1,38 @@
+BlueZ D-Bus Out Of Band Pairing API description
+===============================================
+
+Copyright (C) 2011  Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+
+Service                org.bluez
+Interface      org.bluez.OutOfBand
+Object path    [variable prefix]/{hci0,hci1,...}
+
+Methods                array{byte} hash, array{byte} randomizer ReadLocalData()
+
+                       This method reads local OOB data from adapter. Return
+                       value is pair of arrays 16 bytes each.
+
+                       Note: This method will generate and return new local
+                       OOB data.
+
+                       Possible errors: org.bluez.Error.Failed
+                                        org.bluez.Error.InProgress
+
+               void AddRemoteData(string address, array{byte} hash,
+                                                       array{byte} randomizer)
+
+                       This method adds new Out Of Band data for
+                       specified address. If data for specified address
+                       already exists it will be overwritten with new one.
+
+                       Possible errors: org.bluez.Error.Failed
+                                        org.bluez.Error.InvalidArguments
+
+               void RemoveRemoteData(string address)
+
+                       This method removes Out Of Band data for specified
+                       address. If data for specified address does not exist
+                       nothing is removed.
+
+                       Possible errors: org.bluez.Error.Failed
+                                        org.bluez.Error.InvalidArguments
index ec18034..60eee2b 100755 (executable)
@@ -91,7 +91,100 @@ Object path /org/bluez
 
                        Possible errors: org.bluez.Error.InvalidArguments
                                         org.bluez.Error.AlreadyExists
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               void RegisterProfile2(object profile, string uuid, string destination,
+               string path,dict options)
 
+                       This registers a profile implementation.
+
+                       If an application disconects/exits, its registered profile
+                       will not be removed and bluez launches an application through
+                       dbus activation when profile is connected.
+
+                       HFP HS UUID: 0000111e-0000-1000-8000-00805f9b34fb
+
+                               Default RFCOMM channel is 6. And this requires
+                               authentication.
+
+                       string Destination
+
+                               Application bus name
+
+                       string Path
+
+                               Applicatoin path name
+
+                       Available options:
+
+                               string Name
+
+                                       Human readable name for the profile
+
+                               string Service
+
+                                       The primary service class UUID
+                                       (if different from the actual
+                                        profile UUID)
+
+                               string Role
+
+                                       For asymmetric profiles that do not
+                                       have UUIDs available to uniquely
+                                       identify each side this
+                                       parameter allows specifying the
+                                       precise local role.
+
+                                       Possible values: "client", "server"
+
+                               uint16 Channel
+
+                                       RFCOMM channel number that is used
+                                       for client and server UUIDs.
+
+                                       If applicable it will be used in the
+                                       SDP record as well.
+
+                               uint16 PSM
+
+                                       PSM number that is used for client
+                                       and server UUIDs.
+
+                                       If applicable it will be used in the
+                                       SDP record as well.
+
+                               boolean RequireAuthentication
+
+                                       Pairing is required before connections
+                                       will be established. No devices will
+                                       be connected if not paired.
+
+                               boolean RequireAuthorization
+
+                                       Request authorization before any
+                                       connection will be established.
+
+                               boolean AutoConnect
+
+                                       In case of a client UUID this will
+                                       force connection of the RFCOMM or
+                                       L2CAP channels when a remote device
+                                       is connected.
+
+                               string ServiceRecord
+
+                                       Provide a manual SDP record.
+
+                               uint16 Version
+
+                                       Profile version (for SDP record)
+
+                               uint16 Features
+
+                                       Profile features (for SDP record)
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.AlreadyExists
+#endif
                void UnregisterProfile(object profile)
 
                        This unregisters the profile that has been previously
index 6a708b4..bbe525a 100755 (executable)
@@ -81,13 +81,23 @@ Settings file contains one [General] group with adapter info like:
                                        0 = disable timer, i.e. stay
                                        discoverable forever
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+  LocalIrk             String          Identity resolving key for local
+                                       adapter. This key value is used to
+                                       generate the RPA to suport privacy feature.
+                                       If value is NULL, i.e IRK to be generated
+                                       for this adapter.
+#endif
+
 Sample:
   [General]
   Name=My PC
   Discoverable=false
   Pairable=true
   DiscoverableTimeout=0
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+  LocalIrk=""
+#endif
 
 Identity file format
 ====================
@@ -294,3 +304,13 @@ Long term key) related to a remote device.
   Counter              Integer         Signing counter
 
   Authenticated                Boolean         True if the key is authenticated
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+[IdentityResolvingKey] group contains:
+
+  Key                  String          Identity Resolving key in hexadecimal format
+
+  IdentityAddress      String          Identity Address of the device
+
+  IdentityAddressType  String          Type of Identity Address of the device
+#endif
index e37385f..e40dd44 100755 (executable)
@@ -277,6 +277,12 @@ gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
                                const char *path, const char *interface,
                                const char *name, int type, va_list args);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+gboolean g_dbus_emit_signal_to_dest(DBusConnection *connection,
+                               const char *dest, const char *path,
+                               const char *interface, const char *name, int type, ...);
+#endif
+
 guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
                                GDBusWatchFunction connect,
                                GDBusWatchFunction disconnect,
index afb4587..bd80060 100755 (executable)
 
 #include "gdbus.h"
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#if 0
+#include <syslog.h>
+static void gdbus_dbg(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_DEBUG, format, ap);
+
+       va_end(ap);
+}
+#endif
+#else
 #define info(fmt...)
+#endif
+
 #define error(fmt...)
 #define debug(fmt...)
 
@@ -88,6 +105,12 @@ static int global_flags = 0;
 static struct generic_data *root;
 static GSList *pending = NULL;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define ADAPTER_INTERFACE      "org.bluez.Adapter1"
+
+static char *adapter_path = NULL;
+#endif
+
 static gboolean process_changes(gpointer user_data);
 static void process_properties_from_interface(struct generic_data *data,
                                                struct interface_data *iface);
@@ -663,6 +686,13 @@ static gboolean remove_interface(struct generic_data *data, const char *name)
 
        data->interfaces = g_slist_remove(data->interfaces, iface);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (g_strcmp0(iface->name, ADAPTER_INTERFACE) == 0) {
+               g_free(adapter_path);
+               adapter_path = NULL;
+       }
+#endif
+
        if (iface->destroy) {
                iface->destroy(iface->user_data);
                iface->user_data = NULL;
@@ -1075,7 +1105,12 @@ static DBusHandlerResult generic_message(DBusConnection *connection,
                if (check_privilege(connection, message, method,
                                                iface->user_data) == TRUE)
                        return DBUS_HANDLER_RESULT_HANDLED;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#if 0
+               gdbus_dbg("%s: %s.%s()", dbus_message_get_path(message),
+                                                       iface->name, method->name);
+#endif
+#endif
                return process_message(connection, message, method,
                                                        iface->user_data);
        }
@@ -1165,9 +1200,36 @@ static DBusMessage *get_objects(DBusConnection *connection,
        return reply;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *default_adapter(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+
+       if (!adapter_path)
+               return g_dbus_create_error(msg,
+                               "org.bluez.Error" ".NoSuchAdapter",
+                               "No such adapter");
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &adapter_path,
+                               DBUS_TYPE_INVALID);
+
+       return reply;
+}
+#endif
+
 static const GDBusMethodTable manager_methods[] = {
        { GDBUS_METHOD("GetManagedObjects", NULL,
                GDBUS_ARGS({ "objects", "a{oa{sa{sv}}}" }), get_objects) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { GDBUS_METHOD("DefaultAdapter",
+                       NULL, GDBUS_ARGS({ "adapter", "o" }),
+                       default_adapter) },
+#endif
        { }
 };
 
@@ -1355,6 +1417,11 @@ gboolean g_dbus_register_interface(DBusConnection *connection,
                return FALSE;
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (g_strcmp0(name, ADAPTER_INTERFACE) == 0)
+               adapter_path = g_strdup(path);
+#endif
+
        if (!add_interface(data, name, methods, signals, properties, user_data,
                                                                destroy)) {
                object_path_unref(connection, path);
@@ -1615,6 +1682,65 @@ gboolean g_dbus_emit_signal(DBusConnection *connection,
        return result;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean g_dbus_emit_signal_valist_to_dest(DBusConnection *connection,
+                               const char *dest, const char *path, const char *interface,
+                               const char *name, int type, va_list args)
+{
+       DBusMessage *signal;
+       dbus_bool_t ret;
+       const GDBusArgInfo *args_info;
+
+       if (!check_signal(connection, path, interface, name, &args_info))
+               return FALSE;
+
+       signal = dbus_message_new_signal(path, interface, name);
+       if (signal == NULL) {
+               error("Unable to allocate new %s.%s signal", interface,  name);
+               return FALSE;
+       }
+
+       ret = dbus_message_append_args_valist(signal, type, args);
+       if (!ret)
+               goto fail;
+
+       if (g_dbus_args_have_signature(args_info, signal) == FALSE) {
+               error("%s.%s: got unexpected signature '%s'", interface, name,
+                                       dbus_message_get_signature(signal));
+               ret = FALSE;
+               goto fail;
+       }
+
+       ret = dbus_message_set_destination(signal, dest);
+       if (!ret)
+               error("Fail to set destination");
+
+       return g_dbus_send_message(connection, signal);
+
+fail:
+       dbus_message_unref(signal);
+
+       return ret;
+}
+
+gboolean g_dbus_emit_signal_to_dest(DBusConnection *connection,
+                               const char *dest, const char *path,
+                               const char *interface, const char *name, int type, ...)
+{
+       va_list args;
+       gboolean result;
+
+       va_start(args, type);
+
+       result = g_dbus_emit_signal_valist_to_dest(connection, dest, path,
+                                               interface, name, type, args);
+
+       va_end(args);
+
+       return result;
+}
+#endif
+
 gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
                                const char *path, const char *interface,
                                const char *name, int type, va_list args)
index 4328172..6f7a952 100755 (executable)
@@ -104,8 +104,10 @@ GObexApparam *g_obex_apparam_decode(const void *data, gsize size)
        GHashTable *tags;
        gsize count = 0;
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (size < 2)
                return NULL;
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
 
        apparam = g_obex_apparam_new();
 
@@ -166,6 +168,13 @@ gssize g_obex_apparam_encode(GObexApparam *apparam, void *buf, gsize len)
        return count;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void g_obex_apparam_remove_all(GObexApparam *apparam)
+{
+       g_hash_table_remove_all(apparam->tags);
+}
+#endif
+
 GObexApparam *g_obex_apparam_set_bytes(GObexApparam *apparam, guint8 id,
                                                const void *value, gsize len)
 {
index 6c08609..701fd43 100755 (executable)
@@ -30,6 +30,10 @@ typedef struct _GObexApparam GObexApparam;
 GObexApparam *g_obex_apparam_decode(const void *data, gsize size);
 gssize g_obex_apparam_encode(GObexApparam *apparam, void *buf, gsize size);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void g_obex_apparam_remove_all(GObexApparam *apparam);
+#endif
+
 GObexApparam *g_obex_apparam_set_bytes(GObexApparam *apparam, guint8 id,
                                                const void *value, gsize size);
 GObexApparam *g_obex_apparam_set_uint8(GObexApparam *apparam, guint8 id,
index c594999..c6634c7 100755 (executable)
@@ -389,6 +389,11 @@ GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str)
        GObexHeader *header;
        gsize len;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       gunichar2 *utf16;
+       glong utf16_len;
+#endif
+
        g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id));
 
        if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_UNICODE)
@@ -401,9 +406,15 @@ GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str)
        len = g_utf8_strlen(str, -1);
 
        header->vlen = len;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        header->hlen = len == 0 ? 3 : 3 + ((len + 1) * 2);
        header->v.string = g_strdup(str);
-
+#else
+       header->v.string = g_strdup(str);
+       utf16_len = utf8_to_utf16(&utf16, header->v.string);
+       header->hlen = len == 0 ? 3 : 3 + utf16_len;
+       g_free(utf16);
+#endif
        g_obex_debug(G_OBEX_DEBUG_HEADER, "%s", header->v.string);
 
        return header;
index 42175fc..61ddc66 100755 (executable)
@@ -571,7 +571,12 @@ static guint8 *digest_response(const guint8 *nonce)
                return result;
 
        g_checksum_update(md5, nonce, NONCE_LEN);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       g_checksum_update(md5, (guint8 *) ":0000", 5);
+#else
        g_checksum_update(md5, (guint8 *) ":BlueZ", 6);
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
 
        size = NONCE_LEN;
        g_checksum_get_digest(md5, result, &size);
index eb27926..f70ff09 100755 (executable)
@@ -120,6 +120,16 @@ struct bt_voice {
 #define BT_SNDMTU              12
 #define BT_RCVMTU              13
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define BT_LE_CONN_PARAM       14
+struct le_conn_param {
+       uint16_t min;
+       uint16_t max;
+       uint16_t latency;
+       uint16_t to_multiplier;
+};
+#endif
+
 #define BT_VOICE_TRANSPARENT                   0x0003
 #define BT_VOICE_CVSD_16BIT                    0x0060
 
index a3f5d96..4dd92b8 100755 (executable)
--- a/lib/hci.c
+++ b/lib/hci.c
@@ -160,8 +160,13 @@ char *hci_bustostr(int bus)
                return "SPI";
        case HCI_I2C:
                return "I2C";
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       case HCI_SMD:
+               return "QC_SMD";
+#else
        case HCI_SMD:
                return "SMD";
+#endif
        default:
                return "Unknown";
        }
@@ -594,7 +599,12 @@ static hci_map commands_map[] = {
        { "LE Receiver Test",                           228 },
        { "LE Transmitter Test",                        229 },
        { "LE Test End",                                230 },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { "LE Read Maximum Data Length",                231 },
+       { "Reserved",                                   232 },
+#else
        { "Reserved",                                   231 },
+#endif
 
        { NULL }
 };
@@ -1086,6 +1096,38 @@ int hci_close_dev(int dd)
 /* HCI functions that require open device
  * dd - Device descriptor returned by hci_open_dev. */
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int hci_send_data(int dd, uint16_t handle, uint8_t len, void *data)
+{
+       uint8_t type = HCI_ACLDATA_PKT;
+       hci_acl_hdr ac;
+       struct iovec iv[3];
+       int ivn;
+
+       ac.handle = htobs(handle);
+       ac.dlen= htobs(len);
+
+       iv[0].iov_base = &type;
+       iv[0].iov_len  = 1;
+       iv[1].iov_base = &ac;
+       iv[1].iov_len  = HCI_ACL_HDR_SIZE;
+       ivn = 2;
+
+       if (len) {
+               iv[2].iov_base = data;
+               iv[2].iov_len  = len;
+               ivn = 3;
+       }
+
+       while (writev(dd, iv, ivn) < 0) {
+               if (errno == EAGAIN || errno == EINTR)
+                       continue;
+               return -1;
+       }
+       return 0;
+}
+#endif
+
 int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param)
 {
        uint8_t type = HCI_COMMAND_PKT;
@@ -1604,7 +1646,11 @@ int hci_write_local_name(int dd, const char *name, int to)
        struct hci_request rq;
 
        memset(&cp, 0, sizeof(cp));
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       strncpy((char *) cp.name, name, sizeof(cp.name) - 1);
+#else
        strncpy((char *) cp.name, name, sizeof(cp.name));
+#endif
 
        memset(&rq, 0, sizeof(rq));
        rq.ogf    = OGF_HOST_CTL;
@@ -3119,3 +3165,132 @@ int hci_le_read_remote_features(int dd, uint16_t handle, uint8_t *features, int
 
        return 0;
 }
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int hci_le_read_maximum_data_length(
+       int dd, uint8_t *status, uint16_t *tx_octets,
+       uint16_t *tx_time, uint16_t *rx_octets,
+       uint16_t *rx_time, int to)
+{
+       le_read_maximum_data_length_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       memset(&rp, 0, sizeof(rp));
+
+       rq.ogf = OGF_LE_CTL;
+       rq.ocf = OCF_LE_READ_MAXIMUM_DATA_LENGTH;
+       rq.rparam = &rp;
+       rq.rlen = LE_READ_MAXIMUM_DATA_LENGTH_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *tx_octets = rp.max_tx_octets;
+       *tx_time = rp.max_tx_time;
+       *rx_octets = rp.max_rx_octets;
+       *rx_time = rp.max_rx_time;
+       *status = rp.status;
+       return 0;
+}
+
+int hci_le_write_host_suggested_data_length(
+               int dd, uint16_t *def_tx_octets,
+               uint16_t *def_tx_time, int to)
+{
+       le_write_host_suggested_data_length_cp cp;
+       struct hci_request rq;
+       uint8_t status;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.def_tx_octets = *def_tx_octets;
+       cp.def_tx_time = *def_tx_time;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf = OGF_LE_CTL;
+       rq.ocf = OCF_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH;
+       rq.cparam = &cp;
+       rq.clen = LE_WRITE_HOST_SUGGESTED_DATA_LENGTH_CP_SIZE;
+       rq.rparam = &status;
+       rq.rlen = 1;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_le_read_host_suggested_data_length(
+       int dd, uint8_t *status, uint16_t *def_tx_octets,
+       uint16_t *def_tx_time, int to)
+{
+       le_read_host_suggested_data_length_rp rp;
+       struct hci_request rq;
+
+       memset(&rp, 0, sizeof(rp));
+       memset(&rq, 0, sizeof(rq));
+
+       rq.ogf = OGF_LE_CTL;
+       rq.ocf = OCF_LE_READ_HOST_SUGGESTED_DATA_LENGTH;
+       rq.rparam = &rp;
+       rq.rlen = LE_READ_HOST_SUGGESTED_DATA_LENGTH_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *def_tx_octets = rp.def_tx_octets;
+       *def_tx_time = rp.def_tx_time;
+       *status = rp.status;
+       return 0;
+}
+
+int hci_le_set_data_length(
+               int dd, const bdaddr_t *bdaddr, uint16_t *max_tx_octets,
+               uint16_t *max_tx_time, int to)
+{
+       le_set_data_length_cp cp;
+       le_set_data_length_rp rp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       memset(&rp, 0, sizeof(rp));
+
+       bacpy(&cp.bdaddr, bdaddr);
+       cp.max_tx_octets = *max_tx_octets;
+       cp.max_tx_time = *max_tx_time;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf = OGF_LE_CTL;
+       rq.ocf = OCF_LE_SET_DATA_LENGTH;
+       rq.cparam = &cp;
+       rq.clen = LE_SET_DATA_LENGTH_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen = LE_SET_DATA_LENGTH_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+#endif
index 794333b..061580b 100755 (executable)
--- a/lib/hci.h
+++ b/lib/hci.h
@@ -1710,6 +1710,52 @@ typedef struct {
 } __attribute__ ((packed)) le_test_end_rp;
 #define LE_TEST_END_RP_SIZE 3
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define OCF_LE_READ_MAXIMUM_DATA_LENGTH        0x002F
+typedef struct {
+       uint8_t status;
+       uint16_t max_tx_octets;
+       uint16_t max_tx_time;
+       uint16_t max_rx_octets;
+       uint16_t max_rx_time;
+} __attribute__ ((packed))
+le_read_maximum_data_length_rp;
+#define LE_READ_MAXIMUM_DATA_LENGTH_SIZE 9
+
+#define OCF_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH        0x0030
+typedef struct {
+       uint16_t def_tx_octets;
+       uint16_t def_tx_time;
+} __attribute__ ((packed))
+le_write_host_suggested_data_length_cp;
+#define LE_WRITE_HOST_SUGGESTED_DATA_LENGTH_CP_SIZE 4
+
+#define OCF_LE_READ_HOST_SUGGESTED_DATA_LENGTH 0x0024
+typedef struct {
+       uint8_t status;
+       uint16_t def_tx_octets;
+       uint16_t def_tx_time;
+} __attribute__ ((packed))
+le_read_host_suggested_data_length_rp;
+#define LE_READ_HOST_SUGGESTED_DATA_LENGTH_SIZE 5
+
+#define OCF_LE_SET_DATA_LENGTH 0x0022
+typedef struct {
+       bdaddr_t bdaddr;
+       uint16_t max_tx_octets;
+       uint16_t max_tx_time;
+} __attribute__ ((packed))
+le_set_data_length_cp;
+#define LE_SET_DATA_LENGTH_CP_SIZE 10
+
+typedef struct {
+       uint16_t handle;
+       uint8_t status;
+} __attribute__ ((packed))
+le_set_data_length_rp;
+#define LE_SET_DATA_LENGTH_RP_SIZE 3
+#endif
+
 #define OCF_LE_ADD_DEVICE_TO_RESOLV_LIST       0x0027
 typedef struct {
        uint8_t         bdaddr_type;
@@ -1744,6 +1790,17 @@ typedef struct {
 /* Vendor specific commands */
 #define OGF_VENDOR_CMD         0x3f
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define BRCM_QOS_PRIORITY_NORMAL       0x00
+#define BRCM_QOS_PRIORITY_HIGH         0xFF
+#define BROADCOM_QOS_CMD       0xFC57  /* Only for bcm4329/bcm4330/bcm4334 chipsets */
+typedef struct {
+        uint16_t handle;
+        uint8_t priority;
+} __attribute__ ((__packed__)) broadcom_qos_cp;
+#define BROADCOM_QOS_CP_SIZE 3
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
 /* ---- HCI Events ---- */
 
 #define EVT_INQUIRY_COMPLETE           0x01
index 55aeb17..ff79599 100755 (executable)
@@ -50,6 +50,9 @@ struct hci_version {
 
 int hci_open_dev(int dev_id);
 int hci_close_dev(int dd);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int hci_send_data(int dd, uint16_t handle, uint8_t len, void *data);
+#endif
 int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param);
 int hci_send_req(int dd, struct hci_request *req, int timeout);
 
@@ -141,7 +144,18 @@ int hci_le_clear_resolving_list(int dd, int to);
 int hci_le_read_resolving_list_size(int dd, uint8_t *size, int to);
 int hci_le_set_address_resolution_enable(int dev_id, uint8_t enable, int to);
 int hci_le_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int hci_le_read_maximum_data_length(
+       int dd, uint8_t *status, uint16_t *tx_octets,
+       uint16_t *tx_time, uint16_t *rx_octets,
+       uint16_t *rx_time, int to );
+int hci_le_write_host_suggested_data_length(int dd, uint16_t *def_tx_octets,
+       uint16_t *def_tx_time, int to);
+int hci_le_read_host_suggested_data_length(int dd, uint8_t *status,
+       uint16_t *def_tx_octets, uint16_t *def_tx_time, int to);
+int hci_le_set_data_length(int dd, const bdaddr_t *bdaddr,
+               uint16_t *max_tx_octets, uint16_t *max_tx_time, int to);
+#endif
 int hci_for_each_dev(int flag, int(*func)(int dd, int dev_id, long arg), long arg);
 int hci_get_route(bdaddr_t *bdaddr);
 
index 5ce94c4..62b8c33 100755 (executable)
@@ -182,6 +182,18 @@ typedef struct {
 } __attribute__ ((packed)) l2cap_conf_opt;
 #define L2CAP_CONF_OPT_SIZE 2
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+typedef struct {
+       uint8_t    mode;
+       uint8_t    txwin_size;
+       uint8_t    max_transmit;
+       uint16_t   retrans_timeout;
+       uint16_t   monitor_timeout;
+       uint16_t   max_pdu_size;
+} __attribute__ ((packed)) l2cap_conf_rfc ;
+#define L2CAP_CONF_RFC_SIZE 9
+#endif
+
 #define L2CAP_CONF_MTU         0x01
 #define L2CAP_CONF_FLUSH_TO    0x02
 #define L2CAP_CONF_QOS         0x03
index 798a05e..2a7b787 100755 (executable)
@@ -900,21 +900,361 @@ static const char *mgmt_status[] = {
        "Permission Denied",
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+
+#define TIZEN_OP_CODE_BASE     0xff00
+#define TIZEN_EV_BASE          0xff00
+
+#define MGMT_MAX_ADVERTISING_LENGTH            31
+
+#define MGMT_MAX_EIR_MANUFACTURER_DATA_LENGTH  100
+
+#define MGMT_IRK_SIZE                          16
+
+#define MGMT_OP_SET_ADVERTISING_PARAMS         (TIZEN_OP_CODE_BASE + 0x01)
+struct mgmt_cp_set_advertising_params {
+       uint16_t interval_min;
+       uint16_t interval_max;
+       uint8_t filter_policy;
+       uint8_t type;
+} __packed;
+
+#define MGMT_OP_SET_ADVERTISING_DATA           (TIZEN_OP_CODE_BASE + 0x02)
+struct mgmt_cp_set_advertising_data {
+       uint8_t data[MGMT_MAX_ADVERTISING_LENGTH];
+} __packed;
+
+#define MGMT_OP_SET_SCAN_RSP_DATA              (TIZEN_OP_CODE_BASE + 0x03)
+struct mgmt_cp_set_scan_rsp_data {
+       uint8_t data[MGMT_MAX_ADVERTISING_LENGTH];
+} __packed;
+
+#define MGMT_OP_ADD_DEV_WHITE_LIST             (TIZEN_OP_CODE_BASE + 0x04)
+struct mgmt_cp_add_dev_white_list {
+       uint8_t bdaddr_type;
+       bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST     (TIZEN_OP_CODE_BASE + 0x05)
+struct mgmt_cp_remove_dev_white_list {
+       uint8_t bdaddr_type;
+       bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_OP_CLEAR_DEV_WHITE_LIST           (TIZEN_OP_CODE_BASE + 0x06)
+
+/* BEGIN TIZEN_Bluetooth :: RSSI monitoring   */
+#define MGMT_OP_SET_RSSI_ENABLE                        (TIZEN_OP_CODE_BASE + 0x07)
+struct mgmt_cp_set_enable_rssi {
+       int8_t     low_th;
+       int8_t     in_range_th;
+       int8_t     high_th;
+       bdaddr_t   bdaddr;
+       int8_t link_type;
+} __packed;
+
+struct mgmt_cc_rsp_enable_rssi {
+       uint8_t status;
+       uint8_t le_ext_opcode;
+       bdaddr_t bt_address;
+       int8_t link_type;
+} __packed;
+
+#define MGMT_OP_GET_RAW_RSSI                   (TIZEN_OP_CODE_BASE + 0x08)
+struct mgmt_cp_get_raw_rssi {
+       bdaddr_t bt_address;
+       uint8_t  link_type;
+} __packed;
+struct mgmt_cc_rp_get_raw_rssi {
+       uint8_t    status;
+       int8_t     rssi_dbm;
+       uint8_t    link_type;
+       bdaddr_t   bt_address;
+} __packed;
+
+#define MGMT_OP_SET_RSSI_DISABLE               (TIZEN_OP_CODE_BASE + 0x09)
+struct mgmt_cp_disable_rssi {
+       bdaddr_t   bdaddr;
+       int8_t link_type;
+} __packed;
+struct mgmt_cc_rp_disable_rssi {
+       uint8_t status;
+       uint8_t le_ext_opcode;
+       bdaddr_t bt_address;
+       int8_t link_type;
+} __packed;
+/* END TIZEN_Bluetooth :: RSSI monitoring */
+
+#define MGMT_OP_START_LE_DISCOVERY             (TIZEN_OP_CODE_BASE + 0x0a)
+struct mgmt_cp_start_le_discovery {
+       uint8_t type;
+} __packed;
+
+#define MGMT_OP_STOP_LE_DISCOVERY              (TIZEN_OP_CODE_BASE + 0x0b)
+struct mgmt_cp_stop_le_discovery {
+       uint8_t type;
+} __packed;
+
+#define MGMT_OP_LE_CONN_UPDATE                 (TIZEN_OP_CODE_BASE + 0x0d)
+struct mgmt_cp_le_conn_update {
+       uint16_t interval_min;
+       uint16_t interval_max;
+       uint16_t latency;
+       uint16_t supervision_time_out;
+       bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_OP_SET_MANUFACTURER_DATA          (TIZEN_OP_CODE_BASE + 0x0e)
+struct mgmt_cp_set_manufacturer_data {
+       uint8_t data[MGMT_MAX_EIR_MANUFACTURER_DATA_LENGTH];
+} __packed;
+
+#define MGMT_OP_LE_SET_SCAN_PARAMS             (TIZEN_OP_CODE_BASE + 0x0f)
+struct mgmt_cp_le_set_scan_params {
+       uint8_t type;   /* le scan type */
+       uint16_t interval;
+       uint16_t window;
+} __packed;
+
+#define MGMT_SCO_ROLE_HANDSFREE                        0x00
+#define MGMT_SCO_ROLE_AUDIO_GATEWAY            0x01
+#define MGMT_OP_SET_VOICE_SETTING              (TIZEN_OP_CODE_BASE + 0x10)
+struct mgmt_cp_set_voice_setting {
+       bdaddr_t bdaddr;
+       uint8_t  sco_role;
+       uint16_t voice_setting;
+} __packed;
+
+#define MGMT_OP_GET_ADV_TX_POWER               (TIZEN_OP_CODE_BASE + 0x11)
+struct mgmt_rp_get_adv_tx_power {
+       int8_t adv_tx_power;
+} __packed;
+
+#define MGMT_OP_ENABLE_6LOWPAN         (TIZEN_OP_CODE_BASE + 0x12)
+struct mgmt_cp_enable_6lowpan {
+       uint8_t enable_6lowpan;
+} __packed;
+
+#define MGMT_OP_CONNECT_6LOWPAN        (TIZEN_OP_CODE_BASE + 0x13)
+struct mgmt_cp_connect_6lowpan {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_DISCONNECT_6LOWPAN     (TIZEN_OP_CODE_BASE + 0x14)
+struct mgmt_cp_disconnect_6lowpan {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH    (TIZEN_OP_CODE_BASE + 0x15)
+struct mgmt_rp_le_read_maximum_data_length {
+       uint8_t status;
+       uint16_t max_tx_octets;
+       uint16_t max_tx_time;
+       uint16_t max_rx_octets;
+       uint16_t max_rx_time;
+} __packed;
+
+#define MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH    (TIZEN_OP_CODE_BASE + 0x16)
+struct mgmt_cp_le_write_host_suggested_data_length {
+       uint16_t def_tx_octets;
+       uint16_t def_tx_time;
+} __packed;
+
+#define MGMT_OP_LE_READ_HOST_SUGGESTED_DATA_LENGTH     (TIZEN_OP_CODE_BASE + 0x17)
+struct mgmt_rp_le_read_host_suggested_data_length {
+       uint8_t status;
+       uint16_t def_tx_octets;
+       uint16_t def_tx_time;
+} __packed;
+
+#define MGMT_OP_LE_SET_DATA_LENGTH     (TIZEN_OP_CODE_BASE + 0x18)
+struct mgmt_cp_le_set_data_length {
+       bdaddr_t bdaddr;
+       uint16_t max_tx_octets;
+       uint16_t max_tx_time;
+} __packed;
+#define MGMT_LE_SET_DATA_LENGTH_SIZE    10
+
+#define MGMT_OP_SET_IRK                        (TIZEN_OP_CODE_BASE + 0x19)
+struct mgmt_cp_set_irk {
+       uint8_t irk[16];
+} __packed;
+
+#define MGMT_OP_SET_DEV_RPA_RES_SUPPORT        (TIZEN_OP_CODE_BASE + 0x1a)
+struct mgmt_cp_set_dev_rpa_res_support {
+       struct mgmt_addr_info addr;
+       uint8_t res_support;
+} __packed;
+
+/* BEGIN TIZEN_Bluetooth :: name update changes */
+#define MGMT_EV_DEVICE_NAME_UPDATE             (TIZEN_EV_BASE + 0x01)
+struct mgmt_ev_device_name_update {
+       struct mgmt_addr_info addr;
+       uint16_t eir_len;
+       uint8_t eir[0];
+} __packed;
+/* END TIZEN_Bluetooth :: name update changes */
+
+/* BEGIN TIZEN_Bluetooth :: Add handling of hardware error event   */
+#define MGMT_EV_HARDWARE_ERROR                 (TIZEN_EV_BASE + 0x02)
+struct mgmt_ev_hardware_error{
+       uint8_t error_code;
+} __packed;
+/* END TIZEN_Bluetooth */
+
+/* BEGIN TIZEN_Bluetooth :: HCI TX Timeout Error   */
+#define MGMT_EV_TX_TIMEOUT_ERROR               (TIZEN_EV_BASE + 0x03)
+/* END TIZEN_Bluetooth */
+
+/* BEGIN TIZEN_Bluetooth :: Add handling of RSSI Events   */
+#define MGMT_EV_RSSI_ALERT                     (TIZEN_EV_BASE + 0x04)
+struct mgmt_ev_vendor_specific_rssi_alert {
+       bdaddr_t bdaddr;
+       int8_t     link_type;
+       int8_t     alert_type;
+       int8_t     rssi_dbm;
+} __packed;
+
+#define MGMT_EV_RAW_RSSI                       (TIZEN_EV_BASE + 0x05)
+
+#define MGMT_EV_RSSI_ENABLED                   (TIZEN_EV_BASE + 0x06)
+
+#define MGMT_EV_RSSI_DISABLED                  (TIZEN_EV_BASE + 0x07)
+/* END TIZEN_Bluetooth :: Handling of RSSI Events */
+
+/* BEGIN TIZEN_Bluetooth :: Add LE connection update Events   */
+#define MGMT_EV_CONN_UPDATED                   (TIZEN_EV_BASE + 0x08)
+struct mgmt_ev_conn_updated {
+       struct  mgmt_addr_info addr;
+       uint16_t        conn_interval;
+       uint16_t        conn_latency;
+       uint16_t        supervision_timeout;
+} __packed;
+
+#define MGMT_EV_CONN_UPDATE_FAILED             (TIZEN_EV_BASE + 0x09)
+struct mgmt_ev_conn_update_failed {
+       struct  mgmt_addr_info addr;
+       uint8_t status;
+} __packed;
+/* END TIZEN_Bluetooth :: Add LE connection update Events */
+
+#define MGMT_EV_LE_DEVICE_FOUND                        (TIZEN_EV_BASE + 0x0a)
+struct mgmt_ev_le_device_found {
+       struct mgmt_addr_info addr;
+       int8_t rssi;
+       uint32_t flags;
+       int8_t adv_type;
+       uint16_t eir_len;
+       uint8_t eir[0];
+} __packed;
+
+#define MGMT_EV_MULTI_ADV_STATE_CHANGED                        (TIZEN_EV_BASE + 0x0b)
+struct mgmt_ev_vendor_specific_multi_adv_state_changed {
+       uint8_t adv_instance;
+       uint8_t state_change_reason;
+       int16_t connection_handle;
+} __packed;
+
+#define MGMT_EV_6LOWPAN_CONN_STATE_CHANGED             (TIZEN_EV_BASE + 0x0c)
+struct mgmt_ev_6lowpan_conn_state_changed {
+       struct  mgmt_addr_info addr;
+       uint8_t connected;
+       uint8_t ifname[16];
+} __packed;
+
+
+#define MGMT_EV_LE_DATA_LENGTH_CHANGED         (TIZEN_EV_BASE + 0x0d)
+struct mgmt_ev_le_data_length_changed {
+       struct  mgmt_addr_info addr;
+       int16_t max_tx_octets;
+       int16_t max_tx_time;
+       int16_t max_rx_octets;
+       int16_t max_rx_time;
+} __packed;
+
+static const char *mgmt_tizen_op[] = {
+       "<0x0000>",
+       "Set Advertising Parameters",
+       "Set Advertising Data",
+       "Set Scan Response Data",
+       "Add Device White List",
+       "Remove Device White List",
+       "Clear Device White List",
+       "Set RSSI Enable",
+       "Get Raw RSSI",
+       "Set RSSI Disable",
+       "Start LE Discovery",
+       "Stop LE Discovery",
+       "Disable LE Auto Connect",
+       "LE Connection Update",
+       "Set Manufacturer Data",
+       "LE Set Scan Parameters",
+       "Set Voice Setting",
+       "Get Adv Tx Power",
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       "Enable BT 6LOWPAN",
+       "Connect BT 6LOWPAN",
+       "Disconnect BT 6LOWPAN"
+#endif
+};
+
+static const char *mgmt_tizen_ev[] = {
+       "<0x0000>",
+       "Device Name Update",
+       "Hardware Error",
+       "Tx TimeOut Error",
+       "RSSI Alert",
+       "Raw RSSI",
+       "RSSI Enabled",
+       "RSSI Disabled",
+       "LE Connection Updated",
+       "LE Connection Update Failed",
+       "LE Device Found",
+       "Multi Adv State Change",
+};
+#endif /* End of TIZEN_FEATURE_BLUEZ_MODIFY */
+
 #ifndef NELEM
 #define NELEM(x) (sizeof(x) / sizeof((x)[0]))
 #endif
 
 static inline const char *mgmt_opstr(uint16_t op)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (op >= NELEM(mgmt_op)) {
+               uint16_t tizen_op = op - TIZEN_OP_CODE_BASE;
+
+               if (tizen_op > 0 &&
+                   tizen_op < NELEM(mgmt_tizen_op))
+                       return mgmt_tizen_op[tizen_op];
+
+               return "<unknown opcode>";
+       }
+#else
        if (op >= NELEM(mgmt_op))
                return "<unknown opcode>";
+#endif
+
        return mgmt_op[op];
 }
 
 static inline const char *mgmt_evstr(uint16_t ev)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (ev >= NELEM(mgmt_ev)) {
+               uint16_t tizen_ev = ev - TIZEN_EV_BASE;
+
+               if (tizen_ev > 0 &&
+                   tizen_ev < NELEM(mgmt_tizen_ev))
+                       return mgmt_tizen_ev[tizen_ev];
+
+               return "<unknown event>";
+       }
+#else
        if (ev >= NELEM(mgmt_ev))
                return "<unknown event>";
+#endif
+
        return mgmt_ev[ev];
 }
 
index 2dcfe9e..393fbe8 100755 (executable)
@@ -30,6 +30,9 @@ extern "C" {
 #endif
 
 #include <stdint.h>
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include <bluetooth/bluetooth.h>
+#endif
 
 #define GENERIC_AUDIO_UUID     "00001203-0000-1000-8000-00805f9b34fb"
 
@@ -88,6 +91,9 @@ extern "C" {
 #define HDP_SINK_UUID          "00001402-0000-1000-8000-00805f9b34fb"
 
 #define HID_UUID               "00001124-0000-1000-8000-00805f9b34fb"
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+#define HID_DEVICE_UUID                "00001124-0000-1000-8000-00805f9b43bf"
+#endif
 
 #define DUN_GW_UUID            "00001103-0000-1000-8000-00805f9b34fb"
 
@@ -106,6 +112,12 @@ extern "C" {
 #define OBEX_MNS_UUID          "00001133-0000-1000-8000-00805f9b34fb"
 #define OBEX_MAP_UUID          "00001134-0000-1000-8000-00805f9b34fb"
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+/* Samsung Accessary Protocol UUIDs */
+#define WEARABLE_OLD_SAP_UUID  "a49eb41e-cb06-495c-9f4f-aa80a90cdf4a"
+#define WEARABLE_NEW_SAP_UUID  "a49eb41e-cb06-495c-9f4f-bb80a90cdf00"
+#endif
+
 /* GATT UUIDs section */
 #define GATT_PRIM_SVC_UUID                             0x2800
 #define GATT_SND_SVC_UUID                              0x2801
@@ -127,6 +139,9 @@ extern "C" {
 #define GATT_CHARAC_SOFTWARE_REVISION_STRING           0x2A28
 #define GATT_CHARAC_MANUFACTURER_NAME_STRING           0x2A29
 #define GATT_CHARAC_PNP_ID                             0x2A50
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define GATT_CHARAC_CENTRAL_RPA_RESOLUTION             0x2AA6
+#endif
 
 /* GATT Characteristic Descriptors */
 #define GATT_CHARAC_EXT_PROPER_UUID                    0x2900
@@ -139,6 +154,11 @@ extern "C" {
 #define GATT_EXTERNAL_REPORT_REFERENCE                 0x2907
 #define GATT_REPORT_REFERENCE                          0x2908
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+/* GATT Service UUIDs : Defined  by SIG */
+#define GATT_IPSP_UUID         0x1820
+#endif
+
 typedef struct {
        enum {
                BT_UUID_UNSPEC = 0,
index a3c3443..80edbeb 100755 (executable)
@@ -35,6 +35,9 @@
 #include "ll.h"
 #include "vendor.h"
 #include "broadcom.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "uuid.h"
+#endif
 
 static void print_status(uint8_t status)
 {
@@ -93,6 +96,599 @@ static void launch_ram_cmd(const void *data, uint8_t size)
        print_field("Address: 0x%8.8x", addr);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void set_advt_param_multi_subcmd(const void *data, uint8_t size)
+{
+       uint8_t adv_instance = get_u8(data + size - 2);
+       int8_t tx_power = *((int8_t *)(data + size - 1));
+
+       print_le_set_adv_parameters_cmd(data, size - 2);
+
+       print_field("Advertising Instance: %u", adv_instance);
+       print_field("TX Power: %d", tx_power);
+}
+
+static void set_advt_data_subcmd(const void *data, uint8_t size)
+{
+       uint8_t adv_instance = get_u8(data + size - 1);
+
+       print_le_set_adv_data_cmd(data, size - 1);
+
+       print_field("Advertising Instance: %u", adv_instance);
+}
+
+static void set_scan_rsp_data_multi_subcmd(const void *data, uint8_t size)
+{
+       uint8_t adv_instance = get_u8(data + size - 1);
+
+       print_le_set_scan_rsp_data_cmd(data, size - 1);
+
+       print_field("Advertising Instance: %u", adv_instance);
+}
+
+static void set_random_addr_multi_subcmd(const void *data, uint8_t size)
+{
+       uint8_t adv_instance = get_u8(data + size - 1);
+
+       print_le_set_random_address_cmd(data, size - 1);
+
+       print_field("Advertising Instance: %u", adv_instance);
+}
+
+static void set_adv_enable_multi_subcmd(const void *data, uint8_t size)
+{
+       uint8_t adv_instance = get_u8(data + size - 1);
+
+       print_le_set_adv_enable_cmd(data, size - 1);
+
+       print_field("Advertising Instance: %u", adv_instance);
+}
+
+static void enable_custom_feature_subcmd(const void *data, uint8_t size)
+{
+       uint8_t enable = get_u8(data);
+       const char *str;
+
+       switch (enable) {
+       case 0x00:
+               str = "Disable";
+               break;
+       case 0x01:
+               str = "Enable";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("%s offloaded RPA feature (0x%2.2x)", str, enable);
+}
+
+static void add_irk_to_list_subcmd(const void *data, uint8_t size)
+{
+       uint8_t addr_type = get_u8(data + 16);
+       const uint8_t *addr = data + 17;
+       const char *str;
+
+       print_field("LE IRK (1st byte LSB)");
+       packet_hexdump(data, 16);
+
+       switch (addr_type) {
+       case 0x00:
+               str = "Public Address";
+               break;
+       case 0x01:
+               str = "Random Address";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Address type : %s (0x%2.2x)", str, addr_type);
+       print_field("Address : %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+                       addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+}
+
+static void remove_irk_from_list_subcmd(const void *data, uint8_t size)
+{
+       uint8_t addr_type = get_u8(data);
+       const uint8_t *addr = data + 1;
+       const char *str;
+
+       switch (addr_type) {
+       case 0x00:
+               str = "Public Address";
+               break;
+       case 0x01:
+               str = "Random Address";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Address type : %s (0x%2.2x)", str, addr_type);
+       print_field("Address : %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+                       addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+}
+
+static void read_irk_list_entry_subcmd(const void *data, uint8_t size)
+{
+       uint8_t index = get_u8(data);
+
+       print_field("LE Read IRK List entry index : %u", index);
+}
+
+static void apcf_enable_subcmd(const void *data, uint8_t size)
+{
+       uint8_t enable = get_u8(data);
+       const char *str;
+
+       switch (enable) {
+       case 0x00:
+               str = "Disable";
+               break;
+       case 0x01:
+               str = "Enable";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("%s APCF feature (0x%2.2x)", str, enable);
+}
+
+static const struct {
+       uint8_t bit;
+       const char *str;
+} apcf_feature_table[] = {
+       {  0, "Broadcast Address filter"        },
+       {  1, "Service Data Change filter"      },
+       {  2, "Service UUID check"              },
+       {  3, "Service Solicitation UUID check" },
+       {  4, "Local Name check"                },
+       {  5, "Manufacturer Data check"         },
+       {  6, "Service Data check"              },
+       { }
+};
+
+static void print_apcf_feature(const char *label, uint16_t feature)
+{
+       int i;
+       uint16_t mask;
+
+       mask = feature;
+
+       print_field("%s", label);
+
+       for (i = 0; apcf_feature_table[i].str; i++) {
+               if (feature & (1 << apcf_feature_table[i].bit)) {
+                       print_field("   %s", apcf_feature_table[i].str);
+                       mask &= ~(1 << apcf_feature_table[i].bit);
+               }
+       }
+
+       if (mask)
+               print_field("   Unknown features (0x%4.4x)", mask);
+}
+
+static void apcf_set_filtering_param_subcmd(const void *data, uint8_t size)
+{
+       uint8_t add = get_u8(data);
+       uint8_t index = get_u8(data + 1);
+       uint16_t feature_selection = get_le16(data + 2);
+       uint16_t list_logic = get_le16(data + 4);
+       uint16_t filter_logic = get_u8(data + 6);
+       uint8_t rssi_high = get_u8(data + 8);
+       uint8_t delivery_mode = get_u8(data + 9);
+       uint16_t onfound_timeout = get_le16(data + 10);
+       uint8_t onfound_timeout_cnt = get_u8(data + 12);
+       uint8_t rssi_low = get_u8(data + 13);
+       uint16_t onlost_timeout = get_le16(data + 14);
+       uint16_t no_of_tracking_entries;
+       const char *str;
+
+       switch (add) {
+       case 0x00:
+               str = "Add";
+               break;
+       case 0x01:
+               str = "Delete";
+               break;
+       case 0x02:
+               str = "Clear";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Action : %s for filter [%d]", str, index);
+
+       print_apcf_feature("Feature Selection", feature_selection);
+       print_apcf_feature("List Logic Type (OR)", ~list_logic);
+       print_apcf_feature("List Logic Type (AND)", list_logic);
+       print_apcf_feature("Filter Logic Type (OR)", ~(filter_logic << 3));
+       print_apcf_feature("Filter Logic Type (AND)", filter_logic << 3);
+       print_field("RSSI High Threshold : %d dBm", rssi_high);
+
+       switch (delivery_mode) {
+       case 0x00:
+               str = "Immediate";
+               break;
+       case 0x01:
+               str = "On Found";
+               break;
+       case 0x02:
+               str = "Batched";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Delivery_mode : %s", str);
+       print_field("On Found Timeout : %d miliseconds", onfound_timeout);
+       print_field("On Found Timeout Count : %d", onfound_timeout_cnt);
+       print_field("RSSI Low Threshold : %d dBm", rssi_low);
+       print_field("On Lost Timeout : %d miliseconds", onlost_timeout);
+
+       if (size >= 18) {
+               no_of_tracking_entries = get_le16(data + 16);
+               print_field("Number of Tracking Entries : %d",
+                               no_of_tracking_entries);
+       }
+}
+
+static void apcf_broadcaster_addr_subcmd(const void *data, uint8_t size)
+{
+       uint8_t add = get_u8(data);
+       uint8_t index = get_u8(data + 1);
+       uint8_t type = get_u8(data + 7);
+       char *str;
+
+       switch (add) {
+       case 0x00:
+               str = "Add";
+               break;
+       case 0x01:
+               str = "Delete";
+               break;
+       case 0x02:
+               str = "Clear";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Action : %s for filter [%d]", str, index);
+       packet_print_addr("Address", data + 2, type == 0x00 ? false : true);
+}
+
+static void apcf_service_uuid_subcmd(const void *data, uint8_t size)
+{
+       uint8_t add = get_u8(data);
+       uint8_t index = get_u8(data + 1);
+       char *str;
+       const uint8_t *uuid;
+       uint16_t uuid16;
+       uint32_t uuid32;
+
+
+       switch (add) {
+       case 0x00:
+               str = "Add";
+               break;
+       case 0x01:
+               str = "Delete";
+               break;
+       case 0x02:
+               str = "Clear";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Action : %s for filter [%d]", str, index);
+
+       switch ((size - 2) / 2) {
+       case 2:
+               uuid16 = get_le16(data + 2);
+               print_field("  UUID     : %s (0x%4.4x)",
+                               uuid16_to_str(uuid16), uuid16);
+
+               uuid16 = get_le16(data + 4);
+               print_field("  UUID Mask: %s (0x%4.4x)",
+                               uuid16_to_str(uuid16), uuid16);
+               break;
+       case 4:
+               uuid32 = get_le32(data + 2);
+               print_field("  UUID     :%s (0x%8.8x)",
+                               uuid32_to_str(uuid32), uuid32);
+
+               uuid32 = get_le32(data + 6);
+               print_field("  UUID Mask:%s (0x%8.8x)",
+                               uuid32_to_str(uuid32), uuid32);
+               break;
+       case 16:
+               uuid = data + 2;
+               print_field("  UUID     :%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
+                               get_le32(&uuid[12]), get_le16(&uuid[10]),
+                               get_le16(&uuid[8]), get_le16(&uuid[6]),
+                               get_le32(&uuid[2]), get_le16(&uuid[0]));
+               uuid = data + 18;
+               print_field("  UUID     :%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
+                               get_le32(&uuid[12]), get_le16(&uuid[10]),
+                               get_le16(&uuid[8]), get_le16(&uuid[6]),
+                               get_le32(&uuid[2]), get_le16(&uuid[0]));
+               break;
+       default:
+               print_field("Invalid UUIDs");
+               packet_hexdump(data + 2, size - 2);
+               break;
+       }
+
+       return;
+}
+
+static void apcf_service_solicitation_uuid_subcmd(const void *data, uint8_t size)
+{
+       apcf_service_uuid_subcmd(data, size);
+}
+
+static void apcf_local_name_subcmd(const void *data, uint8_t size)
+{
+       uint8_t add = get_u8(data);
+       uint8_t index = get_u8(data + 1);
+       char *str;
+       char name[30] = { 0 };
+
+       switch (add) {
+       case 0x00:
+               str = "Add";
+               break;
+       case 0x01:
+               str = "Delete";
+               break;
+       case 0x02:
+               str = "Clear";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Action : %s for filter [%d]", str, index);
+
+       memcpy(name, data + 2, size - 2 < 29 ? size - 2 : 29);
+       print_field("Local Name : %s", name);
+}
+
+static void apcf_manufacturer_data_subcmd(const void *data, uint8_t size)
+{
+       uint8_t add = get_u8(data);
+       uint8_t index = get_u8(data + 1);
+       char *str;
+
+       switch (add) {
+       case 0x00:
+               str = "Add";
+               break;
+       case 0x01:
+               str = "Delete";
+               break;
+       case 0x02:
+               str = "Clear";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Action : %s for filter [%d]", str, index);
+
+       print_field("Manufacturer data");
+       packet_hexdump(data + 2, (size - 2 ) / 2);
+
+       print_field("Manufacturer data Mask");
+       packet_hexdump(data + 2 + (size - 2) / 2, (size - 2 ) / 2);
+}
+
+static void apcf_service_data_subcmd(const void *data, uint8_t size)
+{
+       uint8_t add = get_u8(data);
+       uint8_t index = get_u8(data + 1);
+       char *str;
+
+       switch (add) {
+       case 0x00:
+               str = "Add";
+               break;
+       case 0x01:
+               str = "Delete";
+               break;
+       case 0x02:
+               str = "Clear";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Action : %s for filter [%d]", str, index);
+
+       print_field("Service data");
+       packet_hexdump(data + 2, (size - 2 ) / 2);
+
+       print_field("Service data Mask");
+       packet_hexdump(data + 2 + (size - 2) / 2, (size - 2 ) / 2);
+}
+
+struct subcmd_data {
+       uint8_t subcmd;
+       const char *str;
+       void (*cmd_func) (const void *data, uint8_t size);
+       uint8_t cmd_size;
+       bool cmd_fixed;
+};
+
+static void print_subcmd(const struct subcmd_data *subcmd_data,
+                                       const void *data, uint8_t size)
+{
+       const char *subcmd_color;
+
+       if (subcmd_data->cmd_func)
+               subcmd_color = COLOR_BLUE;
+       else
+               subcmd_color = COLOR_WHITE_BG;
+
+       print_indent(6, subcmd_color, "", subcmd_data->str, COLOR_OFF,
+                                       " (0x%2.2x)", subcmd_data->subcmd);
+
+       if (!subcmd_data->cmd_func) {
+               packet_hexdump(data, size);
+               return;
+       }
+
+       if (subcmd_data->cmd_fixed) {
+               if (size != subcmd_data->cmd_size) {
+                       print_text(COLOR_ERROR, "invalid packet size");
+                       packet_hexdump(data, size);
+                       return;
+               }
+       } else {
+               if (size < subcmd_data->cmd_size) {
+                       print_text(COLOR_ERROR, "too short packet");
+                       packet_hexdump(data, size);
+                       return;
+               }
+       }
+
+       subcmd_data->cmd_func(data, size);
+}
+
+static const struct subcmd_data le_multi_advt_table[] = {
+       { 0x01, "LE Set Advertising Parameters Multi Sub Command",
+               set_advt_param_multi_subcmd, 23, true },
+       { 0x02, "LE Set Advertising Data Multi Sub Command",
+               set_advt_data_subcmd, 33, false },
+       { 0x03, "LE Set Scan Response Data Multi Sub Command",
+               set_scan_rsp_data_multi_subcmd, 33, false },
+       { 0x04, "LE Set Random Address Multi Sub Command",
+               set_random_addr_multi_subcmd, 7, true },
+       { 0x05, "LE Set Advertise Enable Multi Sub Command",
+               set_adv_enable_multi_subcmd, 2, true },
+       { }
+};
+
+static void le_multi_advt_cmd(const void *data, uint8_t size)
+{
+       uint8_t subcmd = *((const uint8_t *)data);
+       struct subcmd_data unknown;
+       const struct subcmd_data *subcmd_data = &unknown;
+       int i;
+
+       unknown.subcmd = subcmd;
+       unknown.str = "Unknown Sub Command";
+       unknown.cmd_func = NULL;
+       unknown.cmd_size = 0;
+       unknown.cmd_fixed = true;
+
+       for (i = 0; le_multi_advt_table[i].str; i++) {
+               if (le_multi_advt_table[i].subcmd == subcmd) {
+                       subcmd_data = &le_multi_advt_table[i];
+                       break;
+               }
+       }
+
+       print_subcmd(subcmd_data, data + 1, size - 1);
+}
+
+static const struct subcmd_data le_rpa_offload_table[] = {
+       { 0x01, "Enable customer specific feature",
+               enable_custom_feature_subcmd, 1, true },
+       { 0x02, "Add IRK to the list",
+               add_irk_to_list_subcmd, 23, true },
+       { 0x03, "Remove IRK from the list",
+               remove_irk_from_list_subcmd, 7, true },
+       { 0x04, "Clear IRK list",
+               null_cmd, 0, true },
+       { 0x05, "Read IRK list entry",
+               read_irk_list_entry_subcmd, 1, true },
+       { }
+};
+
+static void le_rpa_offload_cmd(const void *data, uint8_t size)
+{
+       uint8_t subcmd = *((const uint8_t *)data);
+       struct subcmd_data unknown;
+       const struct subcmd_data *subcmd_data = &unknown;
+       int i;
+
+       unknown.subcmd = subcmd;
+       unknown.str = "Unknown Sub Command";
+       unknown.cmd_func = NULL;
+       unknown.cmd_size = 0;
+       unknown.cmd_fixed = true;
+
+       for (i = 0; le_rpa_offload_table[i].str; i++) {
+               if (le_rpa_offload_table[i].subcmd == subcmd) {
+                       subcmd_data = &le_rpa_offload_table[i];
+                       break;
+               }
+       }
+
+       print_subcmd(subcmd_data, data + 1, size - 1);
+}
+
+static const struct subcmd_data le_apcf_table[] = {
+       { 0x00, "APCF Enable",
+               apcf_enable_subcmd, 1, true },
+       { 0x01, "APCF Set Filtering Parameters",
+               apcf_set_filtering_param_subcmd, 15, false },
+       { 0x02, "APCF Broadcaster Address",
+               apcf_broadcaster_addr_subcmd, 9, true },
+       { 0x03, "APCF Service UUID",
+               apcf_service_uuid_subcmd, 2, false },
+       { 0x04, "APCF Service Solicitation UUID",
+               apcf_service_solicitation_uuid_subcmd, 2, false },
+       { 0x05, "APCF Local Name",
+               apcf_local_name_subcmd, 2, false },
+       { 0x06, "APCF Manufacturer Data",
+               apcf_manufacturer_data_subcmd, 2, false },
+       { 0x07, "APCF Service Data",
+               apcf_service_data_subcmd, 2, false },
+       { }
+};
+
+static void le_apcf_cmd(const void *data, uint8_t size)
+{
+       uint8_t subcmd = *((const uint8_t *)data);
+       struct subcmd_data unknown;
+       const struct subcmd_data *subcmd_data = &unknown;
+       int i;
+
+       unknown.subcmd = subcmd;
+       unknown.str = "Unknown Sub Command";
+       unknown.cmd_func = NULL;
+       unknown.cmd_size = 0;
+       unknown.cmd_fixed = true;
+
+       for (i = 0; le_apcf_table[i].str; i++) {
+               if (le_apcf_table[i].subcmd == subcmd) {
+                       subcmd_data = &le_apcf_table[i];
+                       break;
+               }
+       }
+
+       print_subcmd(subcmd_data, data + 1, size - 1);
+}
+#endif
+
 static void read_vid_pid_rsp(const void *data, uint8_t size)
 {
        uint8_t status = get_u8(data);
@@ -132,6 +728,97 @@ static void read_verbose_version_info_rsp(const void *data, uint8_t size)
        print_field("Build number: %u (0x%4.4x)", build_num, build_num);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void get_vendor_capabilities_rsp(const void *data, uint8_t size)
+{
+       uint8_t status = get_u8(data);
+       uint8_t max_advt_instances = get_u8(data + 1);
+       uint8_t offloaded_resolution_of_private_address = get_u8(data + 2);
+       uint16_t total_scan_results_storage = get_le16(data + 3);
+       uint8_t max_irk_list_sz = get_u8(data + 5);
+       uint8_t filtering_support = get_u8(data + 6);
+       uint8_t max_filter = get_u8(data + 7);
+       uint8_t activity_energy_info_support = get_u8(data + 8);
+       uint8_t onlost_follow_per_filter = get_u8(data + 9);
+
+       print_status(status);
+       print_field("The Number of advertisement instances supported: %u",
+                       max_advt_instances);
+       print_field("BT chip capability of RPA: %s",
+                       offloaded_resolution_of_private_address ?
+                       "Capable" : "Not Capable");
+       print_field("Storage for scan results: %u bytes",
+                       total_scan_results_storage);
+       print_field("The Number of IRK entries supported: %u", max_irk_list_sz);
+       print_field("Support Filtering in BT chip: %s",
+                       filtering_support ? "Supported" : "Not Supported");
+       print_field("The Number of filters supported: %u", max_filter);
+       print_field("Supports reporting of activity and energy info: %s",
+                       activity_energy_info_support ?
+                       "Capable" : "Not Capable");
+       print_field("The Number of advertisers that can be analysed for "
+                       "onlost per filter: %u", onlost_follow_per_filter);
+}
+
+static void le_multi_advt_rsp(const void *data, uint8_t size)
+{
+       uint8_t status = get_u8(data);
+       uint8_t subcmd = get_u8(data + 1);
+       int i;
+       const char *str = "Unknown Sub Command";
+
+       print_status(status);
+
+       for (i = 0; le_multi_advt_table[i].str; i++) {
+               if (le_multi_advt_table[i].subcmd == subcmd) {
+                       str = le_multi_advt_table[i].str;
+                       break;
+               }
+       }
+
+       print_field("Multi Advertise OPcode: %s (%u)", str, subcmd);
+}
+
+static void le_rpa_offload_rsp(const void *data, uint8_t size)
+{
+       uint8_t status = get_u8(data);
+       uint8_t subcmd = get_u8(data + 1);
+       int i;
+       const char *str = "Unknown Sub Command";
+
+       print_status(status);
+
+       for (i = 0; le_rpa_offload_table[i].str; i++) {
+               if (le_rpa_offload_table[i].subcmd == subcmd) {
+                       str = le_rpa_offload_table[i].str;
+                       break;
+               }
+       }
+
+       print_field("RPA Offload OPcode: %s (%u)", str, subcmd);
+}
+
+static void le_apcf_rsp(const void *data, uint8_t size)
+{
+       uint8_t status = get_u8(data);
+       uint8_t subcmd = get_u8(data + 1);
+       int i;
+       const char *str = "Unknown Sub Command";
+
+       print_status(status);
+
+       for (i = 0; le_apcf_table[i].str; i++) {
+               if (le_apcf_table[i].subcmd == subcmd) {
+                       str = le_apcf_table[i].str;
+                       break;
+               }
+       }
+
+       print_field("Advertising Packet Content Filter OPcode: %s (%u)",
+                       str, subcmd);
+}
+#endif
+
 static const struct vendor_ocf vendor_ocf_table[] = {
        { 0x001, "Write BD ADDR",
                        write_bd_addr_cmd, 6, true,
@@ -157,6 +844,25 @@ static const struct vendor_ocf vendor_ocf_table[] = {
        { 0x079, "Read Verbose Config Version Info",
                        null_cmd, 0, true,
                        read_verbose_version_info_rsp, 7, true },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { 0x0153, "LE Get Vendor Capabilities",
+                       null_cmd, 0, true,
+                       get_vendor_capabilities_rsp, 10, false },
+       { 0x0154, "LE Multi Advertise",
+                       le_multi_advt_cmd, 1, false,
+                       le_multi_advt_rsp, 2, true },
+       { 0x0155, "LE RPA Offload",
+                       le_rpa_offload_cmd, 1, false,
+                       le_rpa_offload_rsp, 2, false },
+#if 0
+       { 0x0156, "LE Batch Scan",
+                       le_batch_scan_cmd, 1, false,
+                       le_batch_scan_rsp, 2, true },
+#endif
+       { 0x0157, "LE APCF",
+                       le_apcf_cmd, 1, false,
+                       le_apcf_rsp, 2, false },
+#endif
        { }
 };
 
@@ -234,7 +940,59 @@ void broadcom_lm_diag(const void *data, uint8_t size)
        }
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct hci_vse_sec_brcm_link_loss_dbg_info{
+       unsigned char   linklost_status;
+       unsigned char   conn_handle;
+       char    trans_pwr;
+       char    rssi;
+       unsigned char   ch_map[10];
+       unsigned char   lmp_cmd[4];
+} __packed;
+
+static void linkloss_evt(const void *data, uint8_t size)
+{
+
+         struct hci_vse_sec_brcm_link_loss_dbg_info *ev = (void *) data;
+         char *status = NULL;
+         switch (ev->linklost_status) {
+         case 0:
+                 status = "BT_Link_Supervision_Timeout";
+                 break;
+         case 1:
+                 status = "LE_Link_Supervision_Timeout";
+                 break;
+         case 2:
+                 status = "BT_LMP_Timeout_Local";
+                 break;
+         case 3:
+                 status = "BT_LMP_Timeout_Remote";
+                 break;
+         case 4:
+                 status = "LE_LMP_Timeout";
+                 break;
+         case 5:
+                 status = "Page_Timeout";
+                 break;
+         default :
+                 break;
+         }
+
+       print_field("Status:%s,Handle:%02x,Trans_Pwr:%d,RSSI:%d"
+               " Ch_map:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
+               " LMP_cmd:0x%x%x%x%x",
+               status, ev->conn_handle, ev->trans_pwr, ev->rssi,
+               ev->ch_map[0], ev->ch_map[1], ev->ch_map[2], ev->ch_map[3],
+               ev->ch_map[4], ev->ch_map[5], ev->ch_map[6], ev->ch_map[7],
+               ev->ch_map[8], ev->ch_map[9], ev->lmp_cmd[0], ev->lmp_cmd[1],
+               ev->lmp_cmd[2], ev->lmp_cmd[3]);
+}
+#endif
+
 static const struct vendor_evt vendor_evt_table[] = {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { 0x76, "SEC Link Loss", linkloss_evt,  18, true },
+#endif
        { }
 };
 
index 9bbdc37..4262e43 100755 (executable)
@@ -1364,9 +1364,18 @@ int control_tty(const char *path, unsigned int speed)
        return 0;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+bool control_writer(const char *path, int16_t rotate_count, ssize_t file_size)
+#else
 bool control_writer(const char *path)
+#endif
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       btsnoop_file = btsnoop_create(path, BTSNOOP_FORMAT_MONITOR,
+                       rotate_count, file_size);
+#else
        btsnoop_file = btsnoop_create(path, BTSNOOP_FORMAT_MONITOR);
+#endif
 
        return !!btsnoop_file;
 }
@@ -1396,6 +1405,9 @@ void control_reader(const char *path)
                break;
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       setenv("PAGER", "cat", 0);
+#endif
        open_pager();
 
        switch (format) {
index 630a852..cd66441 100755 (executable)
 
 #include <stdint.h>
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+bool control_writer(const char *path, int16_t rotate_count, ssize_t file_size);
+#else
 bool control_writer(const char *path);
+#endif
 void control_reader(const char *path);
 void control_server(const char *path);
 int control_tty(const char *path, unsigned int speed);
index b85f37b..1f54c6c 100755 (executable)
@@ -48,7 +48,11 @@ bool use_color(void);
 #define COLOR_INFO     COLOR_OFF
 #define COLOR_DEBUG    COLOR_WHITE
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define FALLBACK_TERMINAL_WIDTH 130
+#else
 #define FALLBACK_TERMINAL_WIDTH 80
+#endif
 
 #define print_indent(indent, color1, prefix, title, color2, fmt, args...) \
 do { \
index f9bca22..57830ad 100755 (executable)
@@ -70,6 +70,10 @@ static void usage(void)
                "\t-T, --date             Show time and date information\n"
                "\t-S, --sco              Dump SCO traffic\n"
                "\t-E, --ellisys [ip]     Send Ellisys HCI Injection\n"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               "\t-C, --count <num>      Save traces by <num> rotation\n"
+               "\t-W, --size <num>       Save traces at most <num> size\n"
+#endif
                "\t-h, --help             Show help options\n");
 }
 
@@ -86,6 +90,10 @@ static const struct option main_options[] = {
        { "date",    no_argument,       NULL, 'T' },
        { "sco",     no_argument,       NULL, 'S' },
        { "ellisys", required_argument, NULL, 'E' },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { "count",   required_argument, NULL, 'C' },
+       { "size",    required_argument, NULL, 'W' },
+#endif
        { "todo",    no_argument,       NULL, '#' },
        { "version", no_argument,       NULL, 'v' },
        { "help",    no_argument,       NULL, 'h' },
@@ -102,6 +110,10 @@ int main(int argc, char *argv[])
        const char *tty = NULL;
        unsigned int tty_speed = B115200;
        unsigned short ellisys_port = 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       int16_t rotate_count = -1;
+       ssize_t file_size = -1;
+#endif
        const char *str;
        int exit_status;
        sigset_t mask;
@@ -113,8 +125,13 @@ int main(int argc, char *argv[])
        for (;;) {
                int opt;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               opt = getopt_long(argc, argv, "d:r:w:a:s:p:i:tTSE:C:W:vh",
+                                               main_options, NULL);
+#else
                opt = getopt_long(argc, argv, "d:r:w:a:s:p:i:tTSE:vh",
                                                main_options, NULL);
+#endif
                if (opt < 0)
                        break;
 
@@ -171,6 +188,14 @@ int main(int argc, char *argv[])
                        ellisys_server = optarg;
                        ellisys_port = 24352;
                        break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               case 'C':
+                       rotate_count = atoi(optarg);
+                       break;
+               case 'W':
+                       file_size = atoll(optarg);
+                       break;
+#endif
                case '#':
                        packet_todo();
                        lmp_todo();
@@ -221,10 +246,18 @@ int main(int argc, char *argv[])
                return EXIT_SUCCESS;
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (writer_path && !control_writer(writer_path,
+                               rotate_count, file_size)) {
+               printf("Failed to open '%s'\n", writer_path);
+               return EXIT_FAILURE;
+       }
+#else
        if (writer_path && !control_writer(writer_path)) {
                printf("Failed to open '%s'\n", writer_path);
                return EXIT_FAILURE;
        }
+#endif
 
        if (ellisys_server)
                ellisys_enable(ellisys_server, ellisys_port);
index 6272562..259b805 100755 (executable)
@@ -7413,6 +7413,10 @@ static const char *current_vendor_str(void)
                return "Intel";
        case 15:
                return "Broadcom";
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       default:
+               return "Unknown";
+#endif
        }
 
        return NULL;
@@ -7432,6 +7436,10 @@ static const struct vendor_ocf *current_vendor_ocf(uint16_t ocf)
                return intel_vendor_ocf(ocf);
        case 15:
                return broadcom_vendor_ocf(ocf);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       default:
+               return broadcom_vendor_ocf(ocf);
+#endif
        }
 
        return NULL;
@@ -7451,6 +7459,10 @@ static const struct vendor_evt *current_vendor_evt(uint8_t evt)
                return intel_vendor_evt(evt);
        case 15:
                return broadcom_vendor_evt(evt);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       default:
+               return broadcom_vendor_evt(evt);
+#endif
        }
 
        return NULL;
@@ -11695,3 +11707,30 @@ void packet_todo(void)
                printf("\t%s\n", le_meta_event_table[i].str);
        }
 }
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void print_le_set_adv_parameters_cmd(const void *data, uint8_t size)
+{
+       le_set_adv_parameters_cmd(data, size);
+}
+
+void print_le_set_random_address_cmd(const void *data, uint8_t size)
+{
+       le_set_random_address_cmd(data, size);
+}
+
+void print_le_set_adv_data_cmd(const void *data, uint8_t size)
+{
+       le_set_adv_data_cmd(data, size);
+}
+
+void print_le_set_scan_rsp_data_cmd(const void *data, uint8_t size)
+{
+       le_set_scan_rsp_data_cmd(data, size);
+}
+
+void print_le_set_adv_enable_cmd(const void *data, uint8_t size)
+{
+       le_set_adv_enable_cmd(data, size);
+}
+#endif
index 354f4fe..20e1718 100755 (executable)
@@ -99,3 +99,11 @@ void packet_ctrl_event(struct timeval *tv, struct ucred *cred, uint16_t index,
                                        const void *data, uint16_t size);
 
 void packet_todo(void);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void print_le_set_adv_parameters_cmd(const void *data, uint8_t size);
+void print_le_set_random_address_cmd(const void *data, uint8_t size);
+void print_le_set_adv_data_cmd(const void *data, uint8_t size);
+void print_le_set_scan_rsp_data_cmd(const void *data, uint8_t size);
+void print_le_set_adv_enable_cmd(const void *data, uint8_t size);
+#endif
index e35124a..a755b8c 100755 (executable)
@@ -186,10 +186,21 @@ static void search_callback(uint8_t type, uint16_t status,
                        protos = NULL;
                }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               /* Use obex over l2cap only if obex over rfcomm is not there */
+               if (ch == -1) {
+                       data = sdp_data_get(rec, 0x0200);
+                       /* PSM must be odd and lsb of upper byte must be 0 */
+                       if (data != NULL && (data->val.uint16 & 0x0101) ==
+                                                                       0x0001)
+                               ch = data->val.uint16;
+               }
+#else
                data = sdp_data_get(rec, 0x0200);
                /* PSM must be odd and lsb of upper byte must be 0 */
                if (data != NULL && (data->val.uint16 & 0x0101) == 0x0001)
                        ch = data->val.uint16;
+#endif
 
                /* Cache the sdp record associated with the service that we
                 * attempt to connect. This allows reading its application
index fbcad6d..cc472e4 100755 (executable)
@@ -46,7 +46,9 @@
 #include "sync.h"
 #include "map.h"
 #include "manager.h"
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "mns-tizen.h"
+#endif
 #define CLIENT_INTERFACE       "org.bluez.obex.Client1"
 #define ERROR_INTERFACE                "org.bluez.obex.Error"
 #define CLIENT_PATH            "/org/bluez/obex"
@@ -64,11 +66,21 @@ static void shutdown_session(struct obc_session *session)
        obc_session_unref(session);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void release_session(struct obc_session *session)
+{
+       DBG("+");
+       sessions = g_slist_remove(sessions, session);
+       shutdown_session(session);
+       DBG("-");
+}
+#else
 static void release_session(struct obc_session *session)
 {
        sessions = g_slist_remove(sessions, session);
        shutdown_session(session);
 }
+#endif
 
 static void unregister_session(void *data)
 {
@@ -154,7 +166,10 @@ static int parse_device_dict(DBusMessageIter *iter,
 static struct obc_session *find_session(const char *path)
 {
        GSList *l;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if(!path)
+               return NULL;
+#endif
        for (l = sessions; l; l = l->next) {
                struct obc_session *session = l->data;
 
@@ -238,9 +253,16 @@ static DBusMessage *remove_session(DBusConnection *connection,
                                ERROR_INTERFACE ".NotAuthorized",
                                "Not Authorized");
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       obc_session_update(session, message, connection);
+#endif
        release_session(session);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       return NULL;
+#else
        return dbus_message_new_method_return(message);
+#endif
 }
 
 static const GDBusMethodTable client_methods[] = {
@@ -265,6 +287,9 @@ static struct obc_module {
        { "pbap", pbap_init, pbap_exit },
        { "sync", sync_init, sync_exit },
        { "map", map_init, map_exit },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { "mns", mns_init, mns_exit },
+#endif
        { }
 };
 
diff --git a/obexd/client/mns-tizen.c b/obexd/client/mns-tizen.c
new file mode 100755 (executable)
index 0000000..c6da1eb
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ *  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 <errno.h>
+#include <glib.h>
+#include <gdbus.h>
+#include <string.h>
+
+#include "log.h"
+
+#include "transfer.h"
+#include "session.h"
+#include "driver.h"
+#include "map_ap.h"
+#include "mns-tizen.h"
+#include "gobex/gobex-apparam.h"
+
+#define OBEX_MNS_UUID \
+       "\xBB\x58\x2B\x41\x42\x0C\x11\xDB\xB0\xDE\x08\x00\x20\x0C\x9A\x66"
+#define OBEX_MNS_UUID_LEN 16
+
+#define MNS_INTERFACE  "org.openobex.MessageNotification"
+#define ERROR_INF MNS_INTERFACE ".Error"
+#define MNS_UUID "00001133-0000-1000-8000-00805f9b34fb"
+
+enum msg_event_type {
+       EVENT_TYPE_NEW_MESSAGE,
+       EVENT_TYPE_DELIVERY_SUCCESS,
+       EVENT_TYPE_SENDING_SUCCESS,
+       EVENT_TYPE_DELIVERY_FAILURE,
+       EVENT_TYPE_SENDING_FAILURE,
+       EVENT_TYPE_MEMORY_FULL,
+       EVENT_TYPE_MEMORY_AVAILABLE,
+       EVENT_TYPE_MESSAGE_DELETED,
+       EVENT_TYPE_MESSAGE_SHIFT,
+       EVENT_TYPE_UNKNOWN,
+};
+
+struct mns_data {
+       struct obc_session *session;
+       DBusMessage *msg;
+};
+
+static DBusConnection *conn = NULL;
+
+static int get_event_type(gchar *event_type)
+{
+       DBG("event_type = %s\n", event_type);
+
+       if (!g_strcmp0(event_type, "NewMessage"))
+               return EVENT_TYPE_NEW_MESSAGE;
+       else if (!g_strcmp0(event_type, "DeliverySuccess"))
+               return EVENT_TYPE_DELIVERY_SUCCESS;
+       else if (!g_strcmp0(event_type, "SendingSuccess"))
+               return EVENT_TYPE_SENDING_SUCCESS;
+       else if (!g_strcmp0(event_type, "DeliveryFailure"))
+               return EVENT_TYPE_DELIVERY_FAILURE;
+       else if (!g_strcmp0(event_type, "SendingFailure"))
+               return EVENT_TYPE_SENDING_FAILURE;
+       else if (!g_strcmp0(event_type, "MemoryFull"))
+               return EVENT_TYPE_MEMORY_FULL;
+       else if (!g_strcmp0(event_type, "MemoryAvailable"))
+               return EVENT_TYPE_MEMORY_AVAILABLE;
+       else if (!g_strcmp0(event_type, "MessageDeleted"))
+               return EVENT_TYPE_MESSAGE_DELETED;
+       else if (!g_strcmp0(event_type, "MessageShift"))
+               return EVENT_TYPE_MESSAGE_SHIFT;
+       else
+               return EVENT_TYPE_UNKNOWN;
+
+}
+
+static gchar *generate_event_report(gchar *event_type,
+                               guint64 handle, gchar *folder,
+                               gchar *old_folder, gchar *msg_type)
+{
+       GString *buf;
+       int event;
+
+       event = get_event_type(event_type);
+       if (event == EVENT_TYPE_UNKNOWN)
+               return NULL;
+
+       buf = g_string_new("<MAP-event-report version=\"1.0\">");
+       g_string_append_printf(buf, "<event type=\"%s\" ", event_type);
+
+       if (event == EVENT_TYPE_MEMORY_FULL ||
+                       event == EVENT_TYPE_MEMORY_AVAILABLE)
+               goto done;
+
+       g_string_append_printf(buf, "handle=\"%llx\" ", handle);
+       g_string_append_printf(buf, "folder=\"%s\" ", folder);
+
+       if (event == EVENT_TYPE_MESSAGE_SHIFT)
+               g_string_append_printf(buf, " old_folder=\"%s\" ", old_folder);
+
+       g_string_append_printf(buf, "msg_type=\"%s\" ", msg_type);
+
+done:
+       g_string_append(buf, "/></MAP-event-report>");
+
+       return g_string_free(buf, FALSE);
+}
+
+static DBusMessage *send_event(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct mns_data *mns = user_data;
+       struct obc_transfer *transfer;
+       GObexApparam *apparam;
+       gchar *event_type;
+       gchar *folder;
+       gchar *old_folder;
+       gchar *msg_type;
+       gchar *buf;
+       guint64 handle;
+       GError *err;
+       DBusMessage *reply;
+
+       if (dbus_message_get_args(message, NULL,
+                       DBUS_TYPE_STRING, &event_type,
+                       DBUS_TYPE_UINT64, &handle,
+                       DBUS_TYPE_STRING, &folder,
+                       DBUS_TYPE_STRING, &old_folder,
+                       DBUS_TYPE_STRING, &msg_type,
+                       DBUS_TYPE_INVALID) == FALSE)
+               return g_dbus_create_error(message,
+                                       "org.openobex.Error.InvalidArguments",
+                                       NULL);
+
+       buf = generate_event_report(event_type, handle, folder,
+                               old_folder, msg_type);
+       if (!buf)
+               return g_dbus_create_error(message,
+                               "org.openobex.Error.InvalidArguments", NULL);
+
+       transfer = obc_transfer_put("x-bt/MAP-event-report", NULL, NULL,
+                               buf, strlen(buf), &err);
+
+       g_free(buf);
+
+       if (transfer == NULL)
+               goto fail;
+
+       /* Obexd currently supports single SDP for MAS */
+       apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_MASINSTANCEID, 0);
+
+       obc_transfer_set_apparam(transfer, apparam);
+
+       if (obc_session_queue(mns->session, transfer, NULL, NULL, &err))
+               return dbus_message_new_method_return(message);
+
+fail:
+       reply = g_dbus_create_error(message, ERROR_INF ".Failed", "%s",
+                                                               err->message);
+       g_error_free(err);
+       return reply;
+}
+
+static GDBusMethodTable mns_methods[] = {
+        { GDBUS_ASYNC_METHOD("SendEvent",
+               GDBUS_ARGS({ "event_type", "s" }, { "handle", "t" },
+                               { "folder", "s" }, { "old_folder", "s" },
+                               { "msg_type", "s" }),
+               NULL,
+               send_event) },
+       { }
+};
+
+static void mns_free(void *data)
+{
+       struct mns_data *mns = data;
+
+       obc_session_unref(mns->session);
+       g_free(mns);
+}
+
+static int mns_probe(struct obc_session *session)
+{
+       struct mns_data *mns;
+       const char *path;
+
+       path = obc_session_get_path(session);
+
+       DBG("%s", path);
+
+       mns = g_try_new0(struct mns_data, 1);
+       if (!mns)
+               return -ENOMEM;
+
+       mns->session = obc_session_ref(session);
+
+       if (!g_dbus_register_interface(conn, path, MNS_INTERFACE, mns_methods,
+                                       NULL, NULL, mns, mns_free)) {
+               mns_free(mns);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void mns_remove(struct obc_session *session)
+{
+       const char *path = obc_session_get_path(session);
+
+       DBG("%s", path);
+
+       g_dbus_unregister_interface(conn, path, MNS_INTERFACE);
+}
+
+static struct obc_driver mns = {
+       .service = "MNS",
+       .uuid = MNS_UUID,
+       .target = OBEX_MNS_UUID,
+       .target_len = OBEX_MNS_UUID_LEN,
+       .probe = mns_probe,
+       .remove = mns_remove
+};
+
+int mns_init(void)
+{
+       int err;
+
+       DBG("");
+
+       conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
+       if (!conn)
+               return -EIO;
+
+       err = obc_driver_register(&mns);
+       if (err < 0) {
+               dbus_connection_unref(conn);
+               conn = NULL;
+               return err;
+       }
+
+       return 0;
+}
+
+void mns_exit(void)
+{
+       DBG("");
+
+       dbus_connection_unref(conn);
+       conn = NULL;
+
+       obc_driver_unregister(&mns);
+}
diff --git a/obexd/client/mns-tizen.h b/obexd/client/mns-tizen.h
new file mode 100755 (executable)
index 0000000..e4ee5e5
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ *  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
+ *
+ */
+
+int mns_init(void);
+void mns_exit(void);
index 92785f6..9a6cf34 100755 (executable)
@@ -55,16 +55,26 @@ static DBusMessage *opp_send_file(DBusConnection *connection,
        char *filename;
        char *basename;
        GError *err = NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char *mimetype = NULL;
+#endif
 
        if (dbus_message_get_args(message, NULL,
                                        DBUS_TYPE_STRING, &filename,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                                       DBUS_TYPE_STRING, &mimetype,
+#endif
                                        DBUS_TYPE_INVALID) == FALSE)
                return g_dbus_create_error(message,
                                ERROR_INTERFACE ".InvalidArguments", NULL);
 
        basename = g_path_get_basename(filename);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       transfer = obc_transfer_put(mimetype, basename, filename, NULL, 0, &err);
+#else
        transfer = obc_transfer_put(NULL, basename, filename, NULL, 0, &err);
+#endif
 
        g_free(basename);
 
@@ -123,7 +133,11 @@ static DBusMessage *opp_exchange_business_cards(DBusConnection *connection,
 
 static const GDBusMethodTable opp_methods[] = {
        { GDBUS_METHOD("SendFile",
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               GDBUS_ARGS({ "sourcefile", "s" }, { "mimetype", "s" }),
+#else
                GDBUS_ARGS({ "sourcefile", "s" }),
+#endif
                GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }),
                opp_send_file) },
        { GDBUS_METHOD("PullBusinessCard",
index 1ab34a7..db46a80 100755 (executable)
@@ -218,7 +218,11 @@ static char *build_phonebook_path(const char *location, const char *item)
                internal = TRUE;
        } else if (!g_ascii_strncasecmp(location, "sim", 3)) {
                if (strlen(location) == 3)
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                       tmp = g_strdup("SIM1");
+#else
                        tmp = g_strdup("sim1");
+#endif
                else
                        tmp = g_ascii_strup(location, 4);
 
@@ -227,6 +231,11 @@ static char *build_phonebook_path(const char *location, const char *item)
        } else
                return NULL;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (!g_ascii_strcasecmp(item, "nil"))
+               return path;
+#endif
+
        if (!g_ascii_strcasecmp(item, "pb") ||
                !g_ascii_strcasecmp(item, "ich") ||
                !g_ascii_strcasecmp(item, "och") ||
@@ -358,7 +367,11 @@ static void read_return_apparam(struct obc_transfer *transfer,
 
        g_obex_apparam_get_uint16(apparam, PHONEBOOKSIZE_TAG,
                                                        phone_book_size);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+       g_obex_apparam_get_uint16(apparam, NEWMISSEDCALLS_TAG,
+#else
        g_obex_apparam_get_uint8(apparam, NEWMISSEDCALLS_TAG,
+#endif
                                                        new_missed_calls);
 
        read_version(pbap, apparam);
@@ -493,8 +506,14 @@ static GObexApparam *parse_offset(GObexApparam *apparam, DBusMessageIter *iter)
 {
        guint16 num;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16 &&
+                       dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
+               return NULL;
+#else
        if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
                return NULL;
+#endif
 
        dbus_message_iter_get_basic(iter, &num);
 
@@ -506,8 +525,14 @@ static GObexApparam *parse_max_count(GObexApparam *apparam,
 {
        guint16 num;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16 &&
+                       dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
+               return NULL;
+#else
        if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
                return NULL;
+#endif
 
        dbus_message_iter_get_basic(iter, &num);
 
@@ -737,9 +762,16 @@ static DBusMessage *pbap_select(DBusConnection *connection,
        }
 
        request = pending_request_new(pbap, message);
-
-       obc_session_setpath(pbap->session, path, pbap_setpath_cb, request,
-                                                                       &err);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (pbap->path == NULL || strlen(pbap->path) == 0)
+               obc_session_setpath(pbap->session, path + 1, pbap_setpath_cb, request,
+                                                                               &err);
+       else
+               obc_session_setpath(pbap->session, path, pbap_setpath_cb, request,
+                                                                               &err);
+#else
+       obc_session_setpath(pbap->session, path, pbap_setpath_cb, request, &err);
+#endif
        if (err != NULL) {
                DBusMessage *reply;
                reply =  g_dbus_create_error(message, ERROR_INTERFACE ".Failed",
@@ -870,6 +902,9 @@ static DBusMessage *pbap_list(DBusConnection *connection,
        struct pbap_data *pbap = user_data;
        GObexApparam *apparam;
        DBusMessageIter args;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       const char *pb_folder;
+#endif
 
        if (!pbap->path)
                return g_dbus_create_error(message,
@@ -878,6 +913,15 @@ static DBusMessage *pbap_list(DBusConnection *connection,
 
        dbus_message_iter_init(message, &args);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
+               return g_dbus_create_error(message,
+                               ERROR_INTERFACE ".InvalidArguments", NULL);
+
+       dbus_message_iter_get_basic(&args, &pb_folder);
+       dbus_message_iter_next(&args);
+#endif
+
        apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG,
                                                        DEFAULT_COUNT);
        apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
@@ -889,7 +933,11 @@ static DBusMessage *pbap_list(DBusConnection *connection,
                                ERROR_INTERFACE ".InvalidArguments", NULL);
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       return pull_vcard_listing(pbap, message, pb_folder, apparam);
+#else
        return pull_vcard_listing(pbap, message, "", apparam);
+#endif
 }
 
 static GObexApparam *parse_attribute(GObexApparam *apparam, const char *field)
@@ -1049,7 +1097,11 @@ static const GDBusMethodTable pbap_methods[] = {
                                        { "properties", "a{sv}" }),
                        pbap_pull_vcard) },
        { GDBUS_ASYNC_METHOD("List",
-                       GDBUS_ARGS({ "filters", "a{sv}" }),
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                       GDBUS_ARGS({ "folder", "s" }, {"filters", "a{sv}" }),
+#else
+                       GDBUS_ARGS({"filters", "a{sv}" }),
+#endif
                        GDBUS_ARGS({ "vcard_listing", "a(ss)" }),
                        pbap_list) },
        { GDBUS_ASYNC_METHOD("Search",
index 5f981bf..6c7c337 100755 (executable)
@@ -43,6 +43,9 @@
 #include "dbus.h"
 #include "transfer.h"
 #include "session.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "manager.h"
+#endif
 #include "driver.h"
 #include "transport.h"
 
@@ -114,6 +117,10 @@ struct obc_session {
        guint process_id;
        char *folder;
        struct callback_data *callback;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       DBusMessage *message;
+       DBusConnection *connection;
+#endif
 };
 
 static GSList *sessions = NULL;
@@ -139,6 +146,17 @@ struct obc_session *obc_session_ref(struct obc_session *session)
        return session;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void obc_session_update(struct obc_session *session, DBusMessage *message,
+               DBusConnection *connection)
+{
+       DBG("+");
+       session->message = dbus_message_ref(message);
+       session->connection = dbus_connection_ref(connection);
+       DBG("-");
+}
+#endif
+
 static void session_unregistered(struct obc_session *session)
 {
        char *path;
@@ -263,6 +281,18 @@ static void disconnect_complete(GObex *obex, GError *err, GObexPacket *rsp,
        if (err)
                error("%s", err->message);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (session->message) {
+               /* Dbus reply need to be done */
+               DBG("Dbus reply for remove_session");
+               g_dbus_send_reply(session->connection, session->message,
+                               DBUS_TYPE_INVALID);
+               dbus_message_unref(session->message);
+               dbus_connection_unref(session->connection);
+               session->message = NULL;
+               session->connection = NULL;
+       }
+#endif
        /* Disconnect transport */
        if (session->id > 0 && session->transport != NULL) {
                session->transport->disconnect(session->id);
@@ -350,8 +380,11 @@ static void session_disconnected(GObex *obex, GError *err, gpointer user_data)
 
        if (err)
                error("%s", err->message);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       release_session(session);
+#else
        obc_session_shutdown(session);
+#endif
 }
 
 static void transport_func(GIOChannel *io, GError *err, gpointer user_data)
index b561b7e..2ce9f3f 100755 (executable)
@@ -80,3 +80,8 @@ guint obc_session_delete(struct obc_session *session, const char *file,
                                GError **err);
 void obc_session_cancel(struct obc_session *session, guint id,
                                                        gboolean remove);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void release_session(struct obc_session *session);
+void obc_session_update(struct obc_session *session, DBusMessage *message,
+               DBusConnection *connection);
+#endif
index 092e72f..a5ecd13 100755 (executable)
@@ -787,12 +787,26 @@ static gboolean report_progress(gpointer data)
        if (transfer->transferred == transfer->progress)
                return TRUE;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (transfer->transferred == transfer->size) {
+               transfer->progress_id = 0;
+               if(transfer->progress == 0) {
+                       transfer->progress = transfer->transferred;
+                       transfer_set_status(transfer, TRANSFER_STATUS_ACTIVE);
+               }
+
+               return FALSE;
+       }
+
+       transfer->progress = transfer->transferred;
+#else
        transfer->progress = transfer->transferred;
 
        if (transfer->transferred == transfer->size) {
                transfer->progress_id = 0;
                return FALSE;
        }
+#endif
 
        if (transfer->status != TRANSFER_STATUS_ACTIVE &&
                                transfer->status != TRANSFER_STATUS_SUSPENDED)
@@ -884,8 +898,13 @@ static gboolean transfer_start_put(struct obc_transfer *transfer, GError **err)
        if (transfer->path == NULL)
                return TRUE;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       transfer->progress_id = g_timeout_add(10, report_progress,
+                                                               transfer);
+#else
        transfer->progress_id = g_timeout_add_seconds(1, report_progress,
                                                                transfer);
+#endif
 
        return TRUE;
 }
index 3ee18a6..85756a0 100755 (executable)
@@ -170,6 +170,13 @@ int ftp_chkput(struct obex_session *os, void *user_data)
 {
        struct ftp_session *ftp = user_data;
        const char *name = obex_get_name(os);
+#if 0
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char *folder;
+       int32_t time;
+       int err;
+#endif
+#endif
        char *path;
        int ret;
 
@@ -184,6 +191,15 @@ int ftp_chkput(struct obex_session *os, void *user_data)
        if (obex_get_size(os) == OBJECT_SIZE_DELETE)
                return 0;
 
+#if 0
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       time = 0;
+       err = manager_request_authorization(os, time, &folder, &name);
+       if (err < 0)
+               return -EPERM;
+#endif
+#endif
+
        path = g_build_filename(ftp->folder, name, NULL);
 
        ret = obex_put_stream_start(os, path);
@@ -219,6 +235,36 @@ int ftp_put(struct obex_session *os, void *user_data)
        return 0;
 }
 
+#if 0
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean is_valid_name(const char *name)
+{
+       char *forbid_chars = "[*\"<>;?|\\^:/]";
+       int exp;
+       regex_t reg;
+       regmatch_t match[1];
+       size_t size = 1;
+
+       if (name[0] == '.')
+               return FALSE;
+
+       exp = regcomp(&reg, forbid_chars, 0);
+
+       if (exp != 0)
+               return FALSE;
+
+       exp = regexec(&reg, name, size, match, 0);
+
+       regfree(&reg);
+
+       if (exp != REG_NOMATCH)
+               return FALSE;
+
+       return TRUE;
+}
+#endif
+#endif
+
 int ftp_setpath(struct obex_session *os, void *user_data)
 {
        struct ftp_session *ftp = user_data;
@@ -273,6 +319,16 @@ int ftp_setpath(struct obex_session *os, void *user_data)
                return -EPERM;
        }
 
+#if 0
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* Check if the folder name is valid or not */
+       if (!is_valid_name(name)) {
+               error("Set path failed: Invalid folder name!");
+               return -EINVAL;
+       }
+#endif
+#endif
+
        fullname = g_build_filename(ftp->folder, name, NULL);
 
        DBG("Fullname: %s", fullname);
diff --git a/obexd/plugins/messages-tizen.c b/obexd/plugins/messages-tizen.c
new file mode 100755 (executable)
index 0000000..e59c37b
--- /dev/null
@@ -0,0 +1,1547 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ *  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 <errno.h>
+#include <glib.h>
+#include <string.h>
+#include <stdio.h>
+#include "log.h"
+#include "messages.h"
+#include "../../profile.h"
+
+#include <dbus/dbus.h>
+
+#define QUERY_GET_FOLDER_TREE "GetFolderTree"
+#define QUERY_GET_MSG_LIST "GetMessageList"
+#define QUERY_GET_MESSAGE "GetMessage"
+#define QUERY_PUSH_MESSAGE "PushMessage"
+#define QUERY_PUSH_MESSAGE_DATA "PushMessageData"
+#define QUERY_UPDATE_MESSAGE "UpdateMessage"
+#define QUERY_SET_READ_STATUS "SetReadStatus"
+#define QUERY_SET_DELETE_STATUS "SetDeleteStatus"
+#define QUERY_NOTI_REGISTRATION "NotiRegistration"
+#define QUERY_DESTROY_AGENT "DestroyAgent"
+
+#define BT_MAP_SERVICE_OBJECT_PATH "/org/bluez/map_agent"
+#define BT_MAP_SERVICE_NAME "org.bluez.map_agent"
+#define BT_MAP_SERVICE_INTERFACE "org.bluez.MapAgent"
+
+/* Added as per MAP specification */
+#define BT_MAP_LIST_ITEM_MAX_LEN 256
+
+static DBusConnection *g_conn = NULL;
+
+struct mns_reg_data {
+       uint8_t notification_status;
+       char *remote_addr;
+};
+
+struct message_folder {
+       char *name;
+       GSList *subfolders;
+};
+
+struct session {
+       char *cwd;
+       struct message_folder *folder;
+       char *name;
+       uint16_t max;
+       uint16_t offset;
+       void *user_data;
+       struct messages_filter *filter;
+       struct messages_message *msg;
+       void (*folder_list_cb)(void *session, int err, uint16_t size,
+                                       const char *name, void *user_data);
+       void (*msg_list_cb)(void *session, int err, int size, gboolean newmsg,
+                                       const struct messages_message *entry,
+                                       void *user_data);
+       void (*push_msg_cb)(void *session, int err, guint64 handle,
+                               void *user_data);
+       void (*get_msg_cb)(void *session, int err, gboolean fmore,
+                               const char *chunk, void *user_data);
+       void (*msg_update_cb)(void *session, int err, void *user_data);
+       void (*msg_status_cb)(void *session, int err, void *user_data);
+};
+
+static struct message_folder *folder_tree = NULL;
+
+static void message_list_item_free(struct messages_message *data)
+{
+       DBG("+");
+       g_free(data->handle);
+       data->handle = NULL;
+
+       g_free(data->subject);
+       data->subject = NULL;
+
+       g_free(data->datetime);
+       data->datetime = NULL;
+
+       g_free(data->sender_name);
+       data->sender_name = NULL;
+
+       g_free(data->sender_addressing);
+       data->sender_addressing = NULL;
+
+       g_free(data->replyto_addressing);
+       data->replyto_addressing = NULL;
+
+       g_free(data->recipient_name);
+       data->recipient_name = NULL;
+
+       g_free(data->recipient_addressing);
+       data->recipient_addressing = NULL;
+
+       g_free(data->type);
+       data->type = NULL;
+
+       g_free(data->reception_status);
+       data->reception_status = NULL;
+
+       g_free(data->size);
+       data->size = NULL;
+
+       g_free(data->attachment_size);
+       data->attachment_size = NULL;
+       DBG("-");
+}
+
+static void session_filter_free(struct messages_filter *data)
+{
+       DBG("+");
+       if (NULL == data)
+               return;
+
+       g_free((gpointer)data->period_begin);
+       g_free((gpointer)data->period_end);
+       g_free((gpointer)data->recipient);
+       g_free((gpointer)data->originator);
+       g_free(data);
+       DBG("-");
+}
+
+static gboolean is_time_in_period(char *ref_time, char *period)
+{
+       guint64 ref_date_val;
+       guint64 date_val;
+
+       guint64 ref_time_val;
+       guint64 time_val;
+
+       char *start;
+       char *end = NULL;
+
+       char temp[20];
+
+       start = strtok_r(ref_time, "T", &end);
+       if (NULL == start || NULL == end)
+               return FALSE;
+
+       snprintf(temp, sizeof(temp), "%s", start);
+       ref_date_val = g_ascii_strtoull(temp, NULL, 16);
+       snprintf(temp, sizeof(temp), "%s", end);
+       ref_time_val = g_ascii_strtoull(temp, NULL, 16);
+
+       start = strtok_r(period, "T", &end);
+       if (NULL == start || NULL == end)
+               return FALSE;
+
+       snprintf(temp, sizeof(temp), "%s", start);
+       date_val = g_ascii_strtoull(temp, NULL, 16);
+       snprintf(temp, sizeof(temp), "%s", end);
+       time_val = g_ascii_strtoull(temp, NULL, 16);
+
+       if (ref_date_val < date_val) {
+               return TRUE;
+       } else if (ref_date_val > date_val) {
+               return FALSE;
+       } else {
+               if (ref_time_val <= time_val)
+                       return TRUE;
+               else
+                       return FALSE;
+       }
+}
+
+static gboolean __filter_timebased(char *begin, char *end, char *time)
+{
+       gboolean ret = 0;
+
+       if (!begin && !end) {
+               /* If start and stop are not specified
+               do not filter. */
+
+               return TRUE;
+       } else if (!end && begin) {
+               /* If "FilterPeriodEnd" is not specified the returned
+               message listing shall include the messages from
+               "FilterPeriodBegin" to current time */
+
+               return is_time_in_period(begin, time);
+       } else if (!begin && end) {
+               /* If "FilterPeriodBegin" is not specified the returned
+               message listing shall include the messages older than
+               "FilterPeriodEnd" */
+
+               return is_time_in_period(time, end);
+       } else {
+
+               if (TRUE == is_time_in_period(end, begin))
+                       return FALSE;
+
+               ret = is_time_in_period(begin, time);
+               if (ret == TRUE)
+                       return is_time_in_period(time, end);
+               else
+                       return FALSE;
+       }
+}
+
+static uint8_t get_type_val(const char *type)
+{
+       if (!g_strcmp0(type, "SMS_GSM"))
+               return 0x01;
+       else if (!g_strcmp0(type, "SMS_CDMA"))
+               return 0x02;
+       else if (!g_strcmp0(type, "EMAIL"))
+               return 0x04;
+       else if (!g_strcmp0(type, "MMS"))
+               return 0x08;
+       else
+               return 0x00;
+}
+
+static uint8_t get_read_status_val(gboolean read)
+{
+       if (read)
+               return 0x02; /* Read messages */
+       else
+               return 0x01; /* Unread messages */
+}
+
+static uint8_t get_priority_val(gboolean priority)
+{
+       if (priority)
+               return 0x01; /* High priority */
+       else
+               return 0x02; /* Low priority */
+}
+
+static struct message_folder *get_folder(const char *folder)
+{
+       GSList *folders = folder_tree->subfolders;
+       struct message_folder *last = NULL;
+       char **path;
+       int i;
+
+       if (g_strcmp0(folder, "/") == 0)
+               return folder_tree;
+
+       path = g_strsplit(folder, "/", 0);
+
+       for (i = 1; path[i] != NULL; i++) {
+               gboolean match_found = FALSE;
+               GSList *l;
+
+               for (l = folders; l != NULL; l = g_slist_next(l)) {
+                       struct message_folder *folder = l->data;
+
+                       if (g_ascii_strncasecmp(folder->name, path[i],
+                                       strlen(folder->name)) == 0) {
+                               match_found = TRUE;
+                               last = l->data;
+                               folders = folder->subfolders;
+                               break;
+                       }
+               }
+
+               if (!match_found) {
+                       g_strfreev(path);
+                       return NULL;
+               }
+       }
+
+       g_strfreev(path);
+
+       return last;
+}
+
+static void destroy_folder_tree(void *root)
+{
+       struct message_folder *folder = root;
+       GSList *tmp, *next;
+
+       if (folder == NULL)
+               return;
+
+       g_free(folder->name);
+
+       tmp = folder->subfolders;
+       while (tmp != NULL) {
+               next = g_slist_next(tmp);
+               destroy_folder_tree(tmp->data);
+               tmp = next;
+       }
+       g_slist_free(folder->subfolders);
+       g_free(folder);
+}
+
+static struct message_folder *create_folder(const char *name)
+{
+       struct message_folder *folder = g_new0(struct message_folder, 1);
+
+       folder->name = g_strdup(name);
+       return folder;
+}
+
+static void create_folder_tree()
+{
+
+       struct message_folder *parent, *child;
+
+       folder_tree = create_folder("/");
+
+       parent = create_folder("telecom");
+       folder_tree->subfolders = g_slist_append(folder_tree->subfolders,
+                                                               parent);
+
+       child = create_folder("msg");
+       parent->subfolders = g_slist_append(parent->subfolders, child);
+}
+
+int messages_init(void)
+{
+       g_conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
+       if (!g_conn) {
+               error("Can't get on session bus");
+               return -1;
+       }
+
+       return 0;
+}
+
+void messages_exit(void)
+{
+       if (g_conn) {
+               dbus_connection_unref(g_conn);
+               g_conn = NULL;
+       }
+}
+
+static void message_get_folder_list(DBusMessage *reply, void *user_data)
+{
+       DBusMessageIter iter;
+       DBusMessageIter iter_struct;
+       DBusMessageIter entry;
+       DBusError derr;
+       const char *name = NULL;
+       struct message_folder *parent = {0,}, *child = {0,};
+       GSList *l;
+
+       DBG("+\n");
+
+       for (l = folder_tree->subfolders; l != NULL; l = parent->subfolders)
+               parent = l->data;
+
+       DBG("Last child folder = %s \n", parent->name);
+       dbus_error_init(&derr);
+
+       if (dbus_set_error_from_message(&derr, reply)) {
+               error("Replied with an error: %s, %s", derr.name, derr.message);
+               dbus_error_free(&derr);
+       } else {
+               dbus_message_iter_init(reply, &iter);
+               dbus_message_iter_recurse(&iter, &iter_struct);
+
+               while (dbus_message_iter_get_arg_type(&iter_struct) ==
+                                                       DBUS_TYPE_STRUCT) {
+                       dbus_message_iter_recurse(&iter_struct, &entry);
+
+                       dbus_message_iter_get_basic(&entry, &name);
+                       DBG("Folder name = %s \n", name);
+                       child = create_folder(name);
+                       parent->subfolders = g_slist_append(parent->subfolders,
+                                                       child);
+                       dbus_message_iter_next(&iter_struct);
+               }
+       }
+       dbus_message_unref(reply);
+       DBG("-\n");
+}
+
+static void message_get_msg_list(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusMessageIter iter;
+       DBusMessageIter iter_struct;
+       DBusMessageIter entry;
+       DBusError derr;
+       const char *msg_handle;
+       const char *subject;
+       const char *datetime;
+       const char *sender_name;
+       const char *sender_addressing;
+       const char *replyto_addressing;
+       const char *recipient_name;
+       const char *recipient_addressing;
+       const char *type;
+       const char *reception_status;
+       const char *size;
+       const char *attachment_size;
+       gboolean text;
+       gboolean read;
+       gboolean sent;
+       gboolean protect;
+       gboolean priority;
+       gboolean newmessage;
+       guint64 count;
+       uint8_t type_val;
+       uint8_t read_val;
+       uint8_t priority_val;
+       uint32_t mask;
+
+       struct session *session = user_data;
+       struct messages_message *data = g_new0(struct messages_message, 1);
+
+       DBG("+\n");
+       DBG("parameter_mask = %x; type = %d; period_begin = %s;"
+               "period_end = %s; read_status = %d; recipient = %s;"
+               "originator = %s; priority = %d",
+               session->filter->parameter_mask, session->filter->type,
+               session->filter->period_begin, session->filter->period_end,
+               session->filter->read_status, session->filter->recipient,
+               session->filter->originator, session->filter->priority);
+
+       dbus_error_init(&derr);
+
+       if (dbus_set_error_from_message(&derr, reply)) {
+               error("Replied with an error: %s, %s", derr.name, derr.message);
+               dbus_error_free(&derr);
+               session->msg_list_cb(session, -ENOENT, 0,
+                               FALSE, data, session->user_data);
+
+               g_free(data);
+               g_free(session->name);
+               session_filter_free(session->filter);
+               dbus_message_unref(reply);
+
+               return;
+       }
+
+       dbus_message_iter_init(reply, &iter);
+       dbus_message_iter_get_basic(&iter, &newmessage);
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_get_basic(&iter, &count);
+       dbus_message_iter_next(&iter);
+
+       if (session->max == 0)
+               goto done;
+
+       dbus_message_iter_recurse(&iter, &iter_struct);
+
+       if (session->filter->parameter_mask == 0)
+               mask = ~session->filter->parameter_mask;
+       else
+               mask = session->filter->parameter_mask;
+
+       while (dbus_message_iter_get_arg_type(&iter_struct) ==
+                                               DBUS_TYPE_STRUCT) {
+               dbus_message_iter_recurse(&iter_struct, &entry);
+               dbus_message_iter_get_basic(&entry, &msg_handle);
+
+               if (msg_handle == NULL) {
+                       dbus_message_iter_next(&iter_struct);
+                       continue;
+               }
+
+               DBG("Msg handle = %s \n", msg_handle);
+               data->handle = g_strdup(msg_handle);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &subject);
+
+               if (mask & PMASK_SUBJECT) {
+                       DBG("subject = %s\n", subject);
+                       data->subject = g_strndup(subject,
+                                               BT_MAP_LIST_ITEM_MAX_LEN);
+                       data->mask |= PMASK_SUBJECT;
+               }
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &datetime);
+
+               if ((mask & PMASK_DATETIME) && (NULL != datetime)) {
+                       DBG("datetime = %s\n", datetime);
+                       char *begin = g_strdup(session->filter->period_begin);
+                       char *end = g_strdup(session->filter->period_end);
+                       char *time = g_strdup(datetime);
+                       gboolean filter;
+
+                       filter = __filter_timebased(begin, end, time);
+
+                       g_free(begin);
+                       g_free(end);
+                       g_free(time);
+
+                       if (TRUE == filter) {
+                               data->datetime = g_strdup(datetime);
+                               data->mask |= PMASK_DATETIME;
+                       } else {
+                               message_list_item_free(data);
+                               dbus_message_iter_next(&iter_struct);
+                               continue;
+                       }
+               }
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &sender_name);
+
+               if ((mask & PMASK_SENDER_NAME) &&
+                               (NULL != session->filter->originator)) {
+                       DBG("sender_name = %s \n", sender_name);
+
+                       if (g_strstr_len(sender_name, -1,
+                                       session->filter->originator)) {
+                               data->sender_name = g_strndup(sender_name,
+                                               BT_MAP_LIST_ITEM_MAX_LEN);
+                               data->mask |= PMASK_SENDER_NAME;
+                       } else {
+                               message_list_item_free(data);
+                               dbus_message_iter_next(&iter_struct);
+                               continue;
+                       }
+               }
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &sender_addressing);
+
+               if ((mask & PMASK_SENDER_ADDRESSING) &&
+                                               (NULL != sender_addressing)) {
+                       DBG("sender_addressing = %s \n", sender_addressing);
+
+                       data->sender_addressing = g_strndup(sender_addressing,
+                                               BT_MAP_LIST_ITEM_MAX_LEN);
+                               data->mask |= PMASK_SENDER_ADDRESSING;
+               }
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &recipient_name);
+
+               if ((mask & PMASK_RECIPIENT_NAME) &&
+                               (NULL != session->filter->recipient)) {
+                       DBG("recipient_name = %s \n", recipient_name);
+
+                       if (g_strstr_len(recipient_name, -1,
+                                       session->filter->recipient)) {
+                               data->recipient_name =
+                                       g_strndup(recipient_name,
+                                       BT_MAP_LIST_ITEM_MAX_LEN);
+                               data->mask |= PMASK_RECIPIENT_NAME;
+                       } else {
+                               message_list_item_free(data);
+                               dbus_message_iter_next(&iter_struct);
+                               continue;
+                       }
+               }
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &recipient_addressing);
+
+               if ((mask & PMASK_RECIPIENT_ADDRESSING) &&
+                               (NULL != recipient_addressing)) {
+                       DBG("recipient_addressing=%s\n", recipient_addressing);
+
+                       data->recipient_addressing =
+                                       g_strndup(recipient_addressing,
+                                       BT_MAP_LIST_ITEM_MAX_LEN);
+                       data->mask |= PMASK_RECIPIENT_ADDRESSING;
+               }
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &type);
+
+               if ((mask & PMASK_TYPE) && (NULL != type)) {
+                       DBG("type = %s \n", type);
+
+                       type_val = get_type_val(type);
+                       if (!(session->filter->type & type_val)) {
+                               data->type = g_strdup(type);
+                               data->mask |= PMASK_TYPE;
+                       }
+               }
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &size);
+
+               if ((mask & PMASK_SIZE) && (NULL != size)) {
+                       DBG("size = %s \n", size);
+
+                       data->size = g_strdup(size);
+                       data->mask |= PMASK_SIZE;
+               }
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &reception_status);
+
+               if (mask & PMASK_RECEPTION_STATUS) {
+                       DBG("reception_status = %s \n", reception_status);
+
+                       data->reception_status = g_strdup(reception_status);
+                       data->mask |= PMASK_RECEPTION_STATUS;
+               }
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &text);
+
+               if (mask & PMASK_TEXT) {
+                       DBG("text = %d \n", text);
+                       data->text = text;
+                       data->mask |= PMASK_TEXT;
+               }
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &attachment_size);
+
+               if (mask & PMASK_ATTACHMENT_SIZE) {
+                       DBG("attachment_size = %s\n", attachment_size);
+
+                       data->attachment_size = g_strdup(attachment_size);
+                       data->mask |= PMASK_ATTACHMENT_SIZE;
+               }
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &priority);
+
+               if (mask & PMASK_PRIORITY) {
+                       DBG("priority = %d \n", priority);
+
+                       priority_val = get_priority_val(priority);
+                       if ((session->filter->priority == 0) ||
+                               (session->filter->priority & priority_val)) {
+                               data->priority = priority;
+                               data->mask |= PMASK_PRIORITY;
+                       } else {
+                               message_list_item_free(data);
+                               dbus_message_iter_next(&iter_struct);
+                               continue;
+                       }
+               }
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &read);
+
+               if (mask & PMASK_READ) {
+                       DBG("read = %d \n", read);
+
+                       read_val = get_read_status_val(read);
+
+                       if ((session->filter->read_status == 0) ||
+                               (session->filter->read_status & read_val)) {
+                               data->read = read;
+                               data->mask |= PMASK_READ;
+                       } else {
+                               message_list_item_free(data);
+                               dbus_message_iter_next(&iter_struct);
+                               continue;
+                       }
+               }
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &sent);
+
+               if (mask & PMASK_SENT) {
+                       DBG("sent = %d \n", sent);
+                       data->sent = sent;
+                       data->mask |= PMASK_SENT;
+               }
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &protect);
+
+               if (mask & PMASK_PROTECTED) {
+                       DBG("protect = %d \n", protect);
+                       data->protect = protect;
+                       data->mask |= PMASK_PROTECTED;
+               }
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &replyto_addressing);
+
+               if ((mask & PMASK_REPLYTO_ADDRESSING) &&
+                                               (0x04 == get_type_val(type))) {
+
+                       DBG("replyto_addressing = %s \n", replyto_addressing);
+                       if (replyto_addressing)
+                               data->replyto_addressing =
+                                               g_strdup(replyto_addressing);
+                       else
+                               data->replyto_addressing = g_strdup("");
+
+                       data->mask |= PMASK_REPLYTO_ADDRESSING;
+               }
+
+               session->msg_list_cb(session, -EAGAIN, 1, newmessage, data,
+                                                       session->user_data);
+
+               message_list_item_free(data);
+               dbus_message_iter_next(&iter_struct);
+       }
+
+done:
+       session->msg_list_cb(session, 0, count, newmessage, NULL,
+                                                       session->user_data);
+
+       g_free(data);
+       g_free(session->name);
+       session_filter_free(session->filter);
+       dbus_message_unref(reply);
+       DBG("-\n");
+}
+
+static void message_get_msg(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusMessageIter iter;
+       DBusError derr;
+       struct session *session = user_data;
+       char *msg_body;
+       gboolean fraction_deliver;
+
+       DBG("+\n");
+
+       dbus_error_init(&derr);
+       if (dbus_set_error_from_message(&derr, reply)) {
+               error("Replied with an error: %s, %s", derr.name, derr.message);
+               dbus_error_free(&derr);
+       } else {
+               dbus_message_iter_init(reply, &iter);
+               dbus_message_iter_get_basic(&iter, &fraction_deliver);
+               dbus_message_iter_next(&iter);
+               dbus_message_iter_get_basic(&iter, &msg_body);
+               DBG("msg_body %s\n", msg_body);
+
+               session->get_msg_cb(session, -EAGAIN, fraction_deliver,
+                                       msg_body, session->user_data);
+               session->get_msg_cb(session, 0, fraction_deliver,
+                                       NULL, session->user_data);
+       }
+       dbus_message_unref(reply);
+       DBG("-\n");
+}
+
+int messages_connect(void **s)
+{
+       DBusMessage *message;
+       DBusMessage *reply;
+       DBusError err;
+       DBG("+\n");
+
+       struct session *session = g_new0(struct session, 1);
+
+       create_folder_tree();
+
+       session->cwd = g_strdup("/");
+       session->folder = folder_tree;
+
+       *s = session;
+
+       message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+                                               BT_MAP_SERVICE_OBJECT_PATH,
+                                               BT_MAP_SERVICE_INTERFACE,
+                                               QUERY_GET_FOLDER_TREE);
+       if (!message) {
+               error("Can't allocate new message");
+               return -1;
+       }
+
+       dbus_error_init(&err);
+
+       reply = dbus_connection_send_with_reply_and_block(g_conn, message,
+                                               DBUS_TIMEOUT_USE_DEFAULT, &err);
+       if (!reply) {
+               DBG(" Reply failed");
+               if (dbus_error_is_set(&err)) {
+                       DBG("%s", err.message);
+                       dbus_error_free(&err);
+               }
+
+               dbus_message_unref(message);
+               return -1;
+       }
+
+       message_get_folder_list(reply, session);
+
+       dbus_message_unref(message);
+       DBG("-\n");
+       return 0;
+}
+
+void messages_disconnect(void *s)
+{
+       DBusMessage *message;
+       struct session *session = s;
+       DBG("+\n");
+
+       destroy_folder_tree(folder_tree);
+       folder_tree = NULL;
+       g_free(session->cwd);
+       g_free(session);
+
+       message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+                                               BT_MAP_SERVICE_OBJECT_PATH,
+                                               BT_MAP_SERVICE_INTERFACE,
+                                               QUERY_DESTROY_AGENT);
+
+       if (!message) {
+               error("Can't allocate new message");
+               return;
+       }
+
+       if (dbus_connection_send(g_conn, message, NULL) == FALSE)
+               error("Could not send dbus message");
+
+       dbus_message_unref(message);
+
+       DBG("-\n");
+}
+
+static gboolean notification_registration(gpointer user_data)
+{
+       DBG("+\n");
+       DBusMessage *message = NULL;
+       gboolean reg;
+       struct mns_reg_data *data = (struct mns_reg_data *)user_data;
+
+       message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+                                       BT_MAP_SERVICE_OBJECT_PATH,
+                                       BT_MAP_SERVICE_INTERFACE,
+                                       QUERY_NOTI_REGISTRATION);
+       if (!message) {
+               error("Can't allocate new message");
+               goto done;
+       }
+
+       DBG("data->notification_status = %d\n", data->notification_status);
+
+       if (data->notification_status == 1)
+               reg = TRUE;
+       else
+               reg = FALSE;
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &data->remote_addr,
+                                               DBUS_TYPE_BOOLEAN, &reg,
+                                               DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send(g_conn, message, NULL) == FALSE)
+               error("Could not send dbus message");
+
+       dbus_message_unref(message);
+
+done:
+       g_free(data->remote_addr);
+       g_free(data);
+
+       DBG("-\n");
+       return FALSE;
+}
+
+int messages_set_notification_registration(void *session,
+                               char *address, uint8_t status,
+                               void *user_data)
+{
+       DBG("+\n");
+       struct mns_reg_data *data = g_new0(struct mns_reg_data, 1);
+       data->notification_status = status;
+       data->remote_addr = g_strdup(address);
+
+       DBG("status = %d\n", status);
+
+       g_idle_add(notification_registration, data);
+       DBG("-\n");
+       return 1;
+}
+
+int messages_set_folder(void *s, const char *name, gboolean cdup)
+{
+       struct session *session = s;
+       char *newrel = NULL;
+       char *newabs;
+       char *tmp;
+
+       if (name && (strchr(name, '/') || strcmp(name, "..") == 0))
+               return -EBADR;
+
+       if (cdup) {
+               if (session->cwd[0] == 0)
+                       return -ENOENT;
+
+               newrel = g_path_get_dirname(session->cwd);
+
+               /* We use empty string for indication of the root directory */
+               if (newrel[0] == '.' && newrel[1] == 0)
+                       newrel[0] = 0;
+       }
+
+       tmp = newrel;
+       if (!cdup && (!name || name[0] == 0))
+               newrel = g_strdup("");
+       else
+               newrel = g_build_filename(newrel ? newrel : session->cwd, name,
+                                                                       NULL);
+       g_free(tmp);
+
+       if (newrel[0] != '/')
+               newabs = g_build_filename("/", newrel, NULL);
+       else
+               newabs = g_strdup(newrel);
+
+       session->folder = get_folder(newabs);
+       if (session->folder == NULL) {
+               g_free(newrel);
+               g_free(newabs);
+
+               return -ENOENT;
+       }
+
+       g_free(newrel);
+       g_free(session->cwd);
+       session->cwd = newabs;
+
+       return 0;
+}
+
+static gboolean async_get_folder_listing(void *s)
+{
+       struct session *session = s;
+       int i;
+       uint16_t folder_list_size = 0;
+       char *path = NULL;
+       struct message_folder *folder;
+       GSList *dir;
+
+       if (session->name && strchr(session->name, '/') != NULL)
+               goto done;
+
+       path = g_build_filename(session->cwd, session->name, NULL);
+
+       if (path == NULL || strlen(path) == 0)
+               goto done;
+
+       folder = get_folder(path);
+
+       if (folder == NULL)
+               goto done;
+
+       if (session->max == 0) {
+               folder_list_size = g_slist_length(folder->subfolders);
+               goto done;
+       }
+
+       dir = folder->subfolders;
+
+       /* move to offset */
+       for (i = 0; i < session->offset; i++) {
+               if (dir == NULL)
+                       goto done;
+
+               dir = g_slist_next(dir);
+       }
+
+       for (i = 0; i < session->max; i++) {
+               struct message_folder *dir_data;
+
+               if (dir == NULL)
+                       goto done;
+
+               dir_data = dir->data;
+               session->folder_list_cb(session, -EAGAIN, 0,
+                               dir_data->name, session->user_data);
+
+               dir = g_slist_next(dir);
+       }
+
+ done:
+       session->folder_list_cb(session, 0, folder_list_size,
+                       NULL, session->user_data);
+
+       g_free(path);
+       g_free(session->name);
+
+       return FALSE;
+}
+
+int messages_get_folder_listing(void *s, const char *name,
+                                       uint16_t max, uint16_t offset,
+                                       messages_folder_listing_cb callback,
+                                       void *user_data)
+{
+       DBG("+\n");
+       struct session *session = s;
+       session->name = g_strdup(name);
+       session->max = max;
+       session->offset = offset;
+       session->folder_list_cb = callback;
+       session->user_data = user_data;
+
+       g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, async_get_folder_listing,
+                                               session, NULL);
+
+       DBG("-\n");
+       return 0;
+}
+
+
+static void append_variant(DBusMessageIter *iter, int type, void *val)
+{
+       DBusMessageIter value;
+       char sig[2] = { type, '\0' };
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
+       dbus_message_iter_append_basic(&value, type, val);
+       dbus_message_iter_close_container(iter, &value);
+}
+
+static void dict_append_entry(DBusMessageIter *dict, const char *key,
+                                       int type, void *val)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+       append_variant(&entry, type, val);
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+int messages_get_messages_listing(void *session, const char *name,
+                               uint16_t max, uint16_t offset,
+                               uint8_t subject_len,
+                               const struct messages_filter *filter,
+                               messages_get_messages_listing_cb callback,
+                               void *user_data)
+{
+       DBusPendingCall *call;
+       DBusMessage *message;
+       struct session *s = session;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+
+       if (name != NULL && strlen(name))
+               s->name = g_strdup(name);
+       else
+               s->name = g_strdup(s->cwd);
+
+       s->max = max;
+       s->offset = offset;
+
+       s->filter = g_new0(struct messages_filter, 1);
+       s->filter->parameter_mask = filter->parameter_mask;
+       s->filter->type = filter->type;
+       s->filter->period_begin = g_strdup(filter->period_begin);
+       s->filter->period_end = g_strdup(filter->period_end);
+       s->filter->read_status = filter->read_status;
+       s->filter->recipient = g_strdup(filter->recipient);
+       s->filter->originator = g_strdup(filter->originator);
+       s->filter->priority = filter->priority;
+
+       s->msg_list_cb = (void *)callback;
+       s->user_data = user_data;
+
+       message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+                                               BT_MAP_SERVICE_OBJECT_PATH,
+                                               BT_MAP_SERVICE_INTERFACE,
+                                               QUERY_GET_MSG_LIST);
+       if (!message) {
+               error("Can't allocate new message");
+               g_free(s->name);
+               session_filter_free(s->filter);
+               return -1;
+       }
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &s->name,
+                                               DBUS_TYPE_UINT16, &s->max,
+                                               DBUS_TYPE_UINT16, &s->offset,
+                                               DBUS_TYPE_BYTE, &subject_len,
+                                               DBUS_TYPE_INVALID);
+
+       dbus_message_iter_init_append(message, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING
+                       DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       if (filter->parameter_mask)
+               dict_append_entry(&dict, "ParameterMask", DBUS_TYPE_UINT32,
+                                       &filter->parameter_mask);
+       if (filter->type)
+               dict_append_entry(&dict, "FilterMessageType", DBUS_TYPE_BYTE,
+                                       &filter->type);
+       if (filter->period_begin)
+               dict_append_entry(&dict, "FilterPeriodBegin", DBUS_TYPE_STRING,
+                                       &filter->period_begin);
+       if (filter->period_end)
+               dict_append_entry(&dict, "FilterPeriodEnd", DBUS_TYPE_STRING,
+                                       &filter->period_end);
+       if (filter->read_status)
+               dict_append_entry(&dict, "FilterReadStatus", DBUS_TYPE_BYTE,
+                                       &filter->read_status);
+       if (filter->recipient)
+               dict_append_entry(&dict, "FilterRecipient", DBUS_TYPE_STRING,
+                                       &filter->recipient);
+       if (filter->originator)
+               dict_append_entry(&dict, "FilterOriginator", DBUS_TYPE_STRING,
+                                       &filter->originator);
+       if (filter->priority)
+               dict_append_entry(&dict, "FilterPriority", DBUS_TYPE_BYTE,
+                                       &filter->priority);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       if (dbus_connection_send_with_reply(g_conn, message, &call,
+                                       DBUS_TIMEOUT_INFINITE) == FALSE) {
+               error("Could not send dbus message");
+               dbus_message_unref(message);
+               g_free(s->name);
+               session_filter_free(s->filter);
+               return -1;
+       }
+       dbus_pending_call_set_notify(call, message_get_msg_list, s, NULL);
+       dbus_message_unref(message);
+       DBG("-\n");
+       return 1;
+}
+
+int messages_push_message(void *session, const char *folder,
+                                       uint8_t transparent, uint8_t retry,
+                                       uint8_t charset,
+                                       messages_push_message_cb callback,
+                                       void *user_data)
+{
+       DBusMessage *message;
+       DBusMessage *reply;
+       DBusError err;
+       struct session *s = session;
+
+       gboolean save_copy = FALSE;  /* As per specs default value */
+       gboolean retry_send = TRUE; /* As per specs default value */
+       gboolean native = FALSE;
+       gchar *folder_path = NULL;
+       guint64 handle = 0;
+
+       DBG("+\n");
+
+       DBG("session->cwd %s +\n", s->cwd);
+
+       if (g_ascii_strncasecmp(s->cwd, "/telecom/msg",
+                       strlen("/telecom/msg")) != 0) {
+               DBG("Path Not Set properly");
+               return -1;
+       }
+
+       if ((folder[0] == '\0') && (g_strcmp0(s->cwd, "/telecom/msg") == 0)) {
+               DBG("Invalid Folders");
+               return -1;
+       }
+
+       if ((folder[0] != '\0'))
+               folder_path = g_strconcat("/telecom/msg/", folder, NULL);
+       else
+               folder_path = g_strdup(s->cwd);
+
+       s->push_msg_cb = callback;
+       s->user_data = user_data;
+
+       if (transparent & 0x1)
+               save_copy = TRUE;
+
+       if (!(retry & 0x1)) {
+               retry_send = FALSE;
+               DBG("Retry send %d\n", retry_send);
+       }
+
+       if (charset & 0x1) {
+               native = TRUE;
+               DBG("native send %d\n", native);
+       }
+
+       DBG("save_copy  %d\n", save_copy);
+       DBG("retry_send %d\n", retry_send);
+       DBG("native %d\n", native);
+
+       message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+                                               BT_MAP_SERVICE_OBJECT_PATH,
+                                               BT_MAP_SERVICE_INTERFACE,
+                                               QUERY_PUSH_MESSAGE);
+       if (!message) {
+               error("Can't allocate new message");
+               g_free(folder_path);
+               return -1;
+       }
+
+       dbus_message_append_args(message, DBUS_TYPE_BOOLEAN, &save_copy,
+                                               DBUS_TYPE_BOOLEAN, &retry_send,
+                                               DBUS_TYPE_BOOLEAN, &native,
+                                               DBUS_TYPE_STRING, &folder_path,
+                                               DBUS_TYPE_INVALID);
+
+       dbus_error_init(&err);
+
+       reply = dbus_connection_send_with_reply_and_block(
+                                       g_conn, message,
+                                       DBUS_TIMEOUT_USE_DEFAULT, &err);
+       if (!reply) {
+               DBG(" Reply failed");
+
+               if (dbus_error_is_set(&err)) {
+                       DBG("%s", err.message);
+                       dbus_error_free(&err);
+               }
+               g_free(folder_path);
+               dbus_message_unref(message);
+               return -1;
+       }
+
+       if (!dbus_message_get_args(reply, &err, DBUS_TYPE_UINT64,
+                                               &handle, DBUS_TYPE_INVALID)) {
+               if (dbus_error_is_set(&err)) {
+                       error("err %s\n", err.message);
+                       dbus_error_free(&err);
+               }
+               g_free(folder_path);
+               dbus_message_unref(message);
+               dbus_message_unref(reply);
+               return -1;
+       }
+
+       DBG("uint64 handle %"G_GUINT64_FORMAT"\n", handle);
+       s->push_msg_cb(s, 0, handle, s->user_data);
+
+       g_free(folder_path);
+       dbus_message_unref(message);
+       dbus_message_unref(reply);
+
+       DBG("-\n");
+       return 1;
+}
+
+int messages_push_message_data(void *session, const char *bmsg, void *user_data)
+{
+       DBusMessage *message;
+
+       DBG("+\n");
+
+       message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+                                               BT_MAP_SERVICE_OBJECT_PATH,
+                                               BT_MAP_SERVICE_INTERFACE,
+                                               QUERY_PUSH_MESSAGE_DATA);
+       if (!message) {
+               error("Can't allocate new message");
+               return -1;
+       }
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &bmsg,
+                                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send(g_conn, message, NULL) == FALSE) {
+               error("Could not send dbus message");
+               dbus_message_unref(message);
+               return -1;
+       }
+
+       dbus_message_unref(message);
+       DBG("-\n");
+       return 1;
+}
+
+int messages_get_message(void *session,
+                                       const char *handle,
+                                       uint8_t attachment, uint8_t charset,
+                                       uint8_t fraction_request,
+                                       messages_get_message_cb callback,
+                                       void *user_data)
+{
+       DBusPendingCall *call;
+       DBusMessage *message;
+       struct session *s = session;
+       char *message_name;
+       gboolean attach = FALSE;
+       gboolean transcode = FALSE;
+       gboolean first_request = TRUE;
+
+       DBG("+\n");
+
+       if (NULL != handle) {
+               message_name =  g_strdup(handle);
+               DBG("Message handle = %s\n", handle);
+       } else {
+               return -1;
+       }
+       s->get_msg_cb = callback;
+       s->user_data = user_data;
+
+       message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+                                               BT_MAP_SERVICE_OBJECT_PATH,
+                                               BT_MAP_SERVICE_INTERFACE,
+                                               QUERY_GET_MESSAGE);
+       if (!message) {
+               error("Can't allocate new message");
+               g_free(message_name);
+               return -1;
+       }
+
+       if (attachment & 0x1)
+               attach = TRUE;
+
+       if (charset & 0x1)
+               transcode = TRUE;
+
+       if (fraction_request & 0x1)
+               first_request = FALSE;
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &message_name,
+                                       DBUS_TYPE_BOOLEAN, &attach,
+                                       DBUS_TYPE_BOOLEAN, &transcode,
+                                       DBUS_TYPE_BOOLEAN, &first_request,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(g_conn, message, &call, -1) ==
+                                       FALSE) {
+               error("Could not send dbus message");
+               dbus_message_unref(message);
+               g_free(message_name);
+               return -1;
+       }
+       dbus_pending_call_set_notify(call, message_get_msg, s, NULL);
+       dbus_message_unref(message);
+       g_free(message_name);
+       DBG("-\n");
+       return 1;
+}
+
+static void message_update_msg(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusMessageIter iter;
+       DBusError derr;
+       struct session *session = user_data;
+       int err;
+       DBG("+\n");
+
+       dbus_error_init(&derr);
+       if (dbus_set_error_from_message(&derr, reply)) {
+               error("Replied with an error: %s, %s", derr.name, derr.message);
+               dbus_error_free(&derr);
+       } else {
+               dbus_message_iter_init(reply, &iter);
+               if (dbus_message_iter_get_arg_type(&iter) ==
+                                                       DBUS_TYPE_INT32) {
+                       dbus_message_iter_get_basic(&iter, &err);
+                       DBG("Error : %d\n", err);
+                       session->msg_update_cb(session, err,
+                                               session->user_data);
+               }
+       }
+       dbus_message_unref(reply);
+       DBG("-\n");
+}
+
+int messages_update_inbox(void *session,
+                                       messages_status_cb callback,
+                                       void *user_data)
+{
+       if (TIZEN_FEATURE_BLUEZ_SMS_ONLY) {
+               /* MAP.TS.1.0.3 : TP/MMB/BV-16-I
+                  Currently support is only for SMS, Since SMS service does not
+                  allow the polling of its mailbox, it must return Not implemented */
+
+               return -ENOSYS;
+       }
+
+       DBusPendingCall *call;
+       DBusMessage *message;
+       struct session *s = session;
+
+       DBG("+\n");
+
+       s->msg_update_cb = callback;
+       s->user_data = user_data;
+
+       message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+                                               BT_MAP_SERVICE_OBJECT_PATH,
+                                               BT_MAP_SERVICE_INTERFACE,
+                                               QUERY_UPDATE_MESSAGE);
+       if (!message) {
+               error("Can't allocate new message");
+               return -1;
+       }
+
+       if (dbus_connection_send_with_reply(g_conn, message, &call, -1) ==
+                                       FALSE) {
+               error("Could not send dbus message");
+               dbus_message_unref(message);
+               return -1;
+       }
+       dbus_pending_call_set_notify(call, message_update_msg, s, NULL);
+       dbus_message_unref(message);
+       DBG("-\n");
+       return 1;
+}
+
+static void message_status_msg(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusMessageIter iter;
+       DBusError derr;
+       struct session *session = user_data;
+       int err;
+
+       DBG("+\n");
+
+       dbus_error_init(&derr);
+       if (dbus_set_error_from_message(&derr, reply)) {
+               error("Replied with an error: %s, %s", derr.name, derr.message);
+               dbus_error_free(&derr);
+       } else {
+               dbus_message_iter_init(reply, &iter);
+               if (dbus_message_iter_get_arg_type(&iter) ==
+                                                       DBUS_TYPE_INT32) {
+                       dbus_message_iter_get_basic(&iter, &err);
+                       DBG("Error : %d\n", err);
+                       session->msg_status_cb(session, err,
+                                               session->user_data);
+               }
+       }
+       dbus_message_unref(reply);
+       DBG("-\n");
+}
+
+int messages_set_read(void *session, const char *handle, uint8_t value,
+               messages_status_cb callback, void *user_data)
+{
+       DBusPendingCall *call;
+       DBusMessage *message;
+       struct session *s = session;
+       char *message_name;
+       gboolean read;
+
+       DBG("+\n");
+
+       if (NULL == handle)
+               return -1;
+
+       DBG("Message handle = %s\n", handle);
+       message_name = g_strdup(handle);
+
+       s->msg_status_cb = callback;
+       s->user_data = user_data;
+
+       message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+                                               BT_MAP_SERVICE_OBJECT_PATH,
+                                               BT_MAP_SERVICE_INTERFACE,
+                                               QUERY_SET_READ_STATUS);
+       if (!message) {
+               error("Can't allocate new message");
+               g_free(message_name);
+               return -1;
+       }
+
+       read = value ? TRUE : FALSE;
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &message_name,
+                                               DBUS_TYPE_BOOLEAN, &read,
+                                               DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(g_conn, message, &call, -1) ==
+                                       FALSE) {
+               error("Could not send dbus message");
+               g_free(message_name);
+               dbus_message_unref(message);
+               return -1;
+       }
+
+       dbus_pending_call_set_notify(call, message_status_msg, s, NULL);
+       dbus_message_unref(message);
+       g_free(message_name);
+       DBG("-\n");
+       return 1;
+}
+
+int messages_set_delete(void *session, const char *handle,
+                                       uint8_t value,
+                                       messages_status_cb callback,
+                                       void *user_data)
+{
+       DBusPendingCall *call;
+       DBusMessage *message;
+       struct session *s = session;
+       char *message_name;
+       gboolean del;
+
+       DBG("+\n");
+
+       if (NULL == handle)
+               return -1;
+
+       DBG("Message handle = %s\n", handle);
+       message_name = g_strdup(handle);
+
+       s->msg_status_cb = callback;
+       s->user_data = user_data;
+
+       message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+                                               BT_MAP_SERVICE_OBJECT_PATH,
+                                               BT_MAP_SERVICE_INTERFACE,
+                                               QUERY_SET_DELETE_STATUS);
+       if (!message) {
+               error("Can't allocate new message");
+               g_free(message_name);
+               return -1;
+       }
+
+       del = value ? TRUE : FALSE;
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &message_name,
+                                               DBUS_TYPE_BOOLEAN, &del,
+                                               DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(g_conn, message, &call, -1) ==
+                                       FALSE) {
+               error("Could not send dbus message");
+               g_free(message_name);
+               dbus_message_unref(message);
+               return -1;
+       }
+
+       dbus_pending_call_set_notify(call, message_status_msg, s, NULL);
+       dbus_message_unref(message);
+       g_free(message_name);
+       DBG("-\n");
+       return 1;
+}
+
+void messages_abort(void *session)
+{
+}
diff --git a/obexd/plugins/messages-tracker.c b/obexd/plugins/messages-tracker.c
new file mode 100755 (executable)
index 0000000..60f3a80
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation
+ *
+ *
+ *  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 <errno.h>
+#include <glib.h>
+#include <string.h>
+
+#include "messages.h"
+
+struct message_folder {
+       char *name;
+       GSList *subfolders;
+       char *query;
+};
+
+struct session {
+       char *cwd;
+       struct message_folder *folder;
+       char *name;
+       uint16_t max;
+       uint16_t offset;
+       void *user_data;
+       void (*folder_list_cb)(void *session, int err, uint16_t size,
+                                       const char *name, void *user_data);
+};
+
+static struct message_folder *folder_tree = NULL;
+
+static struct message_folder *get_folder(const char *folder)
+{
+       GSList *folders = folder_tree->subfolders;
+       struct message_folder *last = NULL;
+       char **path;
+       int i;
+
+       if (g_strcmp0(folder, "/") == 0)
+               return folder_tree;
+
+       path = g_strsplit(folder, "/", 0);
+
+       for (i = 1; path[i] != NULL; i++) {
+               gboolean match_found = FALSE;
+               GSList *l;
+
+               for (l = folders; l != NULL; l = g_slist_next(l)) {
+                       struct message_folder *folder = l->data;
+
+                       if (g_strcmp0(folder->name, path[i]) == 0) {
+                               match_found = TRUE;
+                               last = l->data;
+                               folders = folder->subfolders;
+                               break;
+                       }
+               }
+
+               if (!match_found) {
+                       g_strfreev(path);
+                       return NULL;
+               }
+       }
+
+       g_strfreev(path);
+
+       return last;
+}
+
+static struct message_folder *create_folder(const char *name, const char *query)
+{
+       struct message_folder *folder = g_new0(struct message_folder, 1);
+
+       folder->name = g_strdup(name);
+       folder->query = g_strdup(query);
+
+       return folder;
+}
+
+static void destroy_folder_tree(void *root)
+{
+       struct message_folder *folder = root;
+       GSList *tmp, *next;
+
+       if (folder == NULL)
+               return;
+
+       g_free(folder->name);
+       g_free(folder->query);
+
+       tmp = folder->subfolders;
+       while (tmp != NULL) {
+               next = g_slist_next(tmp);
+               destroy_folder_tree(tmp->data);
+               tmp = next;
+       }
+
+       g_slist_free(folder->subfolders);
+       g_free(folder);
+}
+
+static void create_folder_tree(void)
+{
+       struct message_folder *parent, *child;
+
+       folder_tree = create_folder("/", "FILTER (!BOUND(?msg))");
+
+       parent = create_folder("telecom", "FILTER (!BOUND(?msg))");
+       folder_tree->subfolders = g_slist_append(folder_tree->subfolders,
+                                                               parent);
+
+       child = create_folder("msg", "FILTER (!BOUND(?msg))");
+       parent->subfolders = g_slist_append(parent->subfolders, child);
+
+       parent = child;
+
+       child = create_folder("inbox", "?msg nmo:isSent \"false\" ; "
+                               "nmo:isDeleted \"false\" ; "
+                               "nmo:isDraft \"false\". ");
+       parent->subfolders = g_slist_append(parent->subfolders, child);
+
+       child = create_folder("sent", "?msg nmo:isDeleted \"false\" ; "
+                               "nmo:isSent \"true\" . ");
+       parent->subfolders = g_slist_append(parent->subfolders, child);
+
+       child = create_folder("deleted", "?msg nmo:isDeleted \"true\" . ");
+       parent->subfolders = g_slist_append(parent->subfolders, child);
+}
+
+int messages_init(void)
+{
+       create_folder_tree();
+
+       return 0;
+}
+
+void messages_exit(void)
+{
+       destroy_folder_tree(folder_tree);
+}
+
+int messages_connect(void **s)
+{
+       struct session *session = g_new0(struct session, 1);
+
+       session->cwd = g_strdup("/");
+       session->folder = folder_tree;
+
+       *s = session;
+
+       return 0;
+}
+
+void messages_disconnect(void *s)
+{
+       struct session *session = s;
+
+       g_free(session->cwd);
+       g_free(session);
+}
+
+int messages_set_notification_registration(void *session,
+               void (*send_event)(void *session,
+                       const struct messages_event *event, void *user_data),
+               void *user_data)
+{
+       return -ENOSYS;
+}
+
+int messages_set_folder(void *s, const char *name, gboolean cdup)
+{
+       struct session *session = s;
+       char *newrel = NULL;
+       char *newabs;
+       char *tmp;
+
+       if (name && (strchr(name, '/') || strcmp(name, "..") == 0))
+               return -EBADR;
+
+       if (cdup) {
+               if (session->cwd[0] == 0)
+                       return -ENOENT;
+
+               newrel = g_path_get_dirname(session->cwd);
+
+               /* We use empty string for indication of the root directory */
+               if (newrel[0] == '.' && newrel[1] == 0)
+                       newrel[0] = 0;
+       }
+
+       tmp = newrel;
+       if (!cdup && (!name || name[0] == 0))
+               newrel = g_strdup("");
+       else
+               newrel = g_build_filename(newrel ? newrel : session->cwd, name,
+                                                                       NULL);
+       g_free(tmp);
+
+       if (newrel[0] != '/')
+               newabs = g_build_filename("/", newrel, NULL);
+       else
+               newabs = g_strdup(newrel);
+
+       session->folder = get_folder(newabs);
+       if (session->folder == NULL) {
+               g_free(newrel);
+               g_free(newabs);
+
+               return -ENOENT;
+       }
+
+       g_free(newrel);
+       g_free(session->cwd);
+       session->cwd = newabs;
+
+       return 0;
+}
+
+static gboolean async_get_folder_listing(void *s)
+{
+       struct session *session = s;
+       gboolean count = FALSE;
+       int folder_count = 0;
+       char *path = NULL;
+       struct message_folder *folder;
+       GSList *dir;
+
+       if (session->name && strchr(session->name, '/') != NULL)
+               goto done;
+
+       path = g_build_filename(session->cwd, session->name, NULL);
+
+       if (path == NULL || strlen(path) == 0)
+               goto done;
+
+       folder = get_folder(path);
+
+       if (folder == NULL)
+               goto done;
+
+       if (session->max == 0) {
+               session->max = 0xffff;
+               session->offset = 0;
+               count = TRUE;
+       }
+
+       for (dir = folder->subfolders; dir &&
+                               (folder_count - session->offset) < session->max;
+                               folder_count++, dir = g_slist_next(dir)) {
+               struct message_folder *dir_data = dir->data;
+
+               if (count == FALSE && session->offset <= folder_count)
+                       session->folder_list_cb(session, -EAGAIN, 0,
+                                       dir_data->name, session->user_data);
+       }
+
+ done:
+       session->folder_list_cb(session, 0, folder_count, NULL,
+                                                       session->user_data);
+
+       g_free(path);
+       g_free(session->name);
+
+       return FALSE;
+}
+
+int messages_get_folder_listing(void *s, const char *name,
+                                       uint16_t max, uint16_t offset,
+                                       messages_folder_listing_cb callback,
+                                       void *user_data)
+{
+       struct session *session = s;
+       session->name = g_strdup(name);
+       session->max = max;
+       session->offset = offset;
+       session->folder_list_cb = callback;
+       session->user_data = user_data;
+
+       g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, async_get_folder_listing,
+                                               session, NULL);
+
+       return 0;
+}
+
+int messages_get_messages_listing(void *session, const char *name,
+                               uint16_t max, uint16_t offset,
+                               uint8_t subject_len,
+                               const struct messages_filter *filter,
+                               messages_get_messages_listing_cb callback,
+                               void *user_data)
+{
+       return -ENOSYS;
+}
+
+int messages_get_message(void *session, const char *handle,
+                               unsigned long flags,
+                               messages_get_message_cb callback,
+                               void *user_data)
+{
+       return -ENOSYS;
+}
+
+int messages_update_inbox(void *session, messages_status_cb callback,
+                                                       void *user_data)
+{
+       return -ENOSYS;
+}
+
+int messages_set_read(void *session, const char *handle, uint8_t value,
+                               messages_status_cb callback, void *user_data)
+{
+       return -ENOSYS;
+}
+
+int messages_set_delete(void *session, const char *handle, uint8_t value,
+                                       messages_status_cb callback,
+                                       void *user_data)
+{
+       return -ENOSYS;
+}
+
+void messages_abort(void *session)
+{
+}
index ad93208..68a2139 100755 (executable)
 #define PHONEBOOKSIZE_TAG      0X08
 #define NEWMISSEDCALLS_TAG     0X09
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define PBAP_MAXLISTCOUNT_MAX_VALUE 65535
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
 struct cache {
        gboolean valid;
        uint32_t index;
@@ -85,6 +89,9 @@ struct pbap_session {
        uint32_t find_handle;
        struct cache cache;
        struct pbap_object *obj;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       void *backend_data;
+#endif
 };
 
 struct pbap_object {
@@ -194,11 +201,18 @@ static void phonebook_size_result(const char *buffer, size_t bufsize,
                                                                phonebooksize);
 
        pbap->obj->firstpacket = TRUE;
-
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (missed > 0) {
+#else
+       if (pbap->params->required_missedcall_call_header == TRUE) {
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
                DBG("missed %d", missed);
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                pbap->obj->apparam = g_obex_apparam_set_uint16(
+#else
+               pbap->obj->apparam = g_obex_apparam_set_uint8(
+#endif
                                                        pbap->obj->apparam,
                                                        NEWMISSEDCALLS_TAG,
                                                        missed);
@@ -231,13 +245,20 @@ static void query_result(const char *buffer, size_t bufsize, int vcards,
        else
                pbap->obj->buffer = g_string_append_len(pbap->obj->buffer,
                                                        buffer, bufsize);
-
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (missed > 0) {
+#else
+       if (pbap->params->required_missedcall_call_header == TRUE) {
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
                DBG("missed %d", missed);
 
                pbap->obj->firstpacket = TRUE;
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                pbap->obj->apparam = g_obex_apparam_set_uint16(
+#else
+               pbap->obj->apparam = g_obex_apparam_set_uint8(
+#endif
                                                        pbap->obj->apparam,
                                                        NEWMISSEDCALLS_TAG,
                                                        missed);
@@ -373,9 +394,27 @@ static int generate_response(void *user_data)
                                                        pbap->obj->apparam,
                                                        PHONEBOOKSIZE_TAG,
                                                        size);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               if (pbap->params->required_missedcall_call_header == TRUE) {
+                               //DBG("missed %d", missed);
+                               pbap->obj->apparam = g_obex_apparam_set_uint8(
+                                                                       pbap->obj->apparam,
+                                                                       NEWMISSEDCALLS_TAG,
+                                                                       pbap->params->new_missed_calls);
+                       }
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
 
                return 0;
        }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (pbap->params->required_missedcall_call_header == TRUE) {
+               pbap->obj->firstpacket = TRUE;
+               pbap->obj->apparam = g_obex_apparam_set_uint8(
+                                                       pbap->obj->apparam,
+                                                       NEWMISSEDCALLS_TAG,
+                                                       pbap->params->new_missed_calls);
+       }
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
 
        /*
         * Don't free the sorted list content: this list contains
@@ -406,12 +445,18 @@ static int generate_response(void *user_data)
        return 0;
 }
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
 static void cache_ready_notify(void *user_data)
+#else
+static void cache_ready_notify(void *user_data, unsigned int new_missed_call)
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
 {
        struct pbap_session *pbap = user_data;
 
        DBG("");
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       pbap->params->new_missed_calls = new_missed_call;
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
        phonebook_req_finalize(pbap->obj->request);
        pbap->obj->request = NULL;
 
@@ -445,10 +490,27 @@ static void cache_entry_done(void *user_data)
                obex_object_set_io_flags(pbap->obj, G_IO_ERR, ret);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void cache_clear_notify(void *user_data)
+{
+       struct pbap_session *pbap = user_data;
+
+       if (pbap == NULL)
+               return;
+
+       pbap->cache.valid = FALSE;
+       pbap->cache.index = 0;
+       cache_clear(&pbap->cache);
+}
+#endif
+
 static struct apparam_field *parse_aparam(const uint8_t *buffer, uint32_t hlen)
 {
        GObexApparam *apparam;
        struct apparam_field *param;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       gboolean bmaxlistCount = FALSE;
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
 
        apparam = g_obex_apparam_decode(buffer, hlen);
        if (apparam == NULL)
@@ -466,13 +528,27 @@ static struct apparam_field *parse_aparam(const uint8_t *buffer, uint32_t hlen)
        g_obex_apparam_get_uint8(apparam, SEARCHATTRIB_TAG,
                                                &param->searchattrib);
        g_obex_apparam_get_uint8(apparam, FORMAT_TAG, &param->format);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       bmaxlistCount = g_obex_apparam_get_uint16(apparam, MAXLISTCOUNT_TAG,
+                                               &param->maxlistcount);
+#else
        g_obex_apparam_get_uint16(apparam, MAXLISTCOUNT_TAG,
                                                &param->maxlistcount);
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
        g_obex_apparam_get_uint16(apparam, LISTSTARTOFFSET_TAG,
                                                &param->liststartoffset);
        g_obex_apparam_get_uint64(apparam, FILTER_TAG, &param->filter);
        param->searchval = g_obex_apparam_get_string(apparam, SEARCHVALUE_TAG);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if(bmaxlistCount == FALSE) {
+               param->maxlistcount = PBAP_MAXLISTCOUNT_MAX_VALUE;
+       }
+       else {
+               /* Keep the MaxlistCount value as send in request from client */
+       }
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
        DBG("o %x sa %x sv %s fil %" G_GINT64_MODIFIER "x for %x max %x off %x",
                        param->order, param->searchattrib, param->searchval,
                        param->filter, param->format, param->maxlistcount,
@@ -493,10 +569,28 @@ static void *pbap_connect(struct obex_session *os, int *err)
        pbap->folder = g_strdup("/");
        pbap->find_handle = PHONEBOOK_INVALID_HANDLE;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       int error;
+
+       error = phonebook_connect(&pbap->backend_data);
+       if (error < 0) {
+               if (err)
+                       *err = error;
+               goto failed;
+       }
+#endif
+
        if (err)
                *err = 0;
 
        return pbap;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+failed:
+       g_free(pbap);
+
+       return NULL;
+#endif
 }
 
 static int pbap_get(struct obex_session *os, void *user_data)
@@ -612,6 +706,13 @@ static void pbap_disconnect(struct obex_session *os, void *user_data)
 
        manager_unregister_session(os);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (!pbap)
+               return;
+
+       phonebook_disconnect(pbap->backend_data);
+#endif
+
        if (pbap->obj)
                pbap->obj->session = NULL;
 
@@ -676,6 +777,13 @@ static void *vobject_pull_open(const char *name, int oflag, mode_t mode,
                ret = -EBADR;
                goto fail;
        }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (strcmp(name, "/telecom/mch.vcf") == 0)
+                       pbap->params->required_missedcall_call_header = TRUE;
+
+       DBG("[%s] - required_missedcall_call_header [%d] ",
+                       name,pbap->params->required_missedcall_call_header);
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
 
        if (pbap->params->maxlistcount == 0)
                cb = phonebook_size_result;
@@ -746,6 +854,13 @@ static void *vobject_list_open(const char *name, int oflag, mode_t mode,
                ret = -EPERM;
                goto fail;
        }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (strcmp(name, "/telecom/mch") == 0)
+               pbap->params->required_missedcall_call_header = TRUE;
+
+       DBG("[%s] - required_missedcall_call_header [%d] ",
+                       name,pbap->params->required_missedcall_call_header);
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
 
        /* PullvCardListing always get the contacts from the cache */
 
@@ -757,6 +872,10 @@ static void *vobject_list_open(const char *name, int oflag, mode_t mode,
                                        cache_ready_notify, pbap, &ret);
                if (ret == 0)
                        obj = vobject_create(pbap, request);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               phonebook_set_cache_notification(pbap->backend_data,
+                                               cache_clear_notify, pbap);
+#endif
        }
        if (ret < 0)
                goto fail;
@@ -782,7 +901,11 @@ static void *vobject_vcard_open(const char *name, int oflag, mode_t mode,
        struct pbap_session *pbap = context;
        const char *id;
        uint32_t handle;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       int ret = 0;
+#else
        int ret;
+#endif
        void *request;
 
        DBG("name %s context %p valid %d", name, context, pbap->cache.valid);
@@ -799,8 +922,18 @@ static void *vobject_vcard_open(const char *name, int oflag, mode_t mode,
 
        if (pbap->cache.valid == FALSE) {
                pbap->find_handle = handle;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               request = phonebook_create_cache(pbap->folder,
+                       (phonebook_entry_cb)cache_entry_notify,
+                       (phonebook_cache_ready_cb)cache_entry_done, pbap, &ret);
+#else
                request = phonebook_create_cache(pbap->folder,
                        cache_entry_notify, cache_entry_done, pbap, &ret);
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               phonebook_set_cache_notification(pbap->backend_data,
+                                               cache_clear_notify, pbap);
+#endif
                goto done;
        }
 
@@ -841,8 +974,15 @@ static ssize_t vobject_pull_get_next_header(void *object, void *buf, size_t mtu,
 
        if (obj->firstpacket) {
                obj->firstpacket = FALSE;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               gsize count = 0;
+               count = g_obex_apparam_encode(obj->apparam, buf, mtu);
+               DBG("APPARAM Processed remove tags");
+               g_obex_apparam_remove_all(obj->apparam);
+               return count;
+#else
                return g_obex_apparam_encode(obj->apparam, buf, mtu);
+#endif
        }
 
        return 0;
@@ -891,11 +1031,22 @@ static ssize_t vobject_list_get_next_header(void *object, void *buf, size_t mtu,
                return -EAGAIN;
 
        *hi = G_OBEX_HDR_APPARAM;
-
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (pbap->params->maxlistcount == 0)
                return g_obex_apparam_encode(obj->apparam, buf, mtu);
 
        return 0;
+#else
+       if (obj->apparam != NULL) {
+               gsize count = 0;
+               count = g_obex_apparam_encode(obj->apparam, buf, mtu);
+               DBG("APPARAM Processed remove tags");
+               g_obex_apparam_remove_all(obj->apparam);
+               return count;
+       }
+       else
+               return 0;
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
 }
 
 static ssize_t vobject_list_read(void *object, void *buf, size_t count)
index 29ae889..322be4d 100755 (executable)
@@ -524,6 +524,11 @@ void *phonebook_get_entry(const char *folder, const char *id,
        filename = g_build_filename(root_folder, folder, id, NULL);
 
        fd = open(filename, O_RDONLY);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       g_free(filename);
+#endif
+
        if (fd < 0) {
                DBG("open(): %s(%d)", strerror(errno), errno);
                if (err)
diff --git a/obexd/plugins/phonebook-ebook.c b/obexd/plugins/phonebook-ebook.c
new file mode 100755 (executable)
index 0000000..c422585
--- /dev/null
@@ -0,0 +1,708 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2009-2010  Intel Corporation
+ *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <libebook/e-book.h>
+
+#include "lib/bluetooth.h"
+
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "phonebook.h"
+
+#define QUERY_FN "(contains \"family_name\" \"%s\")"
+#define QUERY_NAME "(contains \"given_name\" \"%s\")"
+#define QUERY_PHONE "(contains \"phone\" \"%s\")"
+
+struct query_context {
+       const struct apparam_field *params;
+       phonebook_cb contacts_cb;
+       phonebook_entry_cb entry_cb;
+       phonebook_cache_ready_cb ready_cb;
+       EBookQuery *query;
+       unsigned int count;
+       GString *buf;
+       char *id;
+       unsigned queued_calls;
+       void *user_data;
+       GSList *ebooks;
+       gboolean canceled;
+};
+
+static char *attribute_mask[] = {
+/* 0 */                "VERSION",
+               "FN",
+               "N",
+               "PHOTO",
+               "BDAY",
+               "ADR",
+               "LABEL",
+               "TEL",
+/* 8 */                "EMAIL",
+               "MAILER",
+               "TZ",
+               "GEO",
+               "TITLE",
+               "ROLE",
+               "LOGO",
+               "AGENT",
+/* 16 */       "ORG",
+               "NOTE",
+               "REV",
+               "SOUND",
+               "URL",
+               "UID",
+               "KEY",
+               "NICKNAME",
+/* 24 */       "CATEGORIES",
+               "PROID",
+               "CLASS",
+               "SORT-STRING",
+/* 28 */       "X-IRMC-CALL-DATETIME",
+               NULL
+
+};
+
+static void close_ebooks(GSList *ebooks)
+{
+       g_slist_free_full(ebooks, g_object_unref);
+}
+
+static void free_query_context(struct query_context *data)
+{
+       g_free(data->id);
+
+       if (data->buf != NULL)
+               g_string_free(data->buf, TRUE);
+
+       if (data->query != NULL)
+               e_book_query_unref(data->query);
+
+       close_ebooks(data->ebooks);
+
+       g_free(data);
+}
+
+static char *evcard_to_string(EVCard *evcard, unsigned int format,
+                                                       uint64_t filter)
+{
+       EVCard *evcard2;
+       GList *l;
+       char *vcard;
+
+       if (!filter)
+               return e_vcard_to_string(evcard, EVC_FORMAT_VCARD_30);
+               /* XXX There is no support for VCARD 2.1 at this time */
+
+       /*
+        * Mandatory attributes for vCard 2.1 are VERSION ,N and TEL.
+        * Mandatory attributes for vCard 3.0 are VERSION, N, FN and TEL
+        */
+       filter = format == EVC_FORMAT_VCARD_30 ? filter | 0x87: filter | 0x85;
+
+       l = e_vcard_get_attributes(evcard);
+       evcard2 = e_vcard_new();
+       for (; l; l = g_list_next(l)) {
+               EVCardAttribute *attrib = l->data;
+               const char *name;
+               int i;
+
+               if (!attrib)
+                       continue;
+
+               name = e_vcard_attribute_get_name(attrib);
+
+               for (i = 0; attribute_mask[i] != NULL; i++) {
+                       if (!(filter & (1 << i)))
+                               continue;
+                       if (g_strcmp0(name, attribute_mask[i]) != 0)
+                               continue;
+
+                       e_vcard_add_attribute(evcard2,
+                                       e_vcard_attribute_copy(attrib));
+               }
+       }
+
+       vcard = e_vcard_to_string(evcard2, format);
+       g_object_unref(evcard2);
+
+       return vcard;
+}
+
+static void ebookpull_cb(EBook *book, const GError *gerr, GList *contacts,
+                                                       void *user_data)
+{
+       struct query_context *data = user_data;
+       GList *l;
+       unsigned int count, maxcount;
+
+       data->queued_calls--;
+
+       if (data->canceled)
+               goto canceled;
+
+       if (gerr != NULL) {
+               error("E-Book query failed: %s", gerr->message);
+               goto done;
+       }
+
+       DBG("");
+
+       /*
+        * When MaxListCount is zero, PCE wants to know the number of used
+        * indexes in the phonebook of interest. All other parameters that
+        * may be present in the request shall be ignored.
+        */
+       maxcount = data->params->maxlistcount;
+       if (maxcount == 0) {
+               data->count += g_list_length(contacts);
+               goto done;
+       }
+
+       l = g_list_nth(contacts, data->params->liststartoffset);
+
+       for (count = 0; l && count + data->count < maxcount; l = g_list_next(l),
+                                                               count++) {
+               EContact *contact = E_CONTACT(l->data);
+               EVCard *evcard = E_VCARD(contact);
+               char *vcard;
+
+               vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_30,
+                                               data->params->filter);
+
+               data->buf = g_string_append(data->buf, vcard);
+               data->buf = g_string_append(data->buf, "\r\n");
+               g_free(vcard);
+       }
+
+       DBG("collected %d vcards", count);
+
+       data->count += count;
+
+       g_list_free_full(contacts, g_object_unref);
+
+done:
+       if (data->queued_calls == 0) {
+               GString *buf = data->buf;
+               data->buf = NULL;
+
+               data->contacts_cb(buf->str, buf->len, data->count,
+                                               0, TRUE, data->user_data);
+
+               g_string_free(buf, TRUE);
+
+       }
+
+       return;
+
+canceled:
+       if (data->queued_calls == 0)
+               free_query_context(data);
+}
+
+static void ebook_entry_cb(EBook *book, const GError *gerr,
+                               EContact *contact, void *user_data)
+{
+       struct query_context *data = user_data;
+       EVCard *evcard;
+       char *vcard;
+       size_t len;
+
+       data->queued_calls--;
+
+       if (data->canceled)
+               goto done;
+
+       if (gerr != NULL) {
+               error("E-Book query failed: %s", gerr->message);
+               goto done;
+       }
+
+       DBG("");
+
+       evcard = E_VCARD(contact);
+
+       vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_30,
+                                       data->params->filter);
+
+       len = vcard ? strlen(vcard) : 0;
+
+       data->count++;
+       data->contacts_cb(vcard, len, 1, 0, TRUE, data->user_data);
+
+       g_free(vcard);
+       g_object_unref(contact);
+
+       return;
+
+done:
+       if (data->queued_calls == 0) {
+               if (data->count == 0)
+                       data->contacts_cb(NULL, 0, 1, 0, TRUE,
+                                               data->user_data);
+               else if (data->canceled)
+                       free_query_context(data);
+       }
+}
+
+static char *evcard_name_attribute_to_string(EVCard *evcard)
+{
+       EVCardAttribute *attrib;
+       GList *l;
+       GString *name = NULL;
+
+       attrib = e_vcard_get_attribute(evcard, EVC_N);
+       if (!attrib)
+               return NULL;
+
+       for (l = e_vcard_attribute_get_values(attrib); l; l = l->next) {
+               const char *value = l->data;
+
+               if (!strlen(value))
+                       continue;
+
+               if (!name)
+                       name = g_string_new(value);
+               else {
+                       name = g_string_append(name, ";");
+                       name = g_string_append(name, l->data);
+               }
+       }
+
+       if (!name)
+               return NULL;
+
+       return g_string_free(name, FALSE);
+}
+
+static void cache_cb(EBook *book, const GError *gerr, GList *contacts,
+                                                       void *user_data)
+{
+       struct query_context *data = user_data;
+       GList *l;
+
+       data->queued_calls--;
+
+       if (data->canceled)
+               goto canceled;
+
+       if (gerr != NULL) {
+               error("E-Book operation failed: %s", gerr->message);
+               goto done;
+       }
+
+       DBG("");
+
+       for (l = contacts; l; l = g_list_next(l)) {
+               EContact *contact = E_CONTACT(l->data);
+               EVCard *evcard = E_VCARD(contact);
+               EVCardAttribute *attrib;
+               char *uid, *tel, *name;
+
+               name = evcard_name_attribute_to_string(evcard);
+               if (!name)
+                       continue;
+
+               attrib = e_vcard_get_attribute(evcard, EVC_UID);
+               if (!attrib)
+                       continue;
+
+               uid = e_vcard_attribute_get_value(attrib);
+               if (!uid)
+                       continue;
+
+               attrib = e_vcard_get_attribute(evcard, EVC_TEL);
+               if (attrib)
+                       tel = e_vcard_attribute_get_value(attrib);
+               else
+                       tel = g_strdup("");
+
+               data->entry_cb(uid, PHONEBOOK_INVALID_HANDLE, name, NULL,
+                                                       tel, data->user_data);
+
+               g_free(name);
+               g_free(uid);
+               g_free(tel);
+       }
+
+       g_list_free_full(contacts, g_object_unref);
+
+done:
+       if (data->queued_calls == 0)
+               data->ready_cb(data->user_data);
+
+       return;
+
+canceled:
+       if (data->queued_calls == 0)
+               free_query_context(data);
+}
+
+static GSList *traverse_sources(GSList *ebooks, GSList *sources,
+                                                       char **default_src) {
+       GError *gerr = NULL;
+
+       for (; sources != NULL; sources = g_slist_next(sources)) {
+               char *uri;
+               ESource *source = E_SOURCE(sources->data);
+               EBook *ebook = e_book_new(source, &gerr);
+
+               if (ebook == NULL) {
+                       error("Can't create user's address book: %s",
+                                                               gerr->message);
+                       g_clear_error(&gerr);
+                       continue;
+               }
+
+               uri = e_source_get_uri(source);
+               if (g_strcmp0(*default_src, uri) == 0) {
+                       g_free(uri);
+                       continue;
+               }
+               g_free(uri);
+
+               if (e_book_open(ebook, FALSE, &gerr) == FALSE) {
+                       error("Can't open e-book address book: %s",
+                                                       gerr->message);
+                       g_object_unref(ebook);
+                       g_clear_error(&gerr);
+                       continue;
+               }
+
+               if (*default_src == NULL)
+                       *default_src = e_source_get_uri(source);
+
+               DBG("%s address book opened", e_source_peek_name(source));
+
+               ebooks = g_slist_append(ebooks, ebook);
+       }
+
+       return ebooks;
+}
+
+int phonebook_init(void)
+{
+       g_type_init();
+
+       return 0;
+}
+
+static GSList *open_ebooks(void)
+{
+       GError *gerr = NULL;
+       ESourceList *src_list;
+       GSList *list;
+       char *default_src = NULL;
+       GSList *ebooks = NULL;
+
+       if (e_book_get_addressbooks(&src_list, &gerr) == FALSE) {
+               error("Can't list user's address books: %s", gerr->message);
+               g_error_free(gerr);
+               return NULL;
+       }
+
+       list = e_source_list_peek_groups(src_list);
+       while (list != NULL) {
+               ESourceGroup *group = E_SOURCE_GROUP(list->data);
+               GSList *sources = e_source_group_peek_sources(group);
+
+               ebooks = traverse_sources(ebooks, sources, &default_src);
+
+               list = list->next;
+       }
+
+       g_free(default_src);
+       g_object_unref(src_list);
+
+       return ebooks;
+}
+
+void phonebook_exit(void)
+{
+}
+
+char *phonebook_set_folder(const char *current_folder,
+               const char *new_folder, uint8_t flags, int *err)
+{
+       gboolean root, child;
+       char *fullname = NULL, *tmp1, *tmp2, *base;
+       int ret = 0, len;
+
+       root = (g_strcmp0("/", current_folder) == 0);
+       child = (new_folder && strlen(new_folder) != 0);
+
+       /* Evolution back-end will support /telecom/pb folder only */
+
+       switch (flags) {
+       case 0x02:
+               /* Go back to root */
+               if (!child) {
+                       fullname = g_strdup("/");
+                       goto done;
+               }
+
+               /* Go down 1 level */
+               fullname = g_build_filename(current_folder, new_folder, NULL);
+               if (strcmp(PB_TELECOM_FOLDER, fullname) != 0 &&
+                               strcmp(PB_CONTACTS_FOLDER, fullname) != 0) {
+                       g_free(fullname);
+                       fullname = NULL;
+                       ret = -ENOENT;
+               }
+
+               break;
+       case 0x03:
+               /* Go up 1 level */
+               if (root) {
+                       /* Already root */
+                       ret = -EBADR;
+                       goto done;
+               }
+
+               /*
+                * Removing one level of the current folder. Current folder
+                * contains AT LEAST one level since it is not at root folder.
+                * Use glib utility functions to handle invalid chars in the
+                * folder path properly.
+                */
+               tmp1 = g_path_get_basename(current_folder);
+               tmp2 = g_strrstr(current_folder, tmp1);
+               len = tmp2 - (current_folder + 1);
+
+               g_free(tmp1);
+
+               if (len == 0)
+                       base = g_strdup("/");
+               else
+                       base = g_strndup(current_folder, len);
+
+               /* Return one level only */
+               if (!child) {
+                       fullname = base;
+                       goto done;
+               }
+
+               fullname = g_build_filename(base, new_folder, NULL);
+               if (strcmp(fullname, PB_TELECOM_FOLDER) != 0 &&
+                               strcmp(fullname, PB_CONTACTS_FOLDER) != 0) {
+                       g_free(fullname);
+                       fullname = NULL;
+                       ret = -ENOENT;
+               }
+
+               g_free(base);
+
+               break;
+       default:
+               ret = -EBADR;
+               break;
+       }
+
+done:
+       if (err)
+               *err = ret;
+
+       return fullname;
+}
+
+void phonebook_req_finalize(void *request)
+{
+       struct query_context *data = request;
+
+       if (data->queued_calls == 0)
+               free_query_context(data);
+       else
+               data->canceled = TRUE;
+}
+
+void *phonebook_pull(const char *name, const struct apparam_field *params,
+                               phonebook_cb cb, void *user_data, int *err)
+{
+       struct query_context *data;
+
+       if (g_strcmp0(PB_CONTACTS, name) != 0) {
+               if (err)
+                       *err = -ENOENT;
+
+               return NULL;
+       }
+
+       data = g_new0(struct query_context, 1);
+       data->contacts_cb = cb;
+       data->params = params;
+       data->user_data = user_data;
+       data->buf = g_string_new("");
+       data->query = e_book_query_any_field_contains("");
+       data->ebooks = open_ebooks();
+
+       if (err)
+               *err = data->ebooks == NULL ? -EIO : 0;
+
+       return data;
+}
+
+int phonebook_pull_read(void *request)
+{
+       struct query_context *data = request;
+       GSList *l;
+
+       if (!data)
+               return -ENOENT;
+
+       for (l = data->ebooks; l != NULL; l = g_slist_next(l)) {
+               EBook *ebook = l->data;
+
+               if (e_book_is_opened(ebook) == FALSE)
+                       continue;
+
+               if (e_book_get_contacts_async(ebook, data->query,
+                                               ebookpull_cb, data) == TRUE)
+                       data->queued_calls++;
+       }
+
+       if (data->queued_calls == 0)
+               return -ENOENT;
+
+       return 0;
+}
+
+void *phonebook_get_entry(const char *folder, const char *id,
+                               const struct apparam_field *params,
+                               phonebook_cb cb, void *user_data, int *err)
+{
+       struct query_context *data;
+       GSList *l;
+
+       data = g_new0(struct query_context, 1);
+       data->contacts_cb = cb;
+       data->params = params;
+       data->user_data = user_data;
+       data->id = g_strdup(id);
+       data->ebooks = open_ebooks();
+
+       for (l = data->ebooks; l != NULL; l = g_slist_next(l)) {
+               EBook *ebook = l->data;
+
+               if (e_book_is_opened(ebook) == FALSE)
+                       continue;
+
+               if (e_book_get_contact_async(ebook, data->id,
+                                               ebook_entry_cb, data) == TRUE)
+                       data->queued_calls++;
+       }
+
+       if (err)
+               *err = (data->queued_calls == 0 ? -ENOENT : 0);
+
+       return data;
+}
+
+void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
+               phonebook_cache_ready_cb ready_cb, void *user_data, int *err)
+{
+       struct query_context *data;
+       EBookQuery *query;
+       GSList *l;
+       EContact *me;
+       EVCard *evcard;
+       GError *gerr = NULL;
+       EBook *eb;
+       EVCardAttribute *attrib;
+       char *uid, *tel, *cname;
+
+       if (g_strcmp0(PB_CONTACTS_FOLDER, name) != 0) {
+               if (err)
+                       *err = -ENOENT;
+
+               return NULL;
+       }
+
+       DBG("");
+
+       query = e_book_query_any_field_contains("");
+
+       data = g_new0(struct query_context, 1);
+       data->entry_cb = entry_cb;
+       data->ready_cb = ready_cb;
+       data->user_data = user_data;
+       data->query = query;
+       data->ebooks = open_ebooks();
+
+       /* Add 0.vcf */
+       if (e_book_get_self(&me, &eb, &gerr) == FALSE) {
+               g_error_free(gerr);
+               goto next;
+       }
+
+       evcard = E_VCARD(me);
+
+       cname = evcard_name_attribute_to_string(evcard);
+       if (!cname)
+               cname = g_strdup("");
+
+       attrib = e_vcard_get_attribute(evcard, EVC_UID);
+       uid = e_vcard_attribute_get_value(attrib);
+       if (!uid)
+               uid = g_strdup("");
+
+       attrib = e_vcard_get_attribute(evcard, EVC_TEL);
+       if (attrib)
+               tel =  e_vcard_attribute_get_value(attrib);
+       else
+               tel = g_strdup("");
+
+       data->entry_cb(uid, 0, cname, NULL, tel, data->user_data);
+
+       data->count++;
+
+       g_free(cname);
+       g_free(uid);
+       g_free(tel);
+       g_object_unref(eb);
+
+next:
+       for (l = data->ebooks; l != NULL; l = g_slist_next(l)) {
+               EBook *ebook = l->data;
+
+               if (e_book_is_opened(ebook) == FALSE)
+                       continue;
+
+               if (e_book_get_contacts_async(ebook, query,
+                                               cache_cb, data) == TRUE)
+                       data->queued_calls++;
+       }
+
+       if (err)
+               *err = (data->queued_calls == 0 ? -ENOENT : 0);
+
+       return data;
+}
diff --git a/obexd/plugins/phonebook-tizen.c b/obexd/plugins/phonebook-tizen.c
new file mode 100755 (executable)
index 0000000..dcb6179
--- /dev/null
@@ -0,0 +1,813 @@
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (c) 2000-2016 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ *
+ *  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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "phonebook.h"
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#define PHONEBOOK_DESTINATION          "org.bluez.pb_agent"
+#define PHONEBOOK_PATH                 "/org/bluez/pb_agent"
+#define PHONEBOOK_INTERFACE            "org.bluez.PbAgent"
+
+#define QUERY_GET_PHONEBOOK_FOLDER_LIST "GetPhonebookFolderList"
+#define QUERY_GET_PHONEBOOK_SIZE       "GetPhonebookSize"
+#define QUERY_GET_PHONEBOOK            "GetPhonebook"
+#define QUERY_GET_PHONEBOOK_LIST       "GetPhonebookList"
+#define QUERY_GET_PHONEBOOK_ENTRY      "GetPhonebookEntry"
+#define QUERY_DESTROY_AGENT "DestroyAgent"
+
+static GPtrArray *folder_list = NULL;
+
+struct phonebook_data {
+       phonebook_cb cb;
+       DBusPendingCallNotifyFunction reply_cb;
+       DBusPendingCall *call;
+       void *user_data;
+       const struct apparam_field *params;
+
+       phonebook_entry_cb entry_cb;
+       phonebook_cache_ready_cb ready_cb;
+
+       char *req_name;
+};
+
+struct phonebook_session {
+       DBusConnection *connection;
+       phonebook_cache_clear_cb clear_cb;
+       unsigned int clear_id;
+
+       void *user_data;
+};
+
+static gboolean folder_is_valid(const gchar *folder,
+                               gboolean leaf_only)
+{
+       int i;
+       int folder_len;
+
+       if (folder_list == NULL || folder == NULL)
+               return FALSE;
+
+       folder_len = strlen(folder);
+
+       for (i = 0 ; i < folder_list->len; i++) {
+               char *str = (char *)g_ptr_array_index(folder_list, i);
+
+               if (folder_len > strlen(str))
+                       continue;
+
+               if (g_strcmp0(str, folder) == 0)
+                       return TRUE;
+
+               if (leaf_only == TRUE)
+                       continue;
+
+               if (strncmp(str, folder,  folder_len) == 0) {
+
+                       if (*(folder + folder_len - 1) == '/')
+                               return TRUE;    /* folder is ended with '/' */
+
+                       if (*(str + folder_len) == '/')
+                               return TRUE;    /* folder is matched */
+               }
+       }
+
+       return FALSE;
+}
+
+static DBusPendingCall* phonebook_request(struct phonebook_data *data,
+                               const gchar *method,
+                               DBusPendingCallNotifyFunction function,
+                               int first_arg_type,
+                               ...)
+{
+       DBusConnection *connection;
+       DBusPendingCall *call;
+       DBusMessage *message;
+
+       va_list args;
+
+       DBG("%s\n", method);
+
+       if (!method) {
+               error("Can't get method name");
+               return NULL;
+       }
+
+       connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+       if (!connection) {
+               error("Can't get on session bus");
+               return NULL;
+       }
+
+       message = dbus_message_new_method_call(PHONEBOOK_DESTINATION,
+                       PHONEBOOK_PATH,
+                       PHONEBOOK_INTERFACE,
+                       method);
+       if (!message) {
+               dbus_connection_unref(connection);
+               error("Can't Allocate new message");
+               return NULL;
+       }
+
+       va_start(args, first_arg_type);
+       dbus_message_append_args_valist(message, first_arg_type, args);
+       va_end(args);
+
+       if (dbus_connection_send_with_reply(connection, message, &call, -1) == FALSE) {
+               dbus_message_unref(message);
+               dbus_connection_unref(connection);
+               DBG("-");
+               return NULL;
+       }
+       dbus_pending_call_set_notify(call, function, data, NULL);
+
+       dbus_message_unref(message);
+       dbus_connection_unref(connection);
+
+       DBG("-");
+       return call;
+}
+
+static void get_phonebook_size_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       struct phonebook_data *s_data = user_data;
+       DBusError derr;
+       uint32_t phonebook_size;
+
+       unsigned int new_missed_call = 0;
+
+       DBG("");
+       dbus_pending_call_unref(s_data->call);
+       s_data->call = NULL;
+
+       if (!reply) {
+               DBG("Reply Error\n");
+               return;
+       }
+
+       dbus_error_init(&derr);
+       if (dbus_set_error_from_message(&derr, reply)) {
+               error("Replied with an error: %s, %s", derr.name, derr.message);
+               dbus_error_free(&derr);
+               phonebook_size = 0;
+       } else {
+               dbus_message_get_args(reply, NULL,
+                               DBUS_TYPE_UINT32, &phonebook_size,
+                               DBUS_TYPE_UINT32, &new_missed_call,
+                               DBUS_TYPE_INVALID);
+               DBG("phonebooksize:%d\n", phonebook_size);
+       }
+
+       s_data->cb(NULL, 0, phonebook_size, new_missed_call, TRUE, s_data->user_data);
+
+       dbus_message_unref(reply);
+}
+
+static void get_phonebook_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       struct phonebook_data *s_data = user_data;
+       DBusError derr;
+       GString *buffer;
+
+       unsigned int new_missed_call = 0;
+
+       uint32_t count = 0;
+
+       DBG("");
+       dbus_pending_call_unref(s_data->call);
+       s_data->call = NULL;
+
+       if (!reply) {
+               DBG("Reply Error\n");
+               return;
+       }
+
+       buffer = g_string_new("");
+
+       dbus_error_init(&derr);
+       if (dbus_set_error_from_message(&derr, reply)) {
+               error("Replied with an error: %s, %s", derr.name, derr.message);
+               dbus_error_free(&derr);
+       } else {
+               DBusMessageIter iter;
+
+               dbus_message_iter_init(reply, &iter);
+
+               if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
+                       DBusMessageIter recurse_iter;
+
+                       dbus_message_iter_recurse(&iter, &recurse_iter);
+                       while (dbus_message_iter_get_arg_type(&recurse_iter) == DBUS_TYPE_STRING) {
+                               gchar *str;
+
+                               dbus_message_iter_get_basic(&recurse_iter, &str);
+                               dbus_message_iter_next(&recurse_iter);
+
+                               g_string_append(buffer, str);
+
+                               DBG("str:\n%s\n", str);
+
+                               count++;
+                       }
+                       dbus_message_iter_next(&iter);
+               }
+
+               if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32)
+                       dbus_message_iter_get_basic(&iter, &new_missed_call);
+
+               DBG("new_missed_call %d\n", new_missed_call);
+       }
+
+       s_data->cb(buffer->str, buffer->len, count, new_missed_call, 1, s_data->user_data);
+
+       g_string_free(buffer, TRUE);
+       dbus_message_unref(reply);
+}
+
+
+static void get_phonebook_list_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusMessageIter iter, iter_struct, entry;
+       struct phonebook_data *data = user_data;
+       DBusError derr;
+       uint32_t handle = 0;
+       const char *name = NULL;
+       const char *tel = NULL;
+       char id[10];
+       unsigned int new_missed_call = 0;
+
+       dbus_pending_call_unref(data->call);
+       data->call = NULL;
+       if (!reply) {
+               DBG("Reply Error\n");
+               return;
+       }
+
+       dbus_error_init(&derr);
+       if (dbus_set_error_from_message(&derr, reply)) {
+               error("Replied with an error: %s, %s", derr.name, derr.message);
+               dbus_error_free(&derr);
+       } else {
+
+               dbus_message_iter_init(reply, &iter);
+
+               dbus_message_iter_recurse(&iter, &iter_struct);
+
+               while (dbus_message_iter_get_arg_type(&iter_struct) ==
+                                                       DBUS_TYPE_STRUCT) {
+                       dbus_message_iter_recurse(&iter_struct, &entry);
+
+                       dbus_message_iter_get_basic(&entry, &name);
+                       dbus_message_iter_next(&entry);
+                       dbus_message_iter_get_basic(&entry, &tel);
+                       dbus_message_iter_next(&entry);
+                       dbus_message_iter_get_basic(&entry, &handle);
+
+                       /*
+                       DBG("[handle:%d name:%s tel:%s]\n", handle, name, tel);
+                       */
+
+                       snprintf(id, sizeof(id), "%d.vcf", handle);
+
+                       data->entry_cb(id, handle, name, NULL, tel,
+                                       data->user_data);
+
+                       dbus_message_iter_next(&iter_struct);
+               }
+
+               dbus_message_iter_next(&iter);
+               if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32) {
+                       dbus_message_iter_get_basic(&iter, &new_missed_call);
+               }
+
+               DBG("new_missed_call %d\n", new_missed_call);
+       }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       data->ready_cb(data->user_data, new_missed_call);
+#else
+       data->ready_cb(data->user_data);
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
+       dbus_message_unref(reply);
+}
+
+static void get_phonebook_entry_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       struct phonebook_data *s_data = user_data;
+       DBusError derr;
+       const char *phonebook_entry;
+
+       DBG("");
+       dbus_pending_call_unref(s_data->call);
+       s_data->call = NULL;
+
+       if (!reply) {
+               DBG("Reply Error\n");
+               return;
+       }
+
+       dbus_error_init(&derr);
+       if (dbus_set_error_from_message(&derr, reply)) {
+               error("Replied with an error: %s, %s", derr.name, derr.message);
+               dbus_error_free(&derr);
+       } else {
+               dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING,
+                                       &phonebook_entry, DBUS_TYPE_INVALID);
+               DBG("phonebook_entry:[%s]\n", phonebook_entry);
+       }
+
+       s_data->cb(phonebook_entry, strlen(phonebook_entry), 1, 0, TRUE,
+                                                       s_data->user_data);
+
+       dbus_message_unref(reply);
+}
+
+static gboolean clear_signal(DBusConnection *conn, DBusMessage *msg,
+                       void *user_data)
+{
+       struct phonebook_session *session;
+
+       if (user_data == NULL)
+               return FALSE;
+
+       DBG("");
+       session = user_data;
+
+       session->clear_cb(session->user_data);
+
+       g_dbus_remove_watch(session->connection, session->clear_id);
+       session->clear_id = 0;
+
+       return TRUE;
+}
+
+static gboolean phonebook_folder_list_init(struct phonebook_session *session)
+{
+       DBusMessage *message;
+       DBusMessage *reply;
+
+       DBusMessageIter iter;
+       DBusMessageIter recurse_iter;
+
+       DBusError error;
+
+       if (session->connection == NULL) {
+               session->connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM,
+                                                       NULL, NULL);
+       }
+
+       if (session->connection == NULL) {
+               DBG("Can't get on s bus");
+               return FALSE;
+       }
+
+       message = dbus_message_new_method_call(PHONEBOOK_DESTINATION,
+                                       PHONEBOOK_PATH,
+                                       PHONEBOOK_INTERFACE,
+                                       QUERY_GET_PHONEBOOK_FOLDER_LIST);
+
+       if (message == NULL) {
+               DBG("Can't allocate dbus message");
+               return FALSE;
+       }
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(session->connection,
+                                                       message, -1, &error);
+
+       if (!reply) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       DBG("%s", error.message);
+                       dbus_error_free(&error);
+               } else {
+                       DBG("Failed to get contacts");
+               }
+               dbus_message_unref(message);
+               return FALSE;
+       }
+
+       dbus_message_iter_init(reply, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+               dbus_message_unref(message);
+               dbus_message_unref(reply);
+               return FALSE;
+       }
+
+       if (folder_list) {
+               g_ptr_array_free(folder_list, TRUE);
+               folder_list = NULL;
+       }
+
+       folder_list = g_ptr_array_new();
+
+       dbus_message_iter_recurse(&iter, &recurse_iter);
+
+       while (dbus_message_iter_get_arg_type(&recurse_iter)
+                       == DBUS_TYPE_STRING) {
+               gchar *str;
+
+               dbus_message_iter_get_basic(&recurse_iter, &str);
+               DBG("folder list %s\n", str);
+               g_ptr_array_add(folder_list, g_strdup(str));
+
+               dbus_message_iter_next(&recurse_iter);
+       }
+
+       dbus_message_unref(message);
+       dbus_message_unref(reply);
+       return TRUE;
+}
+
+int phonebook_init(void)
+{
+       return 0;
+}
+
+void phonebook_exit(void)
+{
+}
+
+int phonebook_connect(void **user_data)
+{
+       struct phonebook_session *session;
+       gboolean ret;
+
+       DBG("");
+
+       session = g_new0(struct phonebook_session, 1);
+
+       *user_data = session;
+
+       ret = phonebook_folder_list_init(session);
+
+       if (ret)
+               return 0;
+
+       return -1;
+}
+
+void phonebook_disconnect(void *user_data)
+{
+       struct phonebook_session *session;
+       DBusMessage *message;
+       DBG("");
+       session = user_data;
+
+       if (folder_list) {
+               g_ptr_array_free(folder_list, TRUE);
+               folder_list = NULL;
+       }
+
+       if (!session->connection) {
+               g_free(session);
+               return;
+       }
+
+
+       if (session->clear_id)
+               g_dbus_remove_watch(session->connection, session->clear_id);
+
+       message = dbus_message_new_method_call(PHONEBOOK_DESTINATION,
+                                               PHONEBOOK_PATH,
+                                               PHONEBOOK_INTERFACE,
+                                               QUERY_DESTROY_AGENT);
+
+       if (message) {
+               if (dbus_connection_send(session->connection, message,
+                                                               NULL) == FALSE)
+                       error("Could not send dbus message");
+
+               dbus_message_unref(message);
+       }
+
+       dbus_connection_unref(session->connection);
+       g_free(session);
+}
+
+char *phonebook_set_folder(const char *current_folder,
+               const char *new_folder, uint8_t flags, int *err)
+{
+       char *tmp1, *tmp2, *base, *path = NULL;
+       gboolean root, child;
+       int ret = 0;
+       int len;
+
+       root = (g_strcmp0("/", current_folder) == 0);
+       child = (new_folder && strlen(new_folder) != 0);
+
+       switch (flags) {
+               case 0x02:
+                       /* Go back to root */
+                       if (!child) {
+                               path = g_strdup("/");
+                               goto done;
+                       }
+
+                       path = g_build_filename(current_folder, new_folder, NULL);
+                       break;
+               case 0x03:
+                       /* Go up 1 level */
+                       if (root) {
+                               /* Already root */
+                               path = g_strdup("/");
+                               goto done;
+                       }
+
+                       /*
+                        * Removing one level of the current folder. Current folder
+                        * contains AT LEAST one level since it is not at root folder.
+                        * Use glib utility functions to handle invalid chars in the
+                        * folder path properly.
+                        */
+                       tmp1 = g_path_get_basename(current_folder);
+                       tmp2 = g_strrstr(current_folder, tmp1);
+                       len = tmp2 - (current_folder + 1);
+
+                       g_free(tmp1);
+
+                       if (len == 0)
+                               base = g_strdup("/");
+                       else
+                               base = g_strndup(current_folder, len);
+
+                       /* Return: one level only */
+                       if (!child) {
+                               path = base;
+                               goto done;
+                       }
+
+                       path = g_build_filename(base, new_folder, NULL);
+                       g_free(base);
+
+                       break;
+               default:
+                       ret = -EBADR;
+                       break;
+       }
+
+done:
+       if (path && !folder_is_valid(path, FALSE))
+               ret = -ENOENT;
+
+       if (ret < 0) {
+               g_free(path);
+               path = NULL;
+       }
+
+       if (err)
+               *err = ret;
+
+       DBG("path : %s\n", path);
+
+       return path;
+}
+
+void phonebook_req_finalize(void *request)
+{
+       struct phonebook_data *data = request;
+
+       DBG("");
+
+       if (!data)
+               return;
+
+       if (data->call) {
+               dbus_pending_call_cancel(data->call);
+               dbus_pending_call_unref(data->call);
+       }
+       g_free(data->req_name);
+       g_free(data);
+}
+
+void *phonebook_pull(const char *name, const struct apparam_field *params,
+                               phonebook_cb cb, void *user_data, int *err)
+{
+       struct phonebook_data *data;
+       char *folder;
+
+       DBG("name %s", name);
+
+       if (!g_str_has_suffix(name, ".vcf")) {
+               if (err)
+                       *err = -ENOENT;
+
+               return NULL;
+       }
+
+       folder = g_strndup(name, strlen(name) - 4);
+
+       if (!folder_is_valid(folder, TRUE)) {
+               if (err)
+                       *err = -ENOENT;
+
+               g_free(folder);
+               return NULL;
+       }
+
+       g_free(folder);
+
+       data = g_new0(struct phonebook_data, 1);
+       data->params = params;
+       data->user_data = user_data;
+       data->cb = cb;
+       data->req_name = g_strdup(name);
+
+       if (err)
+               *err = 0;
+
+       return data;
+}
+
+int phonebook_pull_read(void *request)
+{
+       struct phonebook_data *data = request;
+
+       DBG("name %s", data->req_name);
+
+       if (data->params->maxlistcount == 0) {
+               data->call = phonebook_request(data,
+                               QUERY_GET_PHONEBOOK_SIZE,
+                               get_phonebook_size_reply,
+                               DBUS_TYPE_STRING, &data->req_name,
+                               DBUS_TYPE_INVALID);
+               return 0;
+       }
+
+       data->call = phonebook_request(data,
+                       QUERY_GET_PHONEBOOK,
+                       get_phonebook_reply,
+                       DBUS_TYPE_STRING, &data->req_name,
+                       DBUS_TYPE_UINT64, &data->params->filter,
+                       DBUS_TYPE_BYTE, &data->params->format,
+                       DBUS_TYPE_UINT16, &data->params->maxlistcount,
+                       DBUS_TYPE_UINT16, &data->params->liststartoffset,
+                       DBUS_TYPE_INVALID);
+
+       return 0;
+}
+
+void *phonebook_get_entry(const char *folder, const char *id,
+                       const struct apparam_field *params, phonebook_cb cb,
+                       void *user_data, int *err)
+{
+       struct phonebook_data *data;
+
+       DBG("");
+       if (!g_str_has_suffix(id, ".vcf")) {
+               if (err)
+                       *err = -ENOENT;
+
+               return NULL;
+       }
+
+       if (!folder_is_valid(folder, TRUE)) {
+               if (err)
+                       *err = - ENOENT;
+
+               return NULL;
+       }
+
+       DBG("folder %s id %s", folder, id);
+
+       data = g_new0(struct phonebook_data, 1);
+       data->params = params;
+       data->user_data = user_data;
+       data->cb = cb;
+
+       data->call = phonebook_request(data,
+                       QUERY_GET_PHONEBOOK_ENTRY,
+                       get_phonebook_entry_reply,
+                       DBUS_TYPE_STRING, &folder,
+                       DBUS_TYPE_STRING, &id,
+                       DBUS_TYPE_UINT64, &data->params->filter,
+                       DBUS_TYPE_BYTE, &data->params->format,
+                       DBUS_TYPE_INVALID);
+
+       if (*err) {
+               if (data->call)
+                       *err = 0;
+               else {
+                       *err = -ENOENT;
+                       g_free(data);
+                       data = NULL;
+               }
+       }
+
+       return data;
+}
+
+void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
+               phonebook_cache_ready_cb ready_cb, void *user_data, int *err)
+{
+       struct phonebook_data *data;
+
+       if (!folder_is_valid(name, TRUE)) {
+               if (err)
+                       *err = - ENOENT;
+
+               return NULL;
+       }
+
+       DBG("name %s", name);
+
+       data = g_new0(struct phonebook_data, 1);
+       data->user_data = user_data;
+       data->entry_cb = entry_cb;
+       data->ready_cb = ready_cb;
+
+       data->call = phonebook_request(data,
+                               QUERY_GET_PHONEBOOK_LIST,
+                               get_phonebook_list_reply,
+                               DBUS_TYPE_STRING, &name,
+                               DBUS_TYPE_INVALID);
+
+       if (*err) {
+               if (data->call)
+                       *err = 0;
+               else {
+                       *err = -ENOENT;
+                       g_free(data);
+                       data = NULL;
+               }
+       }
+
+       return data;
+}
+
+void phonebook_set_cache_notification(void *session,
+                               phonebook_cache_clear_cb clear_cb,
+                               void *user_data)
+{
+       struct phonebook_session *s = session;
+
+       DBG("");
+       s->clear_cb = clear_cb;
+
+       if (s->connection == NULL) {
+               s->connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM,
+                               NULL, NULL);
+
+               if (s->connection == NULL) {
+                       error("Can't get on s bus");
+                       return;
+               }
+       }
+
+       s->user_data = user_data;
+
+       if (s->clear_id) {
+               g_dbus_remove_watch(s->connection, s->clear_id);
+               s->clear_id = 0;
+       }
+
+       s->clear_id = g_dbus_add_signal_watch(s->connection,
+                       NULL, PHONEBOOK_PATH, PHONEBOOK_INTERFACE,
+                       "clear", clear_signal,
+                       s, NULL);
+}
diff --git a/obexd/plugins/phonebook-tracker.c b/obexd/plugins/phonebook-tracker.c
new file mode 100755 (executable)
index 0000000..0743629
--- /dev/null
@@ -0,0 +1,1718 @@
+/*
+ *  Phonebook access through D-Bus vCard and call history service
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *
+ *
+ *  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 <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <libtracker-sparql/tracker-sparql.h>
+
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/mimetype.h"
+#include "phonebook.h"
+#include "vcard.h"
+
+#define TRACKER_SERVICE "org.freedesktop.Tracker1"
+#define TRACKER_RESOURCES_PATH "/org/freedesktop/Tracker1/Resources"
+#define TRACKER_RESOURCES_INTERFACE "org.freedesktop.Tracker1.Resources"
+
+#define TRACKER_DEFAULT_CONTACT_ME "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#default-contact-me"
+#define AFFILATION_HOME "Home"
+#define AFFILATION_WORK "Work"
+#define ADDR_FIELD_AMOUNT 7
+#define PULL_QUERY_COL_AMOUNT 23
+#define COUNT_QUERY_COL_AMOUNT 1
+
+#define COL_PHONE_AFF 0 /* work/home phone numbers */
+#define COL_FULL_NAME 1
+#define COL_FAMILY_NAME 2
+#define COL_GIVEN_NAME 3
+#define COL_ADDITIONAL_NAME 4
+#define COL_NAME_PREFIX 5
+#define COL_NAME_SUFFIX 6
+#define COL_ADDR_AFF 7 /* addresses from affilation */
+#define COL_BIRTH_DATE 8
+#define COL_NICKNAME 9
+#define COL_URL 10
+#define COL_PHOTO 11
+#define COL_ORG_ROLE 12
+#define COL_UID 13
+#define COL_TITLE 14
+#define COL_AFF_TYPE 15
+#define COL_ORG_NAME 16
+#define COL_ORG_DEPARTMENT 17
+#define COL_EMAIL_AFF 18 /* email's from affilation (work/home) */
+#define COL_DATE 19
+#define COL_SENT 20
+#define COL_ANSWERED 21
+#define CONTACTS_ID_COL 22
+#define CONTACT_ID_PREFIX "urn:uuid:"
+#define CALL_ID_PREFIX "message:"
+
+#define FAX_NUM_TYPE "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#FaxNumber"
+#define MOBILE_NUM_TYPE "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#CellPhoneNumber"
+
+#define MAIN_DELIM "\30" /* Main delimiter between phones, addresses, emails*/
+#define SUB_DELIM "\31" /* Delimiter used in telephone number strings*/
+#define ADDR_DELIM "\37" /* Delimiter used for address data fields */
+#define MAX_FIELDS 100 /* Max amount of fields to be concatenated at once*/
+#define VCARDS_PART_COUNT 50 /* amount of vcards sent at once to PBAP core */
+#define QUERY_OFFSET_FORMAT "%s OFFSET %d"
+
+#define CONTACTS_QUERY_ALL                                             \
+"SELECT "                                                              \
+"(SELECT GROUP_CONCAT(fn:concat(rdf:type(?aff_number),"                        \
+"\"\31\", nco:phoneNumber(?aff_number)), \"\30\")"                     \
+"WHERE {"                                                              \
+"      ?_role nco:hasPhoneNumber ?aff_number"                          \
+"}) "                                                                  \
+"nco:fullname(?_contact) "                                             \
+"nco:nameFamily(?_contact) "                                           \
+"nco:nameGiven(?_contact) "                                            \
+"nco:nameAdditional(?_contact) "                                       \
+"nco:nameHonorificPrefix(?_contact) "                                  \
+"nco:nameHonorificSuffix(?_contact) "                                  \
+"(SELECT GROUP_CONCAT(fn:concat("                                      \
+"tracker:coalesce(nco:pobox(?aff_addr), \"\"), \"\37\","               \
+"tracker:coalesce(nco:extendedAddress(?aff_addr), \"\"), \"\37\","     \
+"tracker:coalesce(nco:streetAddress(?aff_addr), \"\"), \"\37\","       \
+"tracker:coalesce(nco:locality(?aff_addr), \"\"), \"\37\","            \
+"tracker:coalesce(nco:region(?aff_addr), \"\"), \"\37\","              \
+"tracker:coalesce(nco:postalcode(?aff_addr), \"\"), \"\37\","          \
+"tracker:coalesce(nco:country(?aff_addr), \"\"), "                     \
+"\"\31\", rdfs:label(?_role) ), "                                      \
+"\"\30\") "                                                            \
+"WHERE {"                                                              \
+"?_role nco:hasPostalAddress ?aff_addr"                                        \
+"}) "                                                                  \
+"nco:birthDate(?_contact) "                                            \
+"(SELECT "                                                             \
+"      ?nick "                                                         \
+"      WHERE { "                                                       \
+"              { "                                                     \
+"                      ?_contact nco:nickname ?nick "                  \
+"              } UNION { "                                             \
+"                      ?_contact nco:hasAffiliation ?role . "          \
+"                      ?role nco:hasIMAddress ?im . "                  \
+"                      ?im nco:imNickname ?nick "                      \
+"              } "                                                     \
+"      } "                                                             \
+") "                                                                   \
+"(SELECT GROUP_CONCAT(fn:concat( "                                     \
+       "?url_val, \"\31\", tracker:coalesce(rdfs:label(?_role), \"\") "\
+       "), \"\30\") "                                                  \
+       "WHERE {"                                                       \
+               "?_role nco:url ?url_val . "                            \
+"})"                                                                   \
+"nie:url(nco:photo(?_contact)) "                                       \
+"nco:role(?_role) "                                                    \
+"nco:contactUID(?_contact) "                                           \
+"nco:title(?_role) "                                                   \
+"rdfs:label(?_role) "                                                  \
+"nco:fullname(nco:org(?_role))"                                                \
+"nco:department(?_role) "                                              \
+"(SELECT GROUP_CONCAT(fn:concat(?emailaddress,\"\31\","                        \
+       "tracker:coalesce(rdfs:label(?_role), \"\")),"                  \
+       "\"\30\") "                                                     \
+       "WHERE { "                                                      \
+       "?_role nco:hasEmailAddress "                                   \
+       "               [ nco:emailAddress ?emailaddress ] "            \
+       "}) "                                                           \
+"\"NOTACALL\" \"false\" \"false\" "                                    \
+"?_contact "                                                           \
+"WHERE {"                                                              \
+"      ?_contact a nco:PersonContact ."                                \
+"      OPTIONAL {?_contact nco:hasAffiliation ?_role .}"               \
+"}"                                                                    \
+"ORDER BY tracker:id(?_contact)"
+
+#define CONTACTS_QUERY_ALL_LIST                                                \
+       "SELECT ?c nco:nameFamily(?c) "                                 \
+       "nco:nameGiven(?c) nco:nameAdditional(?c) "                     \
+       "nco:nameHonorificPrefix(?c) nco:nameHonorificSuffix(?c) "      \
+       "(SELECT "                                                      \
+               "?nick "                                                \
+               "WHERE { "                                              \
+                       "{ "                                            \
+                               "?c nco:nickname ?nick "                \
+                       "} UNION { "                                    \
+                               "?c nco:hasAffiliation ?role . "        \
+                               "?role nco:hasIMAddress ?im . "         \
+                               "?im nco:imNickname ?nick "             \
+                       "} "                                            \
+               "} "                                                    \
+       ") "                                                            \
+       "nco:phoneNumber(?h) "                                          \
+       "WHERE { "                                                      \
+               "?c a nco:PersonContact . "                             \
+       "OPTIONAL { ?c nco:hasPhoneNumber ?h . } "                      \
+       "OPTIONAL { "                                                   \
+               "?c nco:hasAffiliation ?a . "                           \
+               "?a nco:hasPhoneNumber ?h . "                           \
+       "} "                                                            \
+       "} GROUP BY ?c"
+
+#define CALLS_CONSTRAINTS(CONSTRAINT)                                  \
+" WHERE { "                                                            \
+       "?_call a nmo:Call . "                                          \
+       "?_unb_contact a nco:Contact . "                                \
+       "?_unb_contact nco:hasPhoneNumber ?_cpn . "                     \
+CONSTRAINT                                                             \
+       "OPTIONAL { "                                                   \
+               "{ SELECT ?_contact ?_no ?_role ?_number "              \
+                       "count(?_contact) as ?cnt "                     \
+               "WHERE { "                                              \
+                       "?_contact a nco:PersonContact . "              \
+                       "{ "                                            \
+                               "?_contact nco:hasAffiliation ?_role . "\
+                               "?_role nco:hasPhoneNumber ?_number . " \
+                       "} UNION { "                                    \
+                               "?_contact nco:hasPhoneNumber ?_number" \
+                       "} "                                            \
+                       "?_number maemo:localPhoneNumber ?_no . "       \
+               "} GROUP BY ?_no } "                                    \
+               "FILTER(?cnt = 1) "                                     \
+               "?_cpn maemo:localPhoneNumber ?_no . "                  \
+       "} "                                                            \
+"} "
+
+#define CALLS_LIST(CONSTRAINT)                                         \
+"SELECT ?_call nco:nameFamily(?_contact) "                             \
+       "nco:nameGiven(?_contact) nco:nameAdditional(?_contact) "       \
+       "nco:nameHonorificPrefix(?_contact) "                           \
+       "nco:nameHonorificSuffix(?_contact) "                           \
+       "(SELECT "                                                      \
+               "?nick "                                                \
+               "WHERE { "                                              \
+                       "{ "                                            \
+                               "?_contact nco:nickname ?nick "         \
+                       "} UNION { "                                    \
+                               "?_contact nco:hasAffiliation ?role . " \
+                               "?role nco:hasIMAddress ?im . "         \
+                               "?im nco:imNickname ?nick "             \
+                       "} "                                            \
+               "} "                                                    \
+       ") "                                                            \
+       "nco:phoneNumber(?_cpn) "                                       \
+CALLS_CONSTRAINTS(CONSTRAINT)                                          \
+"ORDER BY DESC(nmo:sentDate(?_call)) "
+
+#define CALLS_QUERY(CONSTRAINT)                                                \
+"SELECT "                                                              \
+"(SELECT fn:concat(rdf:type(?role_number),"                            \
+       "\"\31\", nco:phoneNumber(?role_number))"                       \
+       "WHERE {"                                                       \
+       "{"                                                             \
+       "       ?_role nco:hasPhoneNumber ?role_number "                \
+       "       FILTER (?role_number = ?_number)"                       \
+       "} UNION { "                                                    \
+               "?_unb_contact nco:hasPhoneNumber ?role_number . "      \
+       "       FILTER (!bound(?_role)) "                               \
+       "}"                                                             \
+"} GROUP BY nco:phoneNumber(?role_number) ) "                          \
+       "nco:fullname(?_contact) "                                      \
+       "nco:nameFamily(?_contact) "                                    \
+       "nco:nameGiven(?_contact) "                                     \
+       "nco:nameAdditional(?_contact) "                                \
+       "nco:nameHonorificPrefix(?_contact) "                           \
+       "nco:nameHonorificSuffix(?_contact) "                           \
+"(SELECT GROUP_CONCAT(fn:concat("                                      \
+       "tracker:coalesce(nco:pobox(?aff_addr), \"\"), \"\37\","        \
+       "tracker:coalesce(nco:extendedAddress(?aff_addr), \"\"), \"\37\","\
+       "tracker:coalesce(nco:streetAddress(?aff_addr), \"\"), \"\37\","\
+       "tracker:coalesce(nco:locality(?aff_addr), \"\"), \"\37\","     \
+       "tracker:coalesce(nco:region(?aff_addr), \"\"), \"\37\","       \
+       "tracker:coalesce(nco:postalcode(?aff_addr), \"\"), \"\37\","   \
+       "tracker:coalesce(nco:country(?aff_addr), \"\"), "              \
+       "\"\31\", rdfs:label(?c_role) ), "                              \
+       "\"\30\") "                                                     \
+       "WHERE {"                                                       \
+       "?_contact nco:hasAffiliation ?c_role . "                       \
+       "?c_role nco:hasPostalAddress ?aff_addr"                        \
+       "}) "                                                           \
+       "nco:birthDate(?_contact) "                                     \
+"(SELECT "                                                             \
+       "?nick "                                                        \
+       "WHERE { "                                                      \
+       "       { "                                                     \
+       "       ?_contact nco:nickname ?nick "                          \
+       "               } UNION { "                                     \
+       "                       ?_contact nco:hasAffiliation ?role . "  \
+       "                       ?role nco:hasIMAddress ?im . "          \
+       "                       ?im nco:imNickname ?nick "              \
+       "               } "                                             \
+       "       } "                                                     \
+       ") "                                                            \
+"(SELECT GROUP_CONCAT(fn:concat(?url_value, \"\31\", "                 \
+       "tracker:coalesce(rdfs:label(?c_role), \"\")), \"\30\") "       \
+       "WHERE {"                                                       \
+               "?_contact nco:hasAffiliation ?c_role . "               \
+               "?c_role nco:url ?url_value . "                         \
+"})"                                                                   \
+       "nie:url(nco:photo(?_contact)) "                                \
+       "nco:role(?_role) "                                             \
+       "nco:contactUID(?_contact) "                                    \
+       "nco:title(?_role) "                                            \
+       "rdfs:label(?_role) "                                           \
+       "nco:fullname(nco:org(?_role)) "                                \
+       "nco:department(?_role) "                                       \
+"(SELECT GROUP_CONCAT(fn:concat(?emailaddress,\"\31\","                        \
+       "tracker:coalesce(rdfs:label(?c_role), \"\")),"                 \
+       "\"\30\") "                                                     \
+       "WHERE { "                                                      \
+       "?_contact nco:hasAffiliation ?c_role . "                       \
+       "?c_role nco:hasEmailAddress "                                  \
+       "               [ nco:emailAddress ?emailaddress ] "            \
+       "}) "                                                           \
+       "nmo:receivedDate(?_call) "                                     \
+       "nmo:isSent(?_call) "                                           \
+       "nmo:isAnswered(?_call) "                                       \
+       "?_call "                                                       \
+CALLS_CONSTRAINTS(CONSTRAINT)                                          \
+"ORDER BY DESC(nmo:sentDate(?_call)) "
+
+#define MISSED_CONSTRAINT              \
+"?_call nmo:from ?_unb_contact . "     \
+"?_call nmo:isSent false . "           \
+"?_call nmo:isAnswered false . "
+
+#define INCOMING_CONSTRAINT            \
+"?_call nmo:from ?_unb_contact . "     \
+"?_call nmo:isSent false . "           \
+"?_call nmo:isAnswered true . "
+
+#define OUTGOING_CONSTRAINT            \
+"?_call nmo:to ?_unb_contact . "       \
+"?_call nmo:isSent true . "
+
+#define COMBINED_CONSTRAINT                    \
+"{ "                                           \
+"      ?_call nmo:from ?_unb_contact .  "      \
+"      ?_call nmo:isSent false "               \
+"} UNION { "                                   \
+"      ?_call nmo:to ?_unb_contact . "         \
+"      ?_call nmo:isSent true "                \
+"} "
+
+#define CALL_URI_CONSTRAINT    \
+COMBINED_CONSTRAINT            \
+"FILTER (?_call = <%s>) "
+
+#define MISSED_CALLS_QUERY CALLS_QUERY(MISSED_CONSTRAINT)
+#define MISSED_CALLS_LIST CALLS_LIST(MISSED_CONSTRAINT)
+#define INCOMING_CALLS_QUERY CALLS_QUERY(INCOMING_CONSTRAINT)
+#define INCOMING_CALLS_LIST CALLS_LIST(INCOMING_CONSTRAINT)
+#define OUTGOING_CALLS_QUERY CALLS_QUERY(OUTGOING_CONSTRAINT)
+#define OUTGOING_CALLS_LIST CALLS_LIST(OUTGOING_CONSTRAINT)
+#define COMBINED_CALLS_QUERY CALLS_QUERY(COMBINED_CONSTRAINT)
+#define COMBINED_CALLS_LIST CALLS_LIST(COMBINED_CONSTRAINT)
+#define CONTACT_FROM_CALL_QUERY CALLS_QUERY(CALL_URI_CONSTRAINT)
+
+#define CONTACTS_QUERY_FROM_URI                                                \
+"SELECT "                                                              \
+"(SELECT GROUP_CONCAT(fn:concat(rdf:type(?aff_number),"                        \
+"\"\31\", nco:phoneNumber(?aff_number)), \"\30\")"                     \
+"WHERE {"                                                              \
+"      ?_role nco:hasPhoneNumber ?aff_number"                          \
+"}) "                                                                  \
+"nco:fullname(<%s>) "                                                  \
+"nco:nameFamily(<%s>) "                                                        \
+"nco:nameGiven(<%s>) "                                                 \
+"nco:nameAdditional(<%s>) "                                            \
+"nco:nameHonorificPrefix(<%s>) "                                       \
+"nco:nameHonorificSuffix(<%s>) "                                       \
+"(SELECT GROUP_CONCAT(fn:concat("                                      \
+"tracker:coalesce(nco:pobox(?aff_addr), \"\"), \"\37\","               \
+"tracker:coalesce(nco:extendedAddress(?aff_addr), \"\"), \"\37\","     \
+"tracker:coalesce(nco:streetAddress(?aff_addr), \"\"), \"\37\","       \
+"tracker:coalesce(nco:locality(?aff_addr), \"\"), \"\37\","            \
+"tracker:coalesce(nco:region(?aff_addr), \"\"), \"\37\","              \
+"tracker:coalesce(nco:postalcode(?aff_addr), \"\"), \"\37\","          \
+"tracker:coalesce(nco:country(?aff_addr), \"\"), "                     \
+"\"\31\", rdfs:label(?_role) ), "                                      \
+"\"\30\") "                                                            \
+"WHERE {"                                                              \
+"?_role nco:hasPostalAddress ?aff_addr"                                        \
+"}) "                                                                  \
+"nco:birthDate(<%s>) "                                                 \
+"(SELECT "                                                             \
+"      ?nick "                                                         \
+"      WHERE { "                                                       \
+"              { "                                                     \
+"                      ?_contact nco:nickname ?nick "                  \
+"              } UNION { "                                             \
+"                      ?_contact nco:hasAffiliation ?role . "          \
+"                      ?role nco:hasIMAddress ?im . "                  \
+"                      ?im nco:imNickname ?nick "                      \
+"              } "                                                     \
+"              FILTER (?_contact = <%s>)"                              \
+"      } "                                                             \
+") "                                                                   \
+"(SELECT GROUP_CONCAT(fn:concat( "                                     \
+       "?url_val, \"\31\", tracker:coalesce(rdfs:label(?_role), \"\") "\
+       "), \"\30\") "                                                  \
+       "WHERE {"                                                       \
+               "?_role nco:url ?url_val . "                            \
+"})"                                                                   \
+"nie:url(nco:photo(<%s>)) "                                            \
+"nco:role(?_role) "                                                    \
+"nco:contactUID(<%s>) "                                                        \
+"nco:title(?_role) "                                                   \
+"rdfs:label(?_role) "                                                  \
+"nco:fullname(nco:org(?_role))"                                                \
+"nco:department(?_role) "                                              \
+"(SELECT GROUP_CONCAT(fn:concat(?emailaddress,\"\31\","                        \
+       "tracker:coalesce(rdfs:label(?_role), \"\")),"                  \
+       "\"\30\") "                                                     \
+       "WHERE { "                                                      \
+       "?_role nco:hasEmailAddress "                                   \
+       "               [ nco:emailAddress ?emailaddress ] "            \
+       "}) "                                                           \
+"\"NOTACALL\" \"false\" \"false\" "                                    \
+"<%s> "                                                                        \
+"WHERE {"                                                              \
+"      <%s> a nco:PersonContact ."                                     \
+"      OPTIONAL {<%s> nco:hasAffiliation ?_role .}"                    \
+"}"
+
+#define CONTACTS_OTHER_QUERY_FROM_URI                                  \
+       "SELECT fn:concat(\"TYPE_OTHER\", \"\31\", nco:phoneNumber(?t))"\
+       "\"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" "                      \
+       "\"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" "            \
+       " \"NOTACALL\" \"false\" \"false\" <%s> "                       \
+       "WHERE { "                                                      \
+               "<%s> a nco:Contact . "                                 \
+               "OPTIONAL { <%s> nco:hasPhoneNumber ?t . } "            \
+       "} "
+
+#define CONTACTS_COUNT_QUERY                                           \
+       "SELECT COUNT(?c) "                                             \
+       "WHERE {"                                                       \
+               "?c a nco:PersonContact ."                              \
+       "}"
+
+#define MISSED_CALLS_COUNT_QUERY                                       \
+       "SELECT COUNT(?call) WHERE {"                                   \
+               "?c a nco:Contact ;"                                    \
+               "nco:hasPhoneNumber ?h ."                               \
+               "?call a nmo:Call ;"                                    \
+               "nmo:isSent false ;"                                    \
+               "nmo:from ?c ;"                                         \
+               "nmo:isAnswered false ."                                \
+       "}"
+
+#define INCOMING_CALLS_COUNT_QUERY                                     \
+       "SELECT COUNT(?call) WHERE {"                                   \
+               "?c a nco:Contact ;"                                    \
+               "nco:hasPhoneNumber ?h ."                               \
+               "?call a nmo:Call ;"                                    \
+               "nmo:isSent false ;"                                    \
+               "nmo:from ?c ;"                                         \
+               "nmo:isAnswered true ."                                 \
+       "}"
+
+#define OUTGOING_CALLS_COUNT_QUERY                                     \
+       "SELECT COUNT(?call) WHERE {"                                   \
+               "?c a nco:Contact ;"                                    \
+               "nco:hasPhoneNumber ?h ."                               \
+               "?call a nmo:Call ;"                                    \
+               "nmo:isSent true ;"                                     \
+               "nmo:to ?c ."                                           \
+       "}"
+
+#define COMBINED_CALLS_COUNT_QUERY                                     \
+       "SELECT COUNT(?call) WHERE {"                                   \
+       "{"                                                             \
+               "?c a nco:Contact ;"                                    \
+               "nco:hasPhoneNumber ?h ."                               \
+               "?call a nmo:Call ;"                                    \
+               "nmo:isSent true ;"                                     \
+               "nmo:to ?c ."                                           \
+       "}UNION {"                                                      \
+               "?c a nco:Contact ;"                                    \
+               "nco:hasPhoneNumber ?h ."                               \
+               "?call a nmo:Call ;"                                    \
+               "nmo:from ?c ."                                         \
+       "}"                                                             \
+       "}"
+
+#define NEW_MISSED_CALLS_COUNT_QUERY                                   \
+       "SELECT COUNT(?call) WHERE {"                                   \
+               "?c a nco:Contact ;"                                    \
+               "nco:hasPhoneNumber ?h ."                               \
+               "?call a nmo:Call ;"                                    \
+               "nmo:isSent false ;"                                    \
+               "nmo:from ?c ;"                                         \
+               "nmo:isAnswered false ;"                                \
+               "nmo:isRead false ."                                    \
+       "}"
+
+typedef int (*reply_list_foreach_t) (const char **reply, int num_fields,
+                                                       void *user_data);
+
+typedef void (*add_field_t) (struct phonebook_contact *contact,
+                                               const char *value, int type);
+
+struct pending_reply {
+       reply_list_foreach_t callback;
+       void *user_data;
+       int num_fields;
+};
+
+struct contact_data {
+       char *id;
+       struct phonebook_contact *contact;
+};
+
+struct phonebook_data {
+       phonebook_cb cb;
+       void *user_data;
+       int index;
+       gboolean vcardentry;
+       const struct apparam_field *params;
+       GSList *contacts;
+       phonebook_cache_ready_cb ready_cb;
+       phonebook_entry_cb entry_cb;
+       int newmissedcalls;
+       GCancellable *query_canc;
+       char *req_name;
+       int vcard_part_count;
+       int tracker_index;
+};
+
+struct phonebook_index {
+       GArray *phonebook;
+       int index;
+};
+
+static TrackerSparqlConnection *connection = NULL;
+
+static const char *name2query(const char *name)
+{
+       if (g_str_equal(name, PB_CONTACTS))
+               return CONTACTS_QUERY_ALL;
+       else if (g_str_equal(name, PB_CALLS_INCOMING))
+               return INCOMING_CALLS_QUERY;
+       else if (g_str_equal(name, PB_CALLS_OUTGOING))
+               return OUTGOING_CALLS_QUERY;
+       else if (g_str_equal(name, PB_CALLS_MISSED))
+               return MISSED_CALLS_QUERY;
+       else if (g_str_equal(name, PB_CALLS_COMBINED))
+               return COMBINED_CALLS_QUERY;
+
+       return NULL;
+}
+
+static const char *name2count_query(const char *name)
+{
+       if (g_str_equal(name, PB_CONTACTS))
+               return CONTACTS_COUNT_QUERY;
+       else if (g_str_equal(name, PB_CALLS_INCOMING))
+               return INCOMING_CALLS_COUNT_QUERY;
+       else if (g_str_equal(name, PB_CALLS_OUTGOING))
+               return OUTGOING_CALLS_COUNT_QUERY;
+       else if (g_str_equal(name, PB_CALLS_MISSED))
+               return MISSED_CALLS_COUNT_QUERY;
+       else if (g_str_equal(name, PB_CALLS_COMBINED))
+               return COMBINED_CALLS_COUNT_QUERY;
+
+       return NULL;
+}
+
+static gboolean folder_is_valid(const char *folder)
+{
+       if (folder == NULL)
+               return FALSE;
+
+       if (g_str_equal(folder, "/"))
+               return TRUE;
+       else if (g_str_equal(folder, PB_TELECOM_FOLDER))
+               return TRUE;
+       else if (g_str_equal(folder, PB_CONTACTS_FOLDER))
+               return TRUE;
+       else if (g_str_equal(folder, PB_CALLS_INCOMING_FOLDER))
+               return TRUE;
+       else if (g_str_equal(folder, PB_CALLS_OUTGOING_FOLDER))
+               return TRUE;
+       else if (g_str_equal(folder, PB_CALLS_MISSED_FOLDER))
+               return TRUE;
+       else if (g_str_equal(folder, PB_CALLS_COMBINED_FOLDER))
+               return TRUE;
+
+       return FALSE;
+}
+
+static const char *folder2query(const char *folder)
+{
+       if (g_str_equal(folder, PB_CONTACTS_FOLDER))
+               return CONTACTS_QUERY_ALL_LIST;
+       else if (g_str_equal(folder, PB_CALLS_INCOMING_FOLDER))
+               return INCOMING_CALLS_LIST;
+       else if (g_str_equal(folder, PB_CALLS_OUTGOING_FOLDER))
+               return OUTGOING_CALLS_LIST;
+       else if (g_str_equal(folder, PB_CALLS_MISSED_FOLDER))
+               return MISSED_CALLS_LIST;
+       else if (g_str_equal(folder, PB_CALLS_COMBINED_FOLDER))
+               return COMBINED_CALLS_LIST;
+
+       return NULL;
+}
+
+static const char **string_array_from_cursor(TrackerSparqlCursor *cursor,
+                                                               int array_len)
+{
+       const char **result;
+       int i;
+
+       result = g_new0(const char *, array_len);
+
+       for (i = 0; i < array_len; ++i) {
+               TrackerSparqlValueType type;
+
+               type = tracker_sparql_cursor_get_value_type(cursor, i);
+
+               if (type == TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE ||
+                               type == TRACKER_SPARQL_VALUE_TYPE_UNBOUND)
+                       /* For null/unbound type filling result part with ""*/
+                       result[i] = "";
+               else
+                       /* Filling with string representation of content*/
+                       result[i] = tracker_sparql_cursor_get_string(cursor, i,
+                                                                       NULL);
+       }
+
+       return result;
+}
+
+static void update_cancellable(struct phonebook_data *pdata,
+                                                       GCancellable *canc)
+{
+       if (pdata->query_canc)
+               g_object_unref(pdata->query_canc);
+
+       pdata->query_canc = canc;
+}
+
+static void async_query_cursor_next_cb(GObject *source, GAsyncResult *result,
+                                                       gpointer user_data)
+{
+       struct pending_reply *pending = user_data;
+       TrackerSparqlCursor *cursor = TRACKER_SPARQL_CURSOR(source);
+       GCancellable *cancellable;
+       GError *error = NULL;
+       gboolean success;
+       const char **node;
+       int err;
+
+       success = tracker_sparql_cursor_next_finish(
+                                               TRACKER_SPARQL_CURSOR(source),
+                                               result, &error);
+
+       if (!success) {
+               if (error) {
+                       DBG("cursor_next error: %s", error->message);
+                       g_error_free(error);
+               } else
+                       /* When tracker_sparql_cursor_next_finish ends with
+                        * failure and no error is set, that means end of
+                        * results returned by query */
+                       pending->callback(NULL, 0, pending->user_data);
+
+               goto failed;
+       }
+
+       node = string_array_from_cursor(cursor, pending->num_fields);
+       err = pending->callback(node, pending->num_fields, pending->user_data);
+       g_free(node);
+
+       /* Fetch next result only if processing current chunk ended with
+        * success. Sometimes during processing data, we are able to determine
+        * if there is no need to get more data from tracker - by example
+        * stored amount of data parts is big enough for sending and we might
+        * want to suspend processing or just some error occurred. */
+       if (!err) {
+               cancellable = g_cancellable_new();
+               update_cancellable(pending->user_data, cancellable);
+               tracker_sparql_cursor_next_async(cursor, cancellable,
+                                               async_query_cursor_next_cb,
+                                               pending);
+               return;
+       }
+
+failed:
+       g_object_unref(cursor);
+       g_free(pending);
+}
+
+static int query_tracker(const char *query, int num_fields,
+                               reply_list_foreach_t callback, void *user_data)
+{
+       struct pending_reply *pending;
+       GCancellable *cancellable;
+       TrackerSparqlCursor *cursor;
+       GError *error = NULL;
+
+       DBG("");
+
+       if (connection == NULL)
+               connection = tracker_sparql_connection_get_direct(
+                                                               NULL, &error);
+
+       if (!connection) {
+               if (error) {
+                       DBG("direct-connection error: %s", error->message);
+                       g_error_free(error);
+               }
+
+               return -EINTR;
+       }
+
+       cancellable = g_cancellable_new();
+       update_cancellable(user_data, cancellable);
+       cursor = tracker_sparql_connection_query(connection, query,
+                                                       cancellable, &error);
+
+       if (cursor == NULL) {
+               if (error) {
+                       DBG("connection_query error: %s", error->message);
+                       g_error_free(error);
+               }
+
+               g_object_unref(cancellable);
+
+               return -EINTR;
+       }
+
+       pending = g_new0(struct pending_reply, 1);
+       pending->callback = callback;
+       pending->user_data = user_data;
+       pending->num_fields = num_fields;
+
+       /* Now asynchronously going through each row of results - callback
+        * async_query_cursor_next_cb will be called ALWAYS, even if async
+        * request was canceled */
+       tracker_sparql_cursor_next_async(cursor, cancellable,
+                                               async_query_cursor_next_cb,
+                                               pending);
+
+       return 0;
+}
+
+static char *iso8601_utc_to_localtime(const char *datetime)
+{
+       time_t time;
+       struct tm tm, *local;
+       char localdate[32];
+       int nr;
+
+       memset(&tm, 0, sizeof(tm));
+
+       nr = sscanf(datetime, "%04u-%02u-%02uT%02u:%02u:%02u",
+                       &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+                       &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
+       if (nr < 6) {
+               /* Invalid time format */
+               error("sscanf(): %s (%d)", strerror(errno), errno);
+               return g_strdup("");
+       }
+
+       /* Time already in localtime */
+       if (!g_str_has_suffix(datetime, "Z")) {
+               strftime(localdate, sizeof(localdate), "%Y%m%dT%H%M%S", &tm);
+               return g_strdup(localdate);
+       }
+
+       tm.tm_year -= 1900;     /* Year since 1900 */
+       tm.tm_mon--;            /* Months since January, values 0-11 */
+
+       time = mktime(&tm);
+       time -= timezone;
+
+       local = localtime(&time);
+
+       strftime(localdate, sizeof(localdate), "%Y%m%dT%H%M%S", local);
+
+       return g_strdup(localdate);
+}
+
+static void set_call_type(struct phonebook_contact *contact,
+                               const char *datetime, const char *is_sent,
+                               const char *is_answered)
+{
+       gboolean sent, answered;
+
+       if (g_strcmp0(datetime, "NOTACALL") == 0) {
+               contact->calltype = CALL_TYPE_NOT_A_CALL;
+               return;
+       }
+
+       sent = g_str_equal(is_sent, "true");
+       answered = g_str_equal(is_answered, "true");
+
+       if (sent == FALSE) {
+               if (answered == FALSE)
+                       contact->calltype = CALL_TYPE_MISSED;
+               else
+                       contact->calltype = CALL_TYPE_INCOMING;
+       } else
+               contact->calltype = CALL_TYPE_OUTGOING;
+
+       /* Tracker gives time in the ISO 8601 format, UTC time */
+       contact->datetime = iso8601_utc_to_localtime(datetime);
+}
+
+static gboolean contact_matches(struct contact_data *c_data, const char *id,
+                                                       const char *datetime)
+{
+       char *localtime;
+       int cmp_ret;
+
+       if (g_strcmp0(c_data->id, id) != 0)
+               return FALSE;
+
+       /* id is equal and not call history entry => contact matches */
+       if (c_data->contact->calltype == CALL_TYPE_NOT_A_CALL)
+               return TRUE;
+
+       /* for call history entries have to compare also timestamps of calls */
+       localtime = iso8601_utc_to_localtime(datetime);
+       cmp_ret = g_strcmp0(c_data->contact->datetime, localtime);
+       g_free(localtime);
+
+       return (cmp_ret == 0) ? TRUE : FALSE;
+}
+
+static struct phonebook_contact *find_contact(GSList *contacts, const char *id,
+                                                       const char *datetime)
+{
+       GSList *l;
+
+       for (l = contacts; l; l = l->next) {
+               struct contact_data *c_data = l->data;
+
+               if (contact_matches(c_data, id, datetime))
+                       return c_data->contact;
+       }
+
+       return NULL;
+}
+
+static struct phonebook_field *find_field(GSList *fields, const char *value,
+                                                               int type)
+{
+       GSList *l;
+
+       for (l = fields; l; l = l->next) {
+               struct phonebook_field *field = l->data;
+               /* Returning phonebook number if phone values and type values
+                * are equal */
+               if (g_strcmp0(field->text, value) == 0 && field->type == type)
+                       return field;
+       }
+
+       return NULL;
+}
+
+static void add_phone_number(struct phonebook_contact *contact,
+                                               const char *phone, int type)
+{
+       struct phonebook_field *number;
+
+       if (phone == NULL || strlen(phone) == 0)
+               return;
+
+       /* Not adding number if there is already added with the same value */
+       if (find_field(contact->numbers, phone, type))
+               return;
+
+       number = g_new0(struct phonebook_field, 1);
+       number->text = g_strdup(phone);
+       number->type = type;
+
+       contact->numbers = g_slist_append(contact->numbers, number);
+}
+
+static void add_email(struct phonebook_contact *contact, const char *address,
+                                                               int type)
+{
+       struct phonebook_field *email;
+
+       if (address == NULL || strlen(address) == 0)
+               return;
+
+       /* Not adding email if there is already added with the same value */
+       if (find_field(contact->emails, address, type))
+               return;
+
+       email = g_new0(struct phonebook_field, 1);
+       email->text = g_strdup(address);
+       email->type = type;
+
+       contact->emails = g_slist_append(contact->emails, email);
+}
+
+static gboolean addr_matches(struct phonebook_addr *a, struct phonebook_addr *b)
+{
+       GSList *la, *lb;
+
+       if (a->type != b->type)
+               return FALSE;
+
+       for (la = a->fields, lb = b->fields; la && lb;
+                                               la = la->next, lb = lb->next) {
+               char *field_a = la->data;
+               char *field_b = lb->data;
+
+               if (g_strcmp0(field_a, field_b) != 0)
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+/* generates phonebook_addr struct from tracker address data string. */
+static struct phonebook_addr *gen_addr(const char *address, int type)
+{
+       struct phonebook_addr *addr;
+       GSList *fields = NULL;
+       char **addr_parts;
+       int i;
+
+       /* This test handles cases when address points to empty string
+        * (or address is NULL pointer) or string containing only six
+        * separators. It indicates that none of address fields is present
+        * and there is no sense to create dummy phonebook_addr struct */
+       if (address == NULL || strlen(address) < ADDR_FIELD_AMOUNT)
+               return NULL;
+
+       addr_parts = g_strsplit(address, ADDR_DELIM, ADDR_FIELD_AMOUNT);
+
+       for (i = 0; i < ADDR_FIELD_AMOUNT; ++i)
+               fields = g_slist_append(fields, g_strdup(addr_parts[i]));
+
+       g_strfreev(addr_parts);
+
+       addr = g_new0(struct phonebook_addr, 1);
+       addr->fields = fields;
+       addr->type = type;
+
+       return addr;
+}
+
+static void add_address(struct phonebook_contact *contact,
+                                       const char *address, int type)
+{
+       struct phonebook_addr *addr;
+       GSList *l;
+
+       addr = gen_addr(address, type);
+       if (addr == NULL)
+               return;
+
+       /* Not adding address if there is already added with the same value.
+        * These type of checks have to be done because sometimes tracker
+        * returns results for contact data in more than 1 row - then the same
+        * address may be returned more than once in query results */
+       for (l = contact->addresses; l; l = l->next) {
+               struct phonebook_addr *tmp = l->data;
+
+               if (addr_matches(tmp, addr)) {
+                       phonebook_addr_free(addr);
+                       return;
+               }
+       }
+
+       contact->addresses = g_slist_append(contact->addresses, addr);
+}
+
+static void add_url(struct phonebook_contact *contact, const char *url_val,
+                                                               int type)
+{
+       struct phonebook_field *url;
+
+       if (url_val == NULL || strlen(url_val) == 0)
+               return;
+
+       /* Not adding url if there is already added with the same value */
+       if (find_field(contact->urls, url_val, type))
+               return;
+
+       url = g_new0(struct phonebook_field, 1);
+
+       url->text = g_strdup(url_val);
+       url->type = type;
+
+       contact->urls = g_slist_append(contact->urls, url);
+}
+
+static GString *gen_vcards(GSList *contacts,
+                                       const struct apparam_field *params)
+{
+       GSList *l;
+       GString *vcards;
+
+       vcards = g_string_new(NULL);
+
+       /* Generating VCARD string from contacts and freeing used contacts */
+       for (l = contacts; l; l = l->next) {
+               struct contact_data *c_data = l->data;
+               phonebook_add_contact(vcards, c_data->contact,
+                                       params->filter, params->format);
+       }
+
+       return vcards;
+}
+
+static int pull_contacts_size(const char **reply, int num_fields,
+                                                       void *user_data)
+{
+       struct phonebook_data *data = user_data;
+
+       if (num_fields < 0) {
+               data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data);
+               return -EINTR;
+       }
+
+       if (reply != NULL) {
+               data->index = atoi(reply[0]);
+               return 0;
+       }
+
+       data->cb(NULL, 0, data->index, data->newmissedcalls, TRUE,
+                                                       data->user_data);
+
+       return 0;
+       /*
+        * phonebook_data is freed in phonebook_req_finalize. Useful in
+        * cases when call is terminated.
+        */
+}
+
+static void add_affiliation(char **field, const char *value)
+{
+       if (strlen(*field) > 0 || value == NULL || strlen(value) == 0)
+               return;
+
+       g_free(*field);
+
+       *field = g_strdup(value);
+}
+
+static void contact_init(struct phonebook_contact *contact,
+                                                       const char **reply)
+{
+       if (reply[COL_FAMILY_NAME][0] == '\0' &&
+                       reply[COL_GIVEN_NAME][0] == '\0' &&
+                       reply[COL_ADDITIONAL_NAME][0] == '\0' &&
+                       reply[COL_NAME_PREFIX][0] == '\0' &&
+                       reply[COL_NAME_SUFFIX][0] == '\0') {
+               if (reply[COL_FULL_NAME][0] != '\0')
+                       contact->family = g_strdup(reply[COL_FULL_NAME]);
+               else
+                       contact->family = g_strdup(reply[COL_NICKNAME]);
+       } else {
+               contact->family = g_strdup(reply[COL_FAMILY_NAME]);
+               contact->given = g_strdup(reply[COL_GIVEN_NAME]);
+               contact->additional = g_strdup(reply[COL_ADDITIONAL_NAME]);
+               contact->prefix = g_strdup(reply[COL_NAME_PREFIX]);
+               contact->suffix = g_strdup(reply[COL_NAME_SUFFIX]);
+       }
+       contact->fullname = g_strdup(reply[COL_FULL_NAME]);
+       contact->birthday = g_strdup(reply[COL_BIRTH_DATE]);
+       contact->nickname = g_strdup(reply[COL_NICKNAME]);
+       contact->photo = g_strdup(reply[COL_PHOTO]);
+       contact->company = g_strdup(reply[COL_ORG_NAME]);
+       contact->department = g_strdup(reply[COL_ORG_DEPARTMENT]);
+       contact->role = g_strdup(reply[COL_ORG_ROLE]);
+       contact->uid = g_strdup(reply[COL_UID]);
+       contact->title = g_strdup(reply[COL_TITLE]);
+
+       set_call_type(contact, reply[COL_DATE], reply[COL_SENT],
+                                                       reply[COL_ANSWERED]);
+}
+
+static enum phonebook_number_type get_phone_type(const char *affilation)
+{
+       if (g_strcmp0(AFFILATION_HOME, affilation) == 0)
+               return TEL_TYPE_HOME;
+       else if (g_strcmp0(AFFILATION_WORK, affilation) == 0)
+               return TEL_TYPE_WORK;
+
+       return TEL_TYPE_OTHER;
+}
+
+static void add_aff_number(struct phonebook_contact *contact,
+                               const char *pnumber, const char *aff_type)
+{
+       char **num_parts;
+       char *type, *number;
+
+       /* For phone taken directly from contacts data, phone number string
+        * is represented as number type and number string - those strings are
+        * separated by SUB_DELIM string */
+       num_parts = g_strsplit(pnumber, SUB_DELIM, 2);
+
+       if (!num_parts)
+               return;
+
+       if (num_parts[0])
+               type = num_parts[0];
+       else
+               goto failed;
+
+       if (num_parts[1])
+               number = num_parts[1];
+       else
+               goto failed;
+
+       if (g_strrstr(type, FAX_NUM_TYPE))
+               add_phone_number(contact, number, TEL_TYPE_FAX);
+       else if (g_strrstr(type, MOBILE_NUM_TYPE))
+               add_phone_number(contact, number, TEL_TYPE_MOBILE);
+       else
+               /* if this is no fax/mobile phone, then adding phone number
+                * type based on type of the affilation field */
+               add_phone_number(contact, number, get_phone_type(aff_type));
+
+failed:
+       g_strfreev(num_parts);
+}
+
+static void contact_add_numbers(struct phonebook_contact *contact,
+                                                       const char **reply)
+{
+       char **aff_numbers;
+       int i;
+
+       /* Filling phone numbers from contact's affilation */
+       aff_numbers = g_strsplit(reply[COL_PHONE_AFF], MAIN_DELIM, MAX_FIELDS);
+
+       if (aff_numbers)
+               for (i = 0; aff_numbers[i]; ++i)
+                       add_aff_number(contact, aff_numbers[i],
+                                                       reply[COL_AFF_TYPE]);
+
+       g_strfreev(aff_numbers);
+}
+
+static enum phonebook_field_type get_field_type(const char *affilation)
+{
+       if (g_strcmp0(AFFILATION_HOME, affilation) == 0)
+               return FIELD_TYPE_HOME;
+       else if (g_strcmp0(AFFILATION_WORK, affilation) == 0)
+               return FIELD_TYPE_WORK;
+
+       return FIELD_TYPE_OTHER;
+}
+
+static void add_aff_field(struct phonebook_contact *contact,
+                       const char *aff_email, add_field_t add_field_cb)
+{
+       char **email_parts;
+       char *type, *email;
+
+       /* Emails from affilation data, are represented as real email
+        * string and affilation type - those strings are separated by
+        * SUB_DELIM string */
+       email_parts = g_strsplit(aff_email, SUB_DELIM, 2);
+
+       if (!email_parts)
+               return;
+
+       if (email_parts[0])
+               email = email_parts[0];
+       else
+               goto failed;
+
+       if (email_parts[1])
+               type = email_parts[1];
+       else
+               goto failed;
+
+       add_field_cb(contact, email, get_field_type(type));
+
+failed:
+       g_strfreev(email_parts);
+}
+
+static void contact_add_emails(struct phonebook_contact *contact,
+                                                       const char **reply)
+{
+       char **aff_emails;
+       int i;
+
+       /* Emails from affilation */
+       aff_emails = g_strsplit(reply[COL_EMAIL_AFF], MAIN_DELIM, MAX_FIELDS);
+
+       if (aff_emails)
+               for (i = 0; aff_emails[i] != NULL; ++i)
+                       add_aff_field(contact, aff_emails[i], add_email);
+
+       g_strfreev(aff_emails);
+}
+
+static void contact_add_addresses(struct phonebook_contact *contact,
+                                                       const char **reply)
+{
+       char **aff_addr;
+       int i;
+
+       /* Addresses from affilation */
+       aff_addr = g_strsplit(reply[COL_ADDR_AFF], MAIN_DELIM, MAX_FIELDS);
+
+       if (aff_addr)
+               for (i = 0; aff_addr[i] != NULL; ++i)
+                       add_aff_field(contact, aff_addr[i], add_address);
+
+       g_strfreev(aff_addr);
+}
+
+static void contact_add_urls(struct phonebook_contact *contact,
+                                                       const char **reply)
+{
+       char **aff_url;
+       int i;
+
+       /* Addresses from affilation */
+       aff_url = g_strsplit(reply[COL_URL], MAIN_DELIM, MAX_FIELDS);
+
+       if (aff_url)
+               for (i = 0; aff_url[i] != NULL; ++i)
+                       add_aff_field(contact, aff_url[i], add_url);
+
+       g_strfreev(aff_url);
+}
+
+static void contact_add_organization(struct phonebook_contact *contact,
+                                                       const char **reply)
+{
+       /* Adding fields connected by nco:hasAffiliation - they may be in
+        * separate replies */
+       add_affiliation(&contact->title, reply[COL_TITLE]);
+       add_affiliation(&contact->company, reply[COL_ORG_NAME]);
+       add_affiliation(&contact->department, reply[COL_ORG_DEPARTMENT]);
+       add_affiliation(&contact->role, reply[COL_ORG_ROLE]);
+}
+
+static void free_data_contacts(struct phonebook_data *data)
+{
+       GSList *l;
+
+       /* freeing contacts */
+       for (l = data->contacts; l; l = l->next) {
+               struct contact_data *c_data = l->data;
+
+               g_free(c_data->id);
+               phonebook_contact_free(c_data->contact);
+               g_free(c_data);
+       }
+
+       g_slist_free(data->contacts);
+       data->contacts = NULL;
+}
+
+static void send_pull_part(struct phonebook_data *data,
+                       const struct apparam_field *params, gboolean lastpart)
+{
+       GString *vcards;
+
+       DBG("");
+       vcards = gen_vcards(data->contacts, params);
+       data->cb(vcards->str, vcards->len, g_slist_length(data->contacts),
+                       data->newmissedcalls, lastpart, data->user_data);
+
+       if (!lastpart)
+               free_data_contacts(data);
+       g_string_free(vcards, TRUE);
+}
+
+static int pull_contacts(const char **reply, int num_fields, void *user_data)
+{
+       struct phonebook_data *data = user_data;
+       const struct apparam_field *params = data->params;
+       struct phonebook_contact *contact;
+       struct contact_data *contact_data;
+       int last_index, i;
+       gboolean cdata_present = FALSE, part_sent = FALSE;
+       static char *temp_id = NULL;
+
+       if (num_fields < 0) {
+               data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data);
+               goto fail;
+       }
+
+       DBG("reply %p", reply);
+       data->tracker_index++;
+
+       if (reply == NULL)
+               goto done;
+
+       /* Trying to find contact in recently added contacts. It is needed for
+        * contacts that have more than one telephone number filled */
+       contact = find_contact(data->contacts, reply[CONTACTS_ID_COL],
+                                                       reply[COL_DATE]);
+
+       /* If contact is already created then adding only new phone numbers */
+       if (contact) {
+               cdata_present = TRUE;
+               goto add_numbers;
+       }
+
+       /* We are doing a PullvCardEntry, no need for those checks */
+       if (data->vcardentry)
+               goto add_entry;
+
+       /* Last four fields are always present, ignoring them */
+       for (i = 0; i < num_fields - 4; i++) {
+               if (reply[i][0] != '\0')
+                       break;
+       }
+
+       if (i == num_fields - 4 && !g_str_equal(reply[CONTACTS_ID_COL],
+                                               TRACKER_DEFAULT_CONTACT_ME))
+               return 0;
+
+       if (g_strcmp0(temp_id, reply[CONTACTS_ID_COL])) {
+               data->index++;
+               g_free(temp_id);
+               temp_id = g_strdup(reply[CONTACTS_ID_COL]);
+
+               /* Incrementing counter for vcards in current part of data,
+                * but only if liststartoffset has been already reached */
+               if (data->index > params->liststartoffset)
+                       data->vcard_part_count++;
+       }
+
+       if (data->vcard_part_count > VCARDS_PART_COUNT) {
+               DBG("Part of vcard data ready for sending...");
+               data->vcard_part_count = 0;
+               /* Sending part of data to PBAP core - more data can be still
+                * fetched, so marking lastpart as FALSE */
+               send_pull_part(data, params, FALSE);
+
+               /* Later, after adding contact data, need to return -EINTR to
+                * stop fetching more data for this request. Data will be
+                * downloaded again from this point, when phonebook_pull_read
+                * will be called again with current request as a parameter*/
+               part_sent = TRUE;
+       }
+
+       last_index = params->liststartoffset + params->maxlistcount;
+
+       if (data->index <= params->liststartoffset)
+               return 0;
+
+       /* max number of results achieved - need send vcards data that was
+        * already collected and stop further data processing (these operations
+        * will be invoked in "done" section) */
+       if (data->index > last_index && params->maxlistcount > 0) {
+               DBG("Maxlistcount achieved");
+               goto done;
+       }
+
+add_entry:
+       contact = g_new0(struct phonebook_contact, 1);
+       contact_init(contact, reply);
+
+add_numbers:
+       contact_add_numbers(contact, reply);
+       contact_add_emails(contact, reply);
+       contact_add_addresses(contact, reply);
+       contact_add_urls(contact, reply);
+       contact_add_organization(contact, reply);
+
+       DBG("contact %p", contact);
+
+       /* Adding contacts data to wrapper struct - this data will be used to
+        * generate vcard list */
+       if (!cdata_present) {
+               contact_data = g_new0(struct contact_data, 1);
+               contact_data->contact = contact;
+               contact_data->id = g_strdup(reply[CONTACTS_ID_COL]);
+               data->contacts = g_slist_append(data->contacts, contact_data);
+       }
+
+       if (part_sent)
+               return -EINTR;
+
+       return 0;
+
+done:
+       /* Processing is end, this is definitely last part of transmission
+        * (marking lastpart as TRUE) */
+       send_pull_part(data, params, TRUE);
+
+fail:
+       g_free(temp_id);
+       temp_id = NULL;
+
+       return -EINTR;
+       /*
+        * phonebook_data is freed in phonebook_req_finalize. Useful in
+        * cases when call is terminated.
+        */
+}
+
+static int add_to_cache(const char **reply, int num_fields, void *user_data)
+{
+       struct phonebook_data *data = user_data;
+       char *formatted;
+       int i;
+
+       if (reply == NULL || num_fields < 0)
+               goto done;
+
+       /* the first element is the URI, always not empty */
+       for (i = 1; i < num_fields; i++) {
+               if (reply[i][0] != '\0')
+                       break;
+       }
+
+       if (i == num_fields &&
+                       !g_str_equal(reply[0], TRACKER_DEFAULT_CONTACT_ME))
+               return 0;
+
+       if (i == 7)
+               formatted = g_strdup(reply[7]);
+       else if (i == 6)
+               formatted = g_strdup(reply[6]);
+       else
+               formatted = g_strdup_printf("%s;%s;%s;%s;%s",
+                                       reply[1], reply[2], reply[3], reply[4],
+                                       reply[5]);
+
+       /* The owner vCard must have the 0 handle */
+       if (strcmp(reply[0], TRACKER_DEFAULT_CONTACT_ME) == 0)
+               data->entry_cb(reply[0], 0, formatted, "",
+                                               reply[6], data->user_data);
+       else
+               data->entry_cb(reply[0], PHONEBOOK_INVALID_HANDLE, formatted,
+                                       "", reply[6], data->user_data);
+
+       g_free(formatted);
+
+       return 0;
+
+done:
+       if (num_fields <= 0)
+               data->ready_cb(data->user_data);
+
+       return -EINTR;
+       /*
+        * phonebook_data is freed in phonebook_req_finalize. Useful in
+        * cases when call is terminated.
+        */
+}
+
+int phonebook_init(void)
+{
+       g_thread_init(NULL);
+       g_type_init();
+
+       return 0;
+}
+
+void phonebook_exit(void)
+{
+}
+
+char *phonebook_set_folder(const char *current_folder, const char *new_folder,
+                                               uint8_t flags, int *err)
+{
+       char *tmp1, *tmp2, *base, *path = NULL;
+       gboolean root, child;
+       int ret = 0;
+       int len;
+
+       root = (g_strcmp0("/", current_folder) == 0);
+       child = (new_folder && strlen(new_folder) != 0);
+
+       switch (flags) {
+       case 0x02:
+               /* Go back to root */
+               if (!child) {
+                       path = g_strdup("/");
+                       goto done;
+               }
+
+               path = g_build_filename(current_folder, new_folder, NULL);
+               break;
+       case 0x03:
+               /* Go up 1 level */
+               if (root) {
+                       /* Already root */
+                       path = g_strdup("/");
+                       goto done;
+               }
+
+               /*
+                * Removing one level of the current folder. Current folder
+                * contains AT LEAST one level since it is not at root folder.
+                * Use glib utility functions to handle invalid chars in the
+                * folder path properly.
+                */
+               tmp1 = g_path_get_basename(current_folder);
+               tmp2 = g_strrstr(current_folder, tmp1);
+               len = tmp2 - (current_folder + 1);
+
+               g_free(tmp1);
+
+               if (len == 0)
+                       base = g_strdup("/");
+               else
+                       base = g_strndup(current_folder, len);
+
+               /* Return: one level only */
+               if (!child) {
+                       path = base;
+                       goto done;
+               }
+
+               path = g_build_filename(base, new_folder, NULL);
+               g_free(base);
+
+               break;
+       default:
+               ret = -EBADR;
+               break;
+       }
+
+done:
+       if (path && !folder_is_valid(path))
+               ret = -ENOENT;
+
+       if (ret < 0) {
+               g_free(path);
+               path = NULL;
+       }
+
+       if (err)
+               *err = ret;
+
+       return path;
+}
+
+static int pull_newmissedcalls(const char **reply, int num_fields,
+                                                       void *user_data)
+{
+       struct phonebook_data *data = user_data;
+       reply_list_foreach_t pull_cb;
+       int col_amount, err;
+       const char *query;
+       int nmissed;
+
+       if (num_fields < 0) {
+               data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data);
+
+               return -EINTR;
+       }
+
+       if (reply != NULL) {
+               nmissed = atoi(reply[0]);
+               data->newmissedcalls =
+                       nmissed <= UINT8_MAX ? nmissed : UINT8_MAX;
+               DBG("newmissedcalls %d", data->newmissedcalls);
+
+               return 0;
+       }
+
+       if (data->params->maxlistcount == 0) {
+               query = name2count_query(PB_CALLS_MISSED);
+               col_amount = COUNT_QUERY_COL_AMOUNT;
+               pull_cb = pull_contacts_size;
+       } else {
+               query = name2query(PB_CALLS_MISSED);
+               col_amount = PULL_QUERY_COL_AMOUNT;
+               pull_cb = pull_contacts;
+       }
+
+       err = query_tracker(query, col_amount, pull_cb, data);
+       if (err < 0) {
+               data->cb(NULL, 0, err, 0, TRUE, data->user_data);
+
+               return -EINTR;
+       }
+
+       return 0;
+}
+
+void phonebook_req_finalize(void *request)
+{
+       struct phonebook_data *data = request;
+
+       DBG("");
+
+       if (!data)
+               return;
+
+       /* canceling asynchronous operation on tracker if any is active */
+       if (data->query_canc) {
+               g_cancellable_cancel(data->query_canc);
+               g_object_unref(data->query_canc);
+       }
+
+       free_data_contacts(data);
+       g_free(data->req_name);
+       g_free(data);
+}
+
+void *phonebook_pull(const char *name, const struct apparam_field *params,
+                               phonebook_cb cb, void *user_data, int *err)
+{
+       struct phonebook_data *data;
+
+       DBG("name %s", name);
+
+       data = g_new0(struct phonebook_data, 1);
+       data->params = params;
+       data->user_data = user_data;
+       data->cb = cb;
+       data->req_name = g_strdup(name);
+
+       if (err)
+               *err = 0;
+
+       return data;
+}
+
+int phonebook_pull_read(void *request)
+{
+       struct phonebook_data *data = request;
+       reply_list_foreach_t pull_cb;
+       const char *query;
+       char *offset_query;
+       int col_amount;
+       int ret;
+
+       if (!data)
+               return -ENOENT;
+
+       data->newmissedcalls = 0;
+
+       if (g_strcmp0(data->req_name, PB_CALLS_MISSED) == 0 &&
+                                               data->tracker_index == 0) {
+               /* new missed calls amount should be counted only once - it
+                * will be done during generating first part of results of
+                * missed calls history */
+               query = NEW_MISSED_CALLS_COUNT_QUERY;
+               col_amount = COUNT_QUERY_COL_AMOUNT;
+               pull_cb = pull_newmissedcalls;
+       } else if (data->params->maxlistcount == 0) {
+               query = name2count_query(data->req_name);
+               col_amount = COUNT_QUERY_COL_AMOUNT;
+               pull_cb = pull_contacts_size;
+       } else {
+               query = name2query(data->req_name);
+               col_amount = PULL_QUERY_COL_AMOUNT;
+               pull_cb = pull_contacts;
+       }
+
+       if (query == NULL)
+               return -ENOENT;
+
+       if (pull_cb == pull_contacts && data->tracker_index > 0) {
+               /* Adding offset to pull query to download next parts of data
+                * from tracker (phonebook_pull_read may be called many times
+                * from PBAP core to fetch data partially) */
+               offset_query = g_strdup_printf(QUERY_OFFSET_FORMAT, query,
+                                                       data->tracker_index);
+               ret = query_tracker(offset_query, col_amount, pull_cb, data);
+
+               g_free(offset_query);
+
+               return ret;
+       }
+
+       return query_tracker(query, col_amount, pull_cb, data);
+}
+
+void *phonebook_get_entry(const char *folder, const char *id,
+                               const struct apparam_field *params,
+                               phonebook_cb cb, void *user_data, int *err)
+{
+       struct phonebook_data *data;
+       char *query;
+       int ret;
+
+       DBG("folder %s id %s", folder, id);
+
+       data = g_new0(struct phonebook_data, 1);
+       data->user_data = user_data;
+       data->params = params;
+       data->cb = cb;
+       data->vcardentry = TRUE;
+
+       if (g_str_has_prefix(id, CONTACT_ID_PREFIX) == TRUE ||
+                               g_strcmp0(id, TRACKER_DEFAULT_CONTACT_ME) == 0)
+               query = g_strdup_printf(CONTACTS_QUERY_FROM_URI, id, id, id, id,
+                                       id, id, id, id, id, id, id, id, id);
+       else if (g_str_has_prefix(id, CALL_ID_PREFIX) == TRUE)
+               query = g_strdup_printf(CONTACT_FROM_CALL_QUERY, id);
+       else
+               query = g_strdup_printf(CONTACTS_OTHER_QUERY_FROM_URI,
+                                                               id, id, id);
+
+       ret = query_tracker(query, PULL_QUERY_COL_AMOUNT, pull_contacts, data);
+       if (err)
+               *err = ret;
+
+       g_free(query);
+
+       return data;
+}
+
+void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
+               phonebook_cache_ready_cb ready_cb, void *user_data, int *err)
+{
+       struct phonebook_data *data;
+       const char *query;
+       int ret;
+
+       DBG("name %s", name);
+
+       query = folder2query(name);
+       if (query == NULL) {
+               if (err)
+                       *err = -ENOENT;
+               return NULL;
+       }
+
+       data = g_new0(struct phonebook_data, 1);
+       data->entry_cb = entry_cb;
+       data->ready_cb = ready_cb;
+       data->user_data = user_data;
+
+       ret = query_tracker(query, 8, add_to_cache, data);
+       if (err)
+               *err = ret;
+
+       return data;
+}
diff --git a/obexd/plugins/syncevolution.c b/obexd/plugins/syncevolution.c
new file mode 100755 (executable)
index 0000000..854505a
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2010  Intel Corporation
+ *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+
+#include "gdbus/gdbus.h"
+
+#include "btio/btio.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/mimetype.h"
+#include "obexd/src/log.h"
+#include "obexd/src/manager.h"
+#include "obexd/src/obexd.h"
+#include "filesystem.h"
+
+#define SYNCML_TARGET_SIZE 11
+
+static const uint8_t SYNCML_TARGET[SYNCML_TARGET_SIZE] = {
+                       0x53, 0x59, 0x4E, 0x43, 0x4D, 0x4C, 0x2D, 0x53,
+                       0x59, 0x4E, 0x43 };
+
+#define SYNCEVOLUTION_CHANNEL  19
+
+#define SYNCEVOLUTION_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\
+<record>                                                               \
+ <attribute id=\"0x0001\">                                             \
+    <sequence>                                                         \
+      <uuid value=\"00000002-0000-1000-8000-0002ee000002\"/>           \
+    </sequence>                                                        \
+ </attribute>                                                          \
+                                                                       \
+ <attribute id=\"0x0004\">                                             \
+    <sequence>                                                         \
+      <sequence>                                                       \
+        <uuid value=\"0x0100\"/>                                       \
+      </sequence>                                                      \
+      <sequence>                                                       \
+        <uuid value=\"0x0003\"/>                                       \
+        <uint8 value=\"%u\" name=\"channel\"/>                         \
+      </sequence>                                                      \
+      <sequence>                                                       \
+        <uuid value=\"0x0008\"/>                                       \
+      </sequence>                                                      \
+    </sequence>                                                        \
+ </attribute>                                                          \
+                                                                       \
+ <attribute id=\"0x0100\">                                             \
+    <text value=\"%s\" name=\"name\"/>                                 \
+ </attribute>                                                          \
+</record>"
+
+#define SYNCE_BUS_NAME "org.syncevolution"
+#define SYNCE_PATH     "/org/syncevolution/Server"
+#define SYNCE_SERVER_INTERFACE "org.syncevolution.Server"
+#define SYNCE_CONN_INTERFACE   "org.syncevolution.Connection"
+
+struct synce_context {
+       struct obex_session *os;
+       DBusConnection *dbus_conn;
+       char *conn_obj;
+       unsigned int reply_watch;
+       unsigned int abort_watch;
+       GString *buffer;
+       int lasterr;
+       char *id;
+};
+
+static void append_dict_entry(DBusMessageIter *dict, const char *key,
+                                                       int type, void *val)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                       NULL, &entry);
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val);
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static gboolean reply_signal(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct synce_context *context = data;
+       const char *path = dbus_message_get_path(msg);
+       DBusMessageIter iter, array_iter;
+       char *value;
+       int length;
+
+       if (strcmp(context->conn_obj, path) != 0) {
+               obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
+               context->lasterr = -EPERM;
+               return FALSE;
+       }
+
+       dbus_message_iter_init(msg, &iter);
+
+       dbus_message_iter_recurse(&iter, &array_iter);
+       dbus_message_iter_get_fixed_array(&array_iter, &value, &length);
+
+       context->buffer = g_string_new_len(value, length);
+       obex_object_set_io_flags(context, G_IO_IN, 0);
+       context->lasterr = 0;
+
+       return TRUE;
+}
+
+static gboolean abort_signal(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct synce_context *context = data;
+
+       obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
+       context->lasterr = -EPERM;
+
+       return TRUE;
+}
+
+static void connect_cb(DBusPendingCall *call, void *user_data)
+{
+       struct synce_context *context = user_data;
+       DBusConnection *conn;
+       DBusMessage *reply;
+       DBusError err;
+       char *path;
+
+       conn = context->dbus_conn;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               error("%s", err.message);
+               dbus_error_free(&err);
+               goto failed;
+       }
+
+       DBG("Got conn object %s from syncevolution", path);
+       context->conn_obj = g_strdup(path);
+
+       context->reply_watch = g_dbus_add_signal_watch(conn, NULL, path,
+                                               SYNCE_CONN_INTERFACE, "Reply",
+                                               reply_signal, context, NULL);
+
+       context->abort_watch = g_dbus_add_signal_watch(conn, NULL, path,
+                                               SYNCE_CONN_INTERFACE, "Abort",
+                                               abort_signal, context, NULL);
+
+       dbus_message_unref(reply);
+
+       return;
+
+failed:
+       obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
+       context->lasterr = -EPERM;
+}
+
+static void process_cb(DBusPendingCall *call, void *user_data)
+{
+       struct synce_context *context = user_data;
+       DBusMessage *reply;
+       DBusError derr;
+
+       reply = dbus_pending_call_steal_reply(call);
+       dbus_error_init(&derr);
+       if (dbus_set_error_from_message(&derr, reply)) {
+               error("process_cb(): syncevolution replied with an error:"
+                                       " %s, %s", derr.name, derr.message);
+               dbus_error_free(&derr);
+
+               obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
+               context->lasterr = -EPERM;
+               goto done;
+       }
+
+       obex_object_set_io_flags(context, G_IO_OUT, 0);
+       context->lasterr = 0;
+
+done:
+       dbus_message_unref(reply);
+}
+
+static void *synce_connect(struct obex_session *os, int *err)
+{
+       DBusConnection *conn;
+       struct synce_context *context;
+       char *address;
+
+       manager_register_session(os);
+
+       conn = manager_dbus_get_connection();
+       if (!conn)
+               goto failed;
+
+       context = g_new0(struct synce_context, 1);
+       context->dbus_conn = conn;
+       context->lasterr = -EAGAIN;
+       context->os = os;
+
+       if (obex_getpeername(os, &address) == 0) {
+               context->id = g_strdup_printf("%s+%d", address,
+                                                       SYNCEVOLUTION_CHANNEL);
+               g_free(address);
+       }
+
+       if (err)
+               *err = 0;
+
+       return context;
+
+failed:
+       if (err)
+               *err = -EPERM;
+
+       return NULL;
+}
+
+static int synce_put(struct obex_session *os, void *user_data)
+{
+       return 0;
+}
+
+static int synce_get(struct obex_session *os, void *user_data)
+{
+       return obex_get_stream_start(os, NULL);
+}
+
+static void close_cb(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply;
+       DBusError derr;
+
+       reply = dbus_pending_call_steal_reply(call);
+       dbus_error_init(&derr);
+       if (dbus_set_error_from_message(&derr, reply)) {
+               error("close_cb(): syncevolution replied with an error:"
+                                       " %s, %s", derr.name, derr.message);
+               dbus_error_free(&derr);
+       }
+
+       dbus_message_unref(reply);
+}
+
+static void synce_disconnect(struct obex_session *os, void *user_data)
+{
+       struct synce_context *context = user_data;
+
+       g_free(context);
+}
+
+static void *synce_open(const char *name, int oflag, mode_t mode,
+                               void *user_data, size_t *size, int *err)
+{
+       struct synce_context *context = user_data;
+
+       if (err)
+               *err = context ? 0 : -EFAULT;
+
+       return user_data;
+}
+
+static int synce_close(void *object)
+{
+       struct synce_context *context = object;
+       DBusMessage *msg;
+       const char *error;
+       gboolean normal;
+       DBusPendingCall *call;
+
+       if (!context->conn_obj)
+               goto done;
+
+       msg = dbus_message_new_method_call(SYNCE_BUS_NAME, context->conn_obj,
+                                               SYNCE_CONN_INTERFACE, "Close");
+       if (!msg)
+               goto failed;
+
+       normal = TRUE;
+       error = "none";
+       dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &normal,
+                               DBUS_TYPE_STRING, &error, DBUS_TYPE_INVALID);
+
+       g_dbus_send_message_with_reply(context->dbus_conn, msg, &call, -1);
+       dbus_pending_call_set_notify(call, close_cb, NULL, NULL);
+       dbus_message_unref(msg);
+       dbus_pending_call_unref(call);
+
+failed:
+       g_dbus_remove_watch(context->dbus_conn, context->reply_watch);
+       context->reply_watch = 0;
+       g_dbus_remove_watch(context->dbus_conn, context->abort_watch);
+       context->abort_watch = 0;
+
+       g_free(context->conn_obj);
+       context->conn_obj = NULL;
+
+done:
+       dbus_connection_unref(context->dbus_conn);
+       g_free(context);
+       return 0;
+}
+
+static ssize_t synce_read(void *object, void *buf, size_t count)
+{
+       struct synce_context *context = object;
+       DBusConnection *conn;
+       char transport[36], transport_description[24];
+       const char *session;
+       DBusMessage *msg;
+       DBusMessageIter iter, dict;
+       gboolean authenticate;
+       DBusPendingCall *call;
+
+       if (context->buffer)
+               return string_read(context->buffer, buf, count);
+
+       conn = manager_dbus_get_connection();
+       if (conn == NULL)
+               return -EPERM;
+
+       msg = dbus_message_new_method_call(SYNCE_BUS_NAME, SYNCE_PATH,
+                               SYNCE_SERVER_INTERFACE, "Connect");
+       if (!msg)
+               return -EPERM;
+
+       dbus_message_iter_init_append(msg, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+               DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+               DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       append_dict_entry(&dict, "id", DBUS_TYPE_STRING, context->id);
+
+       snprintf(transport, sizeof(transport), "%s.obexd", OBEXD_SERVICE);
+       append_dict_entry(&dict, "transport", DBUS_TYPE_STRING, transport);
+
+       snprintf(transport_description, sizeof(transport_description),
+                                               "version %s", VERSION);
+       append_dict_entry(&dict, "transport_description", DBUS_TYPE_STRING,
+                                                       transport_description);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       authenticate = FALSE;
+       session = "";
+       dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &authenticate,
+                       DBUS_TYPE_STRING, &session, DBUS_TYPE_INVALID);
+
+       if (!g_dbus_send_message_with_reply(conn, msg, &call, -1)) {
+               error("D-Bus call to %s failed.", SYNCE_SERVER_INTERFACE);
+               dbus_message_unref(msg);
+               return -EPERM;
+       }
+
+       dbus_pending_call_set_notify(call, connect_cb, context, NULL);
+
+       dbus_pending_call_unref(call);
+       dbus_message_unref(msg);
+
+       return -EAGAIN;
+}
+
+static ssize_t synce_write(void *object, const void *buf, size_t count)
+{
+       struct synce_context *context = object;
+       DBusMessage *msg;
+       DBusMessageIter iter, array_iter;
+       DBusPendingCall *call;
+       const char *type = obex_get_type(context->os);
+
+       if (context->lasterr == 0)
+               return count;
+
+       if (!context->conn_obj)
+               return -EFAULT;
+
+       msg = dbus_message_new_method_call(SYNCE_BUS_NAME, context->conn_obj,
+                                       SYNCE_CONN_INTERFACE, "Process");
+       if (!msg)
+               return -EFAULT;
+
+       dbus_message_iter_init_append(msg, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_BYTE_AS_STRING, &array_iter);
+
+       dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE,
+                                               &buf, count);
+       dbus_message_iter_close_container(&iter, &array_iter);
+
+       dbus_message_append_args(msg, DBUS_TYPE_STRING, &type,
+                                               DBUS_TYPE_INVALID);
+
+       if (!g_dbus_send_message_with_reply(context->dbus_conn, msg,
+                                                               &call, -1)) {
+               error("D-Bus call to %s failed.", SYNCE_CONN_INTERFACE);
+               dbus_message_unref(msg);
+               return -EPERM;
+       }
+
+       dbus_pending_call_set_notify(call, process_cb, context, NULL);
+
+       dbus_message_unref(msg);
+       dbus_pending_call_unref(call);
+
+       return -EAGAIN;
+}
+
+static struct obex_mime_type_driver synce_driver = {
+       .target = SYNCML_TARGET,
+       .target_size = SYNCML_TARGET_SIZE,
+       .open = synce_open,
+       .close = synce_close,
+       .read = synce_read,
+       .write = synce_write,
+};
+
+static struct obex_service_driver synce = {
+       .name = "OBEX server for SyncML, using SyncEvolution",
+       .service = OBEX_SYNCEVOLUTION,
+       .channel = SYNCEVOLUTION_CHANNEL,
+       .secure = TRUE,
+       .record = SYNCEVOLUTION_RECORD,
+       .target = SYNCML_TARGET,
+       .target_size = SYNCML_TARGET_SIZE,
+       .get = synce_get,
+       .put = synce_put,
+       .connect = synce_connect,
+       .disconnect = synce_disconnect,
+};
+
+static int synce_init(void)
+{
+       int err;
+
+       err = obex_mime_type_driver_register(&synce_driver);
+       if (err < 0)
+               return err;
+
+       return obex_service_driver_register(&synce);
+}
+
+static void synce_exit(void)
+{
+       obex_service_driver_unregister(&synce);
+       obex_mime_type_driver_unregister(&synce_driver);
+}
+
+OBEX_PLUGIN_DEFINE(syncevolution, synce_init, synce_exit)
index c774cda..e2df4ef 100755 (executable)
@@ -179,6 +179,14 @@ static GOptionEntry options[] = {
        { NULL },
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void obex_option_set_root_folder(const char *root)
+{
+       g_free(option_root);
+       option_root = g_strdup(root);
+}
+#endif
+
 gboolean obex_option_auto_accept(void)
 {
        return option_autoaccept;
index f84384a..90c5114 100755 (executable)
@@ -190,6 +190,52 @@ static DBusMessage *unregister_agent(DBusConnection *conn,
        return dbus_message_new_method_return(msg);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *set_root(DBusConnection *conn, DBusMessage *msg,
+                                       const char *root, void *data)
+{
+       DBG("new_root: %s", root);
+
+       /* Change the option root path (using in filesystem) */
+       obex_option_set_root_folder(root);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter;
+       DBusMessageIter sub;
+       const char *property;
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return invalid_args(msg);
+       dbus_message_iter_recurse(&iter, &sub);
+
+       if (g_str_equal("Root", property)) {
+               const char *root;
+
+               if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
+                       return invalid_args(msg);
+               dbus_message_iter_get_basic(&sub, &root);
+
+               return set_root(conn, msg, root, data);
+       }
+
+       return invalid_args(msg);
+}
+#endif
+
 static gboolean get_source(const GDBusPropertyTable *property,
                                        DBusMessageIter *iter, void *data)
 {
@@ -440,6 +486,79 @@ static gboolean transfer_get_filename(const GDBusPropertyTable *property,
        return TRUE;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean transfer_operation_exists(const GDBusPropertyTable *property,
+                                                               void *data)
+{
+       struct obex_transfer *transfer = data;
+       struct obex_session *session = transfer->session;
+
+       if (session->cmd == G_OBEX_OP_PUT &&
+                               session->size != OBJECT_SIZE_DELETE)
+               return TRUE;
+       else if (session->cmd == G_OBEX_OP_GET)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+static gboolean transfer_get_operation(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct obex_transfer *transfer = data;
+       struct obex_session *session = transfer->session;
+       const char *operation;
+
+       if (session->cmd == G_OBEX_OP_PUT &&
+                               session->size != OBJECT_SIZE_DELETE)
+               operation = "PUT";
+       else if (session->cmd == G_OBEX_OP_GET)
+               operation = "GET";
+       else
+               return FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &operation);
+
+       return TRUE;
+}
+
+static gboolean transfer_address_exists(const GDBusPropertyTable *property,
+               void *data)
+{
+       struct obex_transfer *transfer = data;
+       struct obex_session *session = transfer->session;
+       char *address;
+       int err;
+
+       err = obex_getpeername(session, &address);
+       if (err < 0)
+               return FALSE;
+
+       g_free(address);
+
+       return TRUE;
+}
+
+static gboolean transfer_get_address(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct obex_transfer *transfer = data;
+       struct obex_session *session = transfer->session;
+       char *address;
+       int err;
+
+       err = obex_getpeername(session, &address);
+       if (err < 0)
+               return FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &address);
+       g_free(address);
+
+       return TRUE;
+}
+
+#endif
+
 static gboolean transfer_get_transferred(const GDBusPropertyTable *property,
                                        DBusMessageIter *iter, void *data)
 {
@@ -457,6 +576,10 @@ static const GDBusMethodTable manager_methods[] = {
                        GDBUS_ARGS({ "agent", "o" }), NULL, register_agent) },
        { GDBUS_METHOD("UnregisterAgent",
                        GDBUS_ARGS({ "agent", "o" }), NULL, unregister_agent) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { GDBUS_METHOD("SetProperty",
+                       GDBUS_ARGS({ "property", "sv" }), NULL, set_property) },
+#endif
        { }
 };
 
@@ -474,6 +597,12 @@ static const GDBusPropertyTable transfer_properties[] = {
        { "Time", "t", transfer_get_time, NULL, transfer_time_exists },
        { "Filename", "s", transfer_get_filename, NULL,
                                                transfer_filename_exists },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { "Operation", "s", transfer_get_operation, NULL,
+                                       transfer_operation_exists },
+       { "Address", "s", transfer_get_address, NULL,
+                                       transfer_address_exists },
+#endif
        { "Transferred", "t", transfer_get_transferred },
        { }
 };
index 788bffc..fee2461 100755 (executable)
@@ -642,7 +642,17 @@ static void parse_name(struct obex_session *os, GObexPacket *req)
 
        if (!g_obex_header_get_unicode(hdr, &name))
                return;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       DBG("Obex Session For: %s", os->service->name);
+       if (name && g_strcmp0(os->service->name, "Object Push server") == 0) {
+               char *new_name;
+               new_name = strrchr(name, '/');
+               if (new_name) {
+                       name = new_name + 1;
+                       DBG("FileName %s", name);
+               }
+       }
+#endif
        os->name = g_strdup(name);
        DBG("NAME: %s", os->name);
 }
@@ -773,7 +783,11 @@ int obex_put_stream_start(struct obex_session *os, const char *filename)
        int err;
 
        os->object = os->driver->open(filename, O_WRONLY | O_CREAT | O_TRUNC,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                                       0644, os->service_data,
+#else
                                        0600, os->service_data,
+#endif
                                        os->size != OBJECT_SIZE_UNKNOWN ?
                                        (size_t *) &os->size : NULL, &err);
        if (os->object == NULL) {
index 42c3c4d..ddf5ba0 100755 (executable)
@@ -41,3 +41,6 @@ gboolean obex_option_auto_accept(void);
 const char *obex_option_root_folder(void);
 gboolean obex_option_symlinks(void);
 const char *obex_option_capability(void);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void obex_option_set_root_folder(const char *root);
+#endif
index a538088..3abb555 100755 (executable)
@@ -1,4 +1,3 @@
 [D-BUS Service]
 Name=org.bluez.obex
-Exec=/bin/false
-SystemdService=dbus-org.bluez.obex.service
+Exec=/bin/sh -c 'exec /usr/libexec/bluetooth/obexd -d --noplugin=ftp,syncevolution,pcsuite,irmc --symlinks -r /opt/usr/home/owner/media/'
index ff12594..42d5729 100755 (executable)
@@ -1,10 +1,14 @@
+# Do not create provides fro extension-tm1 because the main package
+# should anchor any reverse-dependencies
+%global __provides_exclude_from ^(.*\\.tm1)$
+
 #%define with_libcapng --enable-capng
 %define _libpath /usr/lib
 %define upgrade_script_path /usr/share/upgrade/scripts
 Name:          bluez
 Summary:       Bluetooth Stack for Linux
-Version:       5.37
-Release:       2
+Version:       5.43
+Release:       1
 Group:         Network & Connectivity/Bluetooth
 License:       GPL-2.0+ and LGPL-2.1+ and Apache-2.0
 URL:           http://www.bluez.org/
@@ -23,6 +27,8 @@ Source1001:     bluez.manifest
 #Requires:   dbus >= 0.60
 #BuildRequires:  pkgconfig(libudev)
 BuildRequires:  pkgconfig(dbus-1)
+BuildRequires:  pkgconfig(iniparser)
+BuildRequires:  pkgconfig(libxml-2.0)
 #BuildRequires:  pkgconfig(glib-2.0)
 #BuildRequires:  pkgconfig(ncurses)
 #BuildRequires:  flex
@@ -45,6 +51,8 @@ BuildRequires:  pkg-config
 BuildRequires:  readline-devel
 BuildRequires:  udev
 BuildRequires:  pkgconfig(libtzplatform-config)
+Requires:      %{name}-compat = %{version}-%{release}
+Recommends:    %{name}-profile_common = %{version}-%{release}
 
 %description
 The Bluetooth stack for Linux.
@@ -87,6 +95,43 @@ Requires:       python-gobject
 Contains a few tools for testing various bluetooth functions. The
 BLUETOOTH trademarks are owned by Bluetooth SIG, Inc., U.S.A.
 
+%package profile_common
+Summary:       Modified bluez for mobile/common profile
+Provides:      %{name}-compat = %{version}-%{release}
+Provides:      %{name}-profile_mobile = %{version}-%{release}
+Provides:      %{name}-profile_ivi = %{version}-%{release}
+Conflicts:     %{name}-profile_wearable
+Conflicts:     %{name}-profile_tv
+%description profile_common
+Bluez default service script for Tizen (mobile/common profile)
+
+%package profile_wearable
+Summary:       Modified bluez for wearable profile
+Provides:      %{name}-compat = %{version}-%{release}
+Conflicts:     %{name}-profile_common
+Conflicts:     %{name}-profile_tv
+%description profile_wearable
+Bluez modified service script for Tizen wearable
+
+%package profile_tv
+Summary:       Modified bluez for wearable profile
+Provides:      %{name}-compat = %{version}-%{release}
+Conflicts:     %{name}-profile_common
+Conflicts:     %{name}-profile_wearable
+%description profile_tv
+Bluez modified service script for Tizen TV
+
+%ifarch %{ix86} || %{arm}
+%package -n libbluetooth-extension-TM1
+Summary:       Extension for mobile TM1
+Requires:      libbluetooth = %{version}-%{release}
+%description -n libbluetooth-extension-TM1
+Bluez default service script for Tizen mobile TM1
+When you want to uninstall this while keeping libbluetooth, you need
+to reinstall libbluetooth after uninstalling this because this package
+overwrites some contents of libbluetooth.
+%endif
+
 %prep
 %setup -q
 cp %{SOURCE1001} .
@@ -94,20 +139,72 @@ cp %{SOURCE1001} .
 %build
 autoreconf -fiv
 
-%if "%{?tizen_target_name}" == "TM1" || "%{?profile}" == "ivi"
-%if "%{?tizen_target_name}" == "TM1"
-export CFLAGS="${CFLAGS} -DTIZEN_FEATURE_BLUEZ_SPRD_QOS -DTIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN"
-%endif
-%else
-export CFLAGS="${CFLAGS} -DTIZEN_FEATURE_BLUEZ_BRCM_CHIP"
-%endif
+#if "{?profile}" == "ivi"
+# TIZEN_FEATURE_BLUEZ_BRCM_CHIP: only in tools/hciattach.c ==> RUNTIME! (profile.h)
+#endif
 
-%if "%{?profile}" == "wearable"
-export CFLAGS="${CFLAGS} -DTIZEN_FEATURE_BLUEZ_SMS_ONLY -DTIZEN_FEATURE_BLUEZ_BRCM_QOS -DTIZEN_FEATURE_BLUEZ_ROLE_CHANGE -DTIZEN_FEATURE_BLUEZ_CONFIRM_ONLY"
-%endif
+#if "{?profile}" == "wearable"
+# TIZEN_FEATURE_BLUEZ_SMS_ONLY: only in obexd/plugins/messages-tizen.c ==> RUNTIME! (profile.h)
+# TIZEN_FEATURE_BLUEZ_BRCM_QOS: only in profiles/audio/avdtp.c ==> RUNTIME! (profile.h)
+# TIZEN_FEATURE_BLUEZ_ROLE_CHANGE: only in profiles/audio/avdtp.c ==> RUNTIME! (profile.h)
+# TIZEN_FEATURE_BLUEZ_CONFIRM_ONLY: only in src/device.c ==> RUNTIME! (profile.h)
+#endif
 
 export LDFLAGS=" -lncurses -Wl,--as-needed "
 export CFLAGS+=" -DTIZEN_FEATURE_BLUEZ_MODIFY -DTIZEN_FEATURE_BLUEZ_PBAP_SIM -DTIZEN_FEATURE_BLUEZ_AVRCP_TARGET"
+export CFLAGS_DEFAULT="$CFLAGS"
+
+%ifarch %{ix86} || %{arm}
+# extension-TM1
+export CFLAGS="$CFLAGS_DEFAULT -DTIZEN_FEATURE_BLUEZ_SPRD_QOS -DTIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN"
+# TIZEN_FEATURE_BLUEZ_SPRD_QOS: only in profiles/audio/avdtp.c
+# TIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN: src/adapter.c
+
+%reconfigure --disable-static \
+                       --sysconfdir=%{_sysconfdir} \
+                       --localstatedir=%{_localstatedir} \
+                       --with-systemdsystemunitdir=%{_libpath}/systemd/system \
+                       --with-systemduserunitdir=%{_libpath}/systemd/user \
+                       --libexecdir=%{_libexecdir} \
+                       --enable-debug \
+                       --enable-pie \
+                       --enable-serial \
+                       --enable-input \
+                       --enable-usb=no \
+                       --enable-tools \
+                       --disable-bccmd \
+                       --enable-pcmcia=no \
+                       --enable-hid2hci=no \
+                       --enable-alsa=no \
+                       --enable-gstreamer=no \
+                       --disable-dfutool \
+                       --disable-cups \
+                       --enable-health=yes \
+                       --enable-proximity=yes \
+                       --enable-tds=yes \
+                       --enable-dbusoob \
+                       --enable-test \
+                       --with-telephony=tizen \
+                       --enable-obex \
+                       --enable-library \
+                       --enable-gatt \
+                       --enable-experimental \
+                       --enable-autopair=no \
+                       --enable-hid=yes \
+                       --enable-tizenunusedplugin=no
+
+
+make %{?_smp_mflags} all V=1
+
+mkdir -p tm1
+
+%make_install
+cp -a  %{buildroot}%{_libdir}/libbluetooth.so* tm1/
+%endif
+
+# non-extension-TM1
+export CFLAGS="$CFLAGS_DEFAULT"
+
 %reconfigure --disable-static \
                        --sysconfdir=%{_sysconfdir} \
                        --localstatedir=%{_localstatedir} \
@@ -140,12 +237,15 @@ export CFLAGS+=" -DTIZEN_FEATURE_BLUEZ_MODIFY -DTIZEN_FEATURE_BLUEZ_PBAP_SIM -DT
                        --enable-autopair=no \
 %if "%{?profile}" == "wearable"
                         --enable-wearable \
-%else
-                       --enable-network \
 %endif
                        --enable-hid=yes \
                        --enable-tizenunusedplugin=no
+# The if/endif for wearable above if only for PRODUCT optimization
 
+# enable-wearable disables "TIZEN_HID_PLUGIN / TIZEN_UNUSED_PLUGIN" (input / hog)
+# TIZEN_UNUSED_PLUGIN is "no" regardless of enable-wearable.
+# TIZEN_HID_PLUGIN only matters.
+# enable-network is not used in configure.
 
 make %{?_smp_mflags} all V=1
 
@@ -154,6 +254,11 @@ make check
 
 %install
 %make_install
+%ifarch %{ix86} || %{arm}
+pushd tm1
+for FILE in libbluetooth.so*; do mv "$FILE" "%{buildroot}%{_libdir}/$FILE.tm1"; done
+popd
+%endif
 
 # bluez-test
 rm -rvf $RPM_BUILD_ROOT/%{_libdir}/gstreamer-*
@@ -163,11 +268,9 @@ install --mode=0644 -D %{S:7} $RPM_BUILD_ROOT/%{_sysconfdir}/modprobe.d/50-bluet
 # no idea why this is suddenly necessary...
 install --mode 0755 -d $RPM_BUILD_ROOT/var/lib/bluetooth
 
-%if "%{?profile}" == "wearable"
-install -D -m 0644 src/main_w.conf %{buildroot}%{_sysconfdir}/bluetooth/main.conf
-%else
+install -D -m 0644 src/main_w.conf %{buildroot}%{_sysconfdir}/bluetooth/main.conf.wearable
 install -D -m 0644 src/main_m.conf %{buildroot}%{_sysconfdir}/bluetooth/main.conf
-%endif
+
 
 #install -D -m 0644 src/bluetooth.conf %{buildroot}%{_sysconfdir}/dbus-1/system.d/bluetooth.conf
 #install -D -m 0644 profiles/audio/audio.conf %{buildroot}%{_sysconfdir}/bluetooth/audio.conf
@@ -192,11 +295,11 @@ install -D -m 0755 attrib/gatttool $RPM_BUILD_ROOT/%{_bindir}/
 install -D -m 0755 tools/obexctl %{buildroot}%{_bindir}/obexctl
 
 #test
-%if "%{?profile}" == "tv"
+#if "tv"
 mkdir -p %{buildroot}%{_libpath}/systemd/system/multi-user.target.wants/
 ln -sf bluetooth.service %{buildroot}%{_libpath}/systemd/system/dbus-org.bluez.service
 ln -sf ../bluetooth-frwk.service %{buildroot}%{_libpath}/systemd/system/multi-user.target.wants/bluetooth.service
-%endif
+#endif
 
 mkdir -p %{buildroot}%{upgrade_script_path}
 cp -f packaging/500.bluez_upgrade.sh %{buildroot}%{upgrade_script_path}
@@ -205,12 +308,19 @@ cp -f packaging/500.bluez_upgrade.sh %{buildroot}%{upgrade_script_path}
 
 %postun -n libbluetooth -p /sbin/ldconfig
 
+%ifarch %{ix86} || %{arm}
+%post -n libbluetooth-extension-TM1
+pushd %{_libdir}
+for FILE in libbluetooth.so*.tm1; do mv "$FILE" "${FILE%.tm1}"; done
+popd
+/sbin/ldconfig
+%endif
+
 %files
 %manifest %{name}.manifest
 %defattr(-, root, root)
 %license COPYING
 #%{_sysconfdir}/bluetooth/audio.conf
-%{_sysconfdir}/bluetooth/main.conf
 #%{_sysconfdir}/bluetooth/network.conf
 #%{_sysconfdir}/bluetooth/rfcomm.conf
 #%{_sysconfdir}/dbus-1/system.d/bluetooth.conf
@@ -234,16 +344,9 @@ cp -f packaging/500.bluez_upgrade.sh %{buildroot}%{upgrade_script_path}
 %{upgrade_script_path}/500.bluez_upgrade.sh
 
 #test -2
-%if "%{?profile}" == "tv"
-%{_libpath}/systemd/system/bluetooth.service
-%{_libpath}/systemd/system/multi-user.target.wants/bluetooth.service
-%{_libpath}/systemd/system/dbus-org.bluez.service
-%{_datadir}/dbus-1/system-services/org.bluez.service
-%else
 %exclude /%{_libpath}/systemd/system/bluetooth.service
 %exclude %{_libpath}/systemd/system/multi-user.target.wants/bluetooth.service
 %exclude /%{_datadir}/dbus-1/system-services/org.bluez.service
-%endif
 %config %{_sysconfdir}/dbus-1/system.d/bluetooth.conf
 %dir /var/lib/bluetooth
 %dir %{_sysconfdir}/modprobe.d
@@ -263,8 +366,16 @@ cp -f packaging/500.bluez_upgrade.sh %{buildroot}%{upgrade_script_path}
 %manifest %{name}.manifest
 %defattr(-, root, root)
 %{_libdir}/libbluetooth.so.*
+%exclude %{_libdir}/libbluetooth.so*.tm1
 %license COPYING
 
+%ifarch %{ix86} || %{arm}
+%files -n libbluetooth-extension-TM1
+%manifest %{name}.manifest
+%defattr(-, root, root)
+%{_libdir}/libbluetooth.so*.tm1
+%endif
+
 %files -n obexd
 %manifest %{name}.manifest
 %defattr(-,root,root,-)
@@ -298,4 +409,21 @@ cp -f packaging/500.bluez_upgrade.sh %{buildroot}%{upgrade_script_path}
 
 %docs_package
 
+%post profile_wearable
+ln -sf main.conf.wearable %{_sysconfdir}/bluetooth/main.conf
+%preun profile_wearable
+rm %{_sysconfdir}/bluetooth/main.conf
+%files profile_wearable
+%{_sysconfdir}/bluetooth/main.conf.wearable
+
+%files profile_tv
+%{_sysconfdir}/bluetooth/main.conf
+%{_libpath}/systemd/system/bluetooth.service
+%{_libpath}/systemd/system/multi-user.target.wants/bluetooth.service
+%{_libpath}/systemd/system/dbus-org.bluez.service
+%{_datadir}/dbus-1/system-services/org.bluez.service
+
+%files profile_common
+%{_sysconfdir}/bluetooth/main.conf
+
 %changelog
diff --git a/plugins/dbusoob.c b/plugins/dbusoob.c
new file mode 100755 (executable)
index 0000000..8948d5d
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  ST-Ericsson SA
+ *
+ *  Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ *  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 TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include "gdbus/gdbus.h"
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+
+#include "src/plugin.h"
+#include "src/log.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/eir.h"
+#include "src/agent.h"
+#include "src/hcid.h"
+#include "src/error.h"
+
+#define OOB_INTERFACE  "org.bluez.OutOfBand"
+
+struct oob_request {
+       struct btd_adapter *adapter;
+       DBusMessage *msg;
+};
+
+static GSList *oob_requests = NULL;
+static DBusConnection *connection = NULL;
+
+static gint oob_request_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct oob_request *data = a;
+       const struct btd_adapter *adapter = b;
+
+       return data->adapter != adapter;
+}
+
+static struct oob_request *find_oob_request(struct btd_adapter *adapter)
+{
+       GSList *match;
+
+       match = g_slist_find_custom(oob_requests, adapter, oob_request_cmp);
+
+       if (match)
+               return match->data;
+
+       return NULL;
+}
+
+static void read_local_data_complete(struct btd_adapter *adapter,
+                       const uint8_t *hash192, const uint8_t *randomizer192,
+                       const uint8_t *hash256, const uint8_t *randomizer256,
+                       void *user_data)
+{
+       struct DBusMessage *reply;
+       struct oob_request *oob_request;
+
+       oob_request = find_oob_request(adapter);
+       if (!oob_request)
+               return;
+
+       if ((hash192 && randomizer192) || (hash256 && randomizer256))
+               reply = g_dbus_create_reply(oob_request->msg,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash192, 16,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer192, 16,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+                               &hash256, hash256 ? 16 : 0,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+                               &randomizer256, randomizer256 ? 16 : 0,
+                       DBUS_TYPE_INVALID);
+       else
+               reply = btd_error_failed(oob_request->msg,
+                                       "Failed to read local OOB data.");
+
+       oob_requests = g_slist_remove(oob_requests, oob_request);
+       dbus_message_unref(oob_request->msg);
+       g_free(oob_request);
+
+       if (!reply) {
+               error("Couldn't allocate D-Bus message");
+               return;
+       }
+
+       if (!g_dbus_send_message(connection, reply))
+               error("D-Bus send failed");
+}
+
+static DBusMessage *read_local_data(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct oob_request *oob_request;
+       struct oob_handler *handler;
+
+       if (find_oob_request(adapter))
+               return btd_error_in_progress(msg);
+
+       if (btd_adapter_read_local_oob_data(adapter))
+               return btd_error_failed(msg, "Request failed.");
+
+       oob_request = g_new(struct oob_request, 1);
+       oob_request->adapter = adapter;
+       oob_requests = g_slist_append(oob_requests, oob_request);
+       oob_request->msg = dbus_message_ref(msg);
+
+       handler = g_new0(struct oob_handler, 1);
+       handler->read_local_cb = read_local_data_complete;
+
+       btd_adapter_set_oob_handler(oob_request->adapter, handler);
+
+       return NULL;
+}
+
+static DBusMessage *add_remote_data(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct btd_adapter *adapter = data;
+       const char *addr = NULL;
+       uint8_t *hash192 = NULL;
+       uint8_t *randomizer192 = NULL;
+       int32_t h192_len = 0;
+       int32_t r192_len = 0;
+       uint8_t *hash256 = NULL;
+       uint8_t *randomizer256 = NULL;
+       int32_t h256_len = 0;
+       int32_t r256_len = 0;
+       bdaddr_t bdaddr;
+       uint8_t addr_type = 0;
+       bool valid_len;
+
+       if (!dbus_message_get_args(msg, NULL,
+                       DBUS_TYPE_STRING, &addr,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash192, &h192_len,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer192, &r192_len,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash256, &h256_len,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer256, &r256_len,
+                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       valid_len = (h192_len == 16 && r192_len == 16) ||
+               (h256_len == 16 && r256_len == 16);
+
+       if (!valid_len || bachk(addr))
+               return btd_error_invalid_args(msg);
+
+       str2ba(addr, &bdaddr);
+
+       if (btd_adapter_add_remote_oob_ext_data(adapter, &bdaddr, addr_type,
+                               hash192, randomizer192,
+                               hash256, randomizer256))
+               return btd_error_failed(msg, "Request failed");
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *remove_remote_data(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct btd_adapter *adapter = data;
+       const char *addr;
+       bdaddr_t bdaddr;
+       uint8_t addr_type = 0;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
+                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       if (bachk(addr))
+               return btd_error_invalid_args(msg);
+
+       str2ba(addr, &bdaddr);
+
+       if (btd_adapter_remove_remote_oob_ext_data(adapter, &bdaddr, addr_type))
+               return btd_error_failed(msg, "Request failed");
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable oob_methods[] = {
+       { GDBUS_METHOD("AddRemoteData",
+                       GDBUS_ARGS({ "address", "s" },
+                               { "hash192", "ay" }, { "randomizer192", "ay" },
+                               { "hash256", "ay" }, { "randomizer256", "ay" }),
+                       NULL,
+                       add_remote_data) },
+       { GDBUS_METHOD("RemoveRemoteData",
+                       GDBUS_ARGS({ "address", "s" }), NULL,
+                       remove_remote_data) },
+       { GDBUS_ASYNC_METHOD("ReadLocalData",
+                       NULL, GDBUS_ARGS(
+                               {"hash192", "ay" }, { "randomizer192", "ay" },
+                               {"hash256", "ay" }, { "randomizer256", "ay" }),
+                       read_local_data) },
+       { }
+};
+
+static int oob_probe(struct btd_adapter *adapter)
+{
+       const char *path = adapter_get_path(adapter);
+
+       DBG("dbusoob probe");
+       DBG("adapter path: %s", path);
+
+       if (!g_dbus_register_interface(connection, path, OOB_INTERFACE,
+                               oob_methods, NULL, NULL, adapter, NULL)) {
+                       error("OOB interface init failed on path %s", path);
+                       return -EIO;
+               }
+
+       return 0;
+}
+
+static void oob_remove(struct btd_adapter *adapter)
+{
+       read_local_data_complete(adapter, NULL, NULL, NULL, NULL, NULL);
+
+       g_dbus_unregister_interface(connection, adapter_get_path(adapter),
+                                                       OOB_INTERFACE);
+}
+
+static struct btd_adapter_driver oob_driver = {
+       .name   = "oob",
+       .probe  = oob_probe,
+       .remove = oob_remove,
+};
+
+static int dbusoob_init(void)
+{
+       DBG("Setup dbusoob plugin");
+
+       connection = btd_get_dbus_connection();
+
+       return btd_register_adapter_driver(&oob_driver);
+}
+
+static void dbusoob_exit(void)
+{
+       DBG("Cleanup dbusoob plugin");
+
+       btd_unregister_adapter_driver(&oob_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(dbusoob, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                                               dbusoob_init, dbusoob_exit)
+#endif
index cabcf34..51e276e 100755 (executable)
@@ -259,9 +259,16 @@ static DBusMessage *create_request_oob_reply(struct btd_adapter *adapter,
        return reply;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void read_local_complete(struct btd_adapter *adapter,
+                       const uint8_t *hash, const uint8_t *randomizer,
+                       const uint8_t *hash256, const uint8_t *randomizer256,
+                       void *user_data)
+#else
 static void read_local_complete(struct btd_adapter *adapter,
                                const uint8_t *hash, const uint8_t *randomizer,
                                void *user_data)
+#endif
 {
        DBusMessage *msg = user_data;
        DBusMessage *reply;
index c9a7c84..9fdee00 100755 (executable)
 #include "src/profile.h"
 #include "src/hcid.h"
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define CONTROL_CONNECT_TIMEOUT 4
+#define TARGET_CONNECT_TIMEOUT 1
+#else
 #define CONTROL_CONNECT_TIMEOUT 2
+#endif
 #define SOURCE_RETRY_TIMEOUT 2
 #define SINK_RETRY_TIMEOUT SOURCE_RETRY_TIMEOUT
 #define CT_RETRY_TIMEOUT 1
 #define TG_RETRY_TIMEOUT CT_RETRY_TIMEOUT
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define SOURCE_RETRIES 0
+#else
 #define SOURCE_RETRIES 1
+#endif
 #define SINK_RETRIES SOURCE_RETRIES
 #define CT_RETRIES 1
 #define TG_RETRIES CT_RETRIES
@@ -287,7 +296,15 @@ static void sink_cb(struct btd_service *service, btd_service_state_t old_state,
                 * immediatelly otherwise set timer
                 */
                if (old_state == BTD_SERVICE_STATE_CONNECTING)
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                       /* Set timer as most of the devices initiate
+                        * avrcp connection immediately; irrespective of local
+                        * or remote initiated a2dp connection
+                        */
+                       policy_set_ct_timer(data, CONTROL_CONNECT_TIMEOUT);
+#else
                        policy_connect(data, controller);
+#endif
                else if (btd_service_get_state(controller) !=
                                                BTD_SERVICE_STATE_CONNECTED)
                        policy_set_ct_timer(data, CONTROL_CONNECT_TIMEOUT);
@@ -317,8 +334,15 @@ static void policy_set_tg_timer(struct policy_data *data, int timeout)
        if (data->tg_timer > 0)
                g_source_remove(data->tg_timer);
 
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       data->tg_timer = g_timeout_add_seconds(TARGET_CONNECT_TIMEOUT,
+                                                       policy_connect_tg,
+                                                       data);
+#else
        data->tg_timer = g_timeout_add_seconds(timeout, policy_connect_tg,
                                                        data);
+#endif
 }
 
 static gboolean policy_connect_source(gpointer user_data)
@@ -386,9 +410,13 @@ static void source_cb(struct btd_service *service,
                if (data->tg_timer > 0) {
                        g_source_remove(data->tg_timer);
                        data->tg_timer = 0;
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined BT_QUALIFICATION
+               }
+#else
                } else if (btd_service_get_state(target) !=
                                                BTD_SERVICE_STATE_DISCONNECTED)
                        policy_disconnect(data, target);
+#endif
                break;
        case BTD_SERVICE_STATE_CONNECTING:
                break;
diff --git a/profile.h b/profile.h
new file mode 100755 (executable)
index 0000000..697303d
--- /dev/null
+++ b/profile.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ *
+ */
+
+#ifndef __TIZEN_PROFILE_H__
+#define __TIZEN_PROFILE_H__
+
+#include <stdlib.h>
+
+#include <iniparser.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#define MODEL_CONFIG_FILE "/etc/config/model-config.xml"
+#define TYPE_FIELD "string"
+#define FEATURE_TAG "platform"
+#define MODEL_CONFIG_TAG "model-config"
+
+typedef enum {
+       TIZEN_PROFILE_UNKNOWN = 0,
+       TIZEN_PROFILE_MOBILE = 0x1,
+       TIZEN_PROFILE_WEARABLE = 0x2,
+       TIZEN_PROFILE_TV = 0x4,
+       TIZEN_PROFILE_IVI = 0x8,
+       TIZEN_PROFILE_COMMON = 0x10,
+} tizen_profile_t;
+
+static tizen_profile_t profile = TIZEN_PROFILE_UNKNOWN;
+
+static inline int __get_profile_from_model_config_xml(const char *field, char **value)
+{
+       char *node_name = NULL;
+       char *node_value = NULL;
+       xmlNode *cur_node = NULL;
+       xmlNodePtr cur_ptr = NULL;
+       xmlNodePtr model_ptr = NULL;
+       xmlDocPtr xml_doc = NULL;
+
+       xml_doc = xmlParseFile(MODEL_CONFIG_FILE);
+       if (xml_doc == NULL)
+               return -1;
+
+       cur_ptr = xmlDocGetRootElement(xml_doc);
+       if (cur_ptr == NULL) {
+               xmlFreeDoc(xml_doc);
+               return -1;
+       }
+
+       for (cur_node = cur_ptr; cur_node; cur_node = cur_node->next) {
+               if (!xmlStrcmp(cur_ptr->name, (const xmlChar*)MODEL_CONFIG_TAG))
+                       break;
+       }
+
+       if (cur_ptr == NULL) {
+               xmlFreeDoc(xml_doc);
+               return -1;
+       }
+
+       cur_ptr = cur_ptr->xmlChildrenNode;
+       for (cur_node = cur_ptr; cur_node; cur_node = cur_node->next) {
+               if (!xmlStrcmp(cur_node->name, (const xmlChar*)FEATURE_TAG)) {
+                       model_ptr = cur_node;
+                       break;
+               }
+       }
+
+       if (model_ptr == NULL) {
+               xmlFreeDoc(xml_doc);
+               return -1;
+       }
+
+       if (model_ptr) {
+               cur_ptr = model_ptr->xmlChildrenNode;
+
+               for (cur_node = cur_ptr; cur_node; cur_node = cur_node->next) {
+                       if (cur_node->type == XML_ELEMENT_NODE) {
+                               node_name = (char *)xmlGetProp(cur_node, (const xmlChar*)"name");
+
+                               if (!strncmp(node_name, field, strlen(node_name))) {
+                                       node_value = (char *)xmlNodeListGetString(xml_doc, cur_node->xmlChildrenNode, 1);
+                                       if (node_value) {
+                                               *value = strdup(node_value);
+                                               free(node_name);
+                                               free(node_value);
+                                               break;
+                                       }
+                               }
+                               free(node_name);
+                       }
+               }
+       }
+
+       xmlFreeDoc(xml_doc);
+       return 0;
+}
+
+static inline tizen_profile_t _get_tizen_profile(void)
+{
+       char *profile_name = NULL;
+
+       if (__builtin_expect(profile != TIZEN_PROFILE_UNKNOWN, 1))
+               return profile;
+
+       if (__get_profile_from_model_config_xml("tizen.org/feature/profile",
+                                                                       &profile_name) < 0) {
+               profile = TIZEN_PROFILE_MOBILE;
+               return profile;
+       }
+
+       if (profile_name == NULL) {
+               profile = TIZEN_PROFILE_MOBILE;
+               return profile;
+       }
+
+       switch (*profile_name) {
+               case 'm':
+               case 'M':
+                       profile = TIZEN_PROFILE_MOBILE;
+                       break;
+               case 'w':
+               case 'W':
+                       profile = TIZEN_PROFILE_WEARABLE;
+                       break;
+               case 't':
+               case 'T':
+                       profile = TIZEN_PROFILE_TV;
+                       break;
+               case 'i':
+               case 'I':
+                       profile = TIZEN_PROFILE_IVI;
+                       break;
+               default: /* common or unknown ==> ALL ARE COMMON */
+                       profile = TIZEN_PROFILE_COMMON;
+       }
+       free(profile_name);
+
+       return profile;
+}
+
+#define TIZEN_FEATURE_BLUEZ_BRCM_CHIP ((_get_tizen_profile()) == TIZEN_PROFILE_IVI)
+#define TIZEN_FEATURE_BLUEZ_SMS_ONLY ((_get_tizen_profile()) == TIZEN_PROFILE_WEARABLE)
+#define TIZEN_FEATURE_BLUEZ_BRCM_QOS ((_get_tizen_profile()) == TIZEN_PROFILE_WEARABLE)
+#define TIZEN_FEATURE_BLUEZ_ROLE_CHANGE ((_get_tizen_profile()) == TIZEN_PROFILE_WEARABLE)
+#define TIZEN_FEATURE_BLUEZ_CONFIRM_ONLY ((_get_tizen_profile()) == TIZEN_PROFILE_WEARABLE)
+
+
+#endif /* __TIZEN_PROFILE_H__ */
+
diff --git a/profiles/alert/server.c b/profiles/alert/server.c
new file mode 100755 (executable)
index 0000000..2f6e3cd
--- /dev/null
@@ -0,0 +1,1044 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdbool.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "src/dbus-common.h"
+#include "attrib/att.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "attrib/att-database.h"
+#include "src/log.h"
+#include "attrib/gatt-service.h"
+#include "attrib/gattrib.h"
+#include "src/attrib-server.h"
+#include "attrib/gatt.h"
+#include "src/profile.h"
+#include "src/error.h"
+#include "src/textfile.h"
+#include "src/attio.h"
+
+#define PHONE_ALERT_STATUS_SVC_UUID    0x180E
+#define ALERT_NOTIF_SVC_UUID           0x1811
+
+#define ALERT_STATUS_CHR_UUID          0x2A3F
+#define RINGER_CP_CHR_UUID             0x2A40
+#define RINGER_SETTING_CHR_UUID                0x2A41
+
+#define ALERT_NOTIF_CP_CHR_UUID                0x2A44
+#define UNREAD_ALERT_CHR_UUID          0x2A45
+#define NEW_ALERT_CHR_UUID             0x2A46
+#define SUPP_NEW_ALERT_CAT_CHR_UUID    0x2A47
+#define SUPP_UNREAD_ALERT_CAT_CHR_UUID 0x2A48
+
+#define ALERT_OBJECT_PATH              "/org/bluez"
+#define ALERT_INTERFACE                        "org.bluez.Alert1"
+#define ALERT_AGENT_INTERFACE          "org.bluez.AlertAgent1"
+
+/* Maximum length for "Text String Information" */
+#define NEW_ALERT_MAX_INFO_SIZE                18
+/* Maximum length for New Alert Characteristic Value */
+#define NEW_ALERT_CHR_MAX_VALUE_SIZE   (NEW_ALERT_MAX_INFO_SIZE + 2)
+
+enum {
+       ENABLE_NEW_INCOMING,
+       ENABLE_UNREAD_CAT,
+       DISABLE_NEW_INCOMING,
+       DISABLE_UNREAD_CAT,
+       NOTIFY_NEW_INCOMING,
+       NOTIFY_UNREAD_CAT,
+};
+
+enum {
+       RINGER_SILENT_MODE = 1,
+       RINGER_MUTE_ONCE,
+       RINGER_CANCEL_SILENT_MODE,
+};
+
+/* Ringer Setting characteristic values */
+enum {
+       RINGER_SILENT,
+       RINGER_NORMAL,
+};
+
+enum notify_type {
+       NOTIFY_RINGER_SETTING = 0,
+       NOTIFY_ALERT_STATUS,
+       NOTIFY_NEW_ALERT,
+       NOTIFY_UNREAD_ALERT,
+       NOTIFY_SIZE,
+};
+
+struct alert_data {
+       const char *category;
+       char *srv;
+       char *path;
+       guint watcher;
+};
+
+struct alert_adapter {
+       struct btd_adapter *adapter;
+       uint16_t supp_new_alert_cat_handle;
+       uint16_t supp_unread_alert_cat_handle;
+       uint16_t hnd_ccc[NOTIFY_SIZE];
+       uint16_t hnd_value[NOTIFY_SIZE];
+};
+
+struct notify_data {
+       struct alert_adapter *al_adapter;
+       enum notify_type type;
+       uint8_t *value;
+       size_t len;
+};
+
+struct notify_callback {
+       struct notify_data *notify_data;
+       struct btd_device *device;
+       guint id;
+};
+
+static GSList *registered_alerts = NULL;
+static GSList *alert_adapters = NULL;
+static uint8_t ringer_setting = RINGER_NORMAL;
+static uint8_t alert_status = 0;
+
+static const char * const anp_categories[] = {
+       "simple",
+       "email",
+       "news",
+       "call",
+       "missed-call",
+       "sms-mms",
+       "voice-mail",
+       "schedule",
+       "high-priority",
+       "instant-message",
+};
+
+static const char * const pasp_categories[] = {
+       "ringer",
+       "vibrate",
+       "display",
+};
+
+static int adapter_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct alert_adapter *al_adapter = a;
+       const struct btd_adapter *adapter = b;
+
+       return al_adapter->adapter == adapter ? 0 : -1;
+}
+
+static struct alert_adapter *find_alert_adapter(struct btd_adapter *adapter)
+{
+       GSList *l = g_slist_find_custom(alert_adapters, adapter, adapter_cmp);
+
+       return l ? l->data : NULL;
+}
+
+static void alert_data_destroy(gpointer user_data)
+{
+       struct alert_data *alert = user_data;
+
+       if (alert->watcher)
+               g_dbus_remove_watch(btd_get_dbus_connection(), alert->watcher);
+
+       g_free(alert->srv);
+       g_free(alert->path);
+       g_free(alert);
+}
+
+static void alert_release(gpointer user_data)
+{
+       struct alert_data *alert = user_data;
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(alert->srv, alert->path,
+                                                       ALERT_AGENT_INTERFACE,
+                                                       "Release");
+       if (msg)
+               g_dbus_send_message(btd_get_dbus_connection(), msg);
+
+       alert_data_destroy(alert);
+}
+
+static void alert_destroy(gpointer user_data)
+{
+       DBG("");
+
+       g_slist_free_full(registered_alerts, alert_release);
+       registered_alerts = NULL;
+}
+
+static const char *valid_category(const char *category)
+{
+       unsigned i;
+
+       for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
+               if (g_str_equal(anp_categories[i], category))
+                       return anp_categories[i];
+       }
+
+       for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++) {
+               if (g_str_equal(pasp_categories[i], category))
+                       return pasp_categories[i];
+       }
+
+       return NULL;
+}
+
+static struct alert_data *get_alert_data_by_category(const char *category)
+{
+       GSList *l;
+       struct alert_data *alert;
+
+       for (l = registered_alerts; l; l = g_slist_next(l)) {
+               alert = l->data;
+               if (g_str_equal(alert->category, category))
+                       return alert;
+       }
+
+       return NULL;
+}
+
+static gboolean registered_category(const char *category)
+{
+       struct alert_data *alert;
+
+       alert = get_alert_data_by_category(category);
+       if (alert)
+               return TRUE;
+
+       return FALSE;
+}
+
+static gboolean pasp_category(const char *category)
+{
+       unsigned i;
+
+       for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++)
+               if (g_str_equal(category, pasp_categories[i]))
+                       return TRUE;
+
+       return FALSE;
+}
+
+static gboolean valid_description(const char *category,
+                                               const char *description)
+{
+       if (!pasp_category(category)) {
+               if (strlen(description) >= NEW_ALERT_MAX_INFO_SIZE)
+                       return FALSE;
+
+               return TRUE;
+       }
+
+       if (g_str_equal(description, "active") ||
+                                       g_str_equal(description, "not active"))
+               return TRUE;
+
+       if (g_str_equal(category, "ringer"))
+               if (g_str_equal(description, "enabled") ||
+                                       g_str_equal(description, "disabled"))
+                       return TRUE;
+
+       return FALSE;
+}
+
+static gboolean valid_count(const char *category, uint16_t count)
+{
+       if (!pasp_category(category) && count > 0 && count <= 255)
+               return TRUE;
+
+       if (pasp_category(category) && count == 1)
+               return TRUE;
+
+       return FALSE;
+}
+
+static void update_supported_categories(gpointer data, gpointer user_data)
+{
+       struct alert_adapter *al_adapter = data;
+       struct btd_adapter *adapter = al_adapter->adapter;
+       uint8_t value[2];
+       unsigned int i;
+
+       memset(value, 0, sizeof(value));
+
+       for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
+               if (registered_category(anp_categories[i]))
+                       hci_set_bit(i, value);
+       }
+
+       attrib_db_update(adapter, al_adapter->supp_new_alert_cat_handle, NULL,
+                                               value, sizeof(value), NULL);
+
+       /* FIXME: For now report all registered categories as supporting unread
+        * status, until it is known which ones should be supported */
+       attrib_db_update(adapter, al_adapter->supp_unread_alert_cat_handle,
+                                       NULL, value, sizeof(value), NULL);
+}
+
+static void watcher_disconnect(DBusConnection *conn, void *user_data)
+{
+       struct alert_data *alert = user_data;
+
+       DBG("Category %s was disconnected", alert->category);
+
+       registered_alerts = g_slist_remove(registered_alerts, alert);
+       alert_data_destroy(alert);
+
+       g_slist_foreach(alert_adapters, update_supported_categories, NULL);
+}
+
+static gboolean is_notifiable_device(struct btd_device *device, uint16_t ccc)
+{
+       char *filename;
+       GKeyFile *key_file;
+       char handle[6];
+       char *str;
+       uint16_t val;
+       gboolean result;
+
+       sprintf(handle, "%hu", ccc);
+
+       filename = btd_device_get_storage_path(device, "ccc");
+       if (!filename) {
+               warn("Unable to get ccc storage path for device");
+               return FALSE;
+       }
+
+       key_file = g_key_file_new();
+       g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+       str = g_key_file_get_string(key_file, handle, "Value", NULL);
+       if (!str) {
+               result = FALSE;
+               goto end;
+       }
+
+       val = strtol(str, NULL, 16);
+       if (!(val & 0x0001)) {
+               result = FALSE;
+               goto end;
+       }
+
+       result = TRUE;
+end:
+       g_free(str);
+       g_free(filename);
+       g_key_file_free(key_file);
+
+       return result;
+}
+
+static void destroy_notify_callback(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct notify_callback *cb = user_data;
+
+       DBG("status=%#x", status);
+
+       btd_device_remove_attio_callback(cb->device, cb->id);
+       btd_device_unref(cb->device);
+       g_free(cb->notify_data->value);
+       g_free(cb->notify_data);
+       g_free(cb);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+       struct notify_callback *cb = user_data;
+       struct notify_data *nd = cb->notify_data;
+       enum notify_type type = nd->type;
+       struct alert_adapter *al_adapter = nd->al_adapter;
+       size_t len;
+       uint8_t *pdu = g_attrib_get_buffer(attrib, &len);
+
+
+       switch (type) {
+       case NOTIFY_RINGER_SETTING:
+               len = enc_notification(al_adapter->hnd_value[type],
+                               &ringer_setting, sizeof(ringer_setting),
+                               pdu, len);
+               break;
+       case NOTIFY_ALERT_STATUS:
+               len = enc_notification(al_adapter->hnd_value[type],
+                               &alert_status, sizeof(alert_status),
+                               pdu, len);
+               break;
+       case NOTIFY_NEW_ALERT:
+       case NOTIFY_UNREAD_ALERT:
+               len = enc_notification(al_adapter->hnd_value[type],
+                                       nd->value, nd->len, pdu, len);
+               break;
+       case NOTIFY_SIZE:
+       default:
+               DBG("Unknown type, could not send notification");
+               goto end;
+       }
+
+       DBG("Send notification for handle: 0x%04x, ccc: 0x%04x",
+                                       al_adapter->hnd_value[type],
+                                       al_adapter->hnd_ccc[type]);
+
+       g_attrib_send(attrib, 0, pdu, len, destroy_notify_callback, cb, NULL);
+
+       return;
+
+end:
+       btd_device_remove_attio_callback(cb->device, cb->id);
+       btd_device_unref(cb->device);
+       g_free(cb->notify_data->value);
+       g_free(cb->notify_data);
+       g_free(cb);
+}
+
+static void filter_devices_notify(struct btd_device *device, void *user_data)
+{
+       struct notify_data *notify_data = user_data;
+       struct alert_adapter *al_adapter = notify_data->al_adapter;
+       enum notify_type type = notify_data->type;
+       struct notify_callback *cb;
+
+       if (!is_notifiable_device(device, al_adapter->hnd_ccc[type]))
+               return;
+
+       cb = g_new0(struct notify_callback, 1);
+       cb->notify_data = notify_data;
+       cb->device = btd_device_ref(device);
+       cb->id = btd_device_add_attio_callback(device,
+                                               attio_connected_cb, NULL, cb);
+}
+
+static void notify_devices(struct alert_adapter *al_adapter,
+                       enum notify_type type, uint8_t *value, size_t len)
+{
+       struct notify_data *notify_data;
+
+       notify_data = g_new0(struct notify_data, 1);
+       notify_data->al_adapter = al_adapter;
+       notify_data->type = type;
+       notify_data->value = g_memdup(value, len);
+       notify_data->len = len;
+
+       btd_adapter_for_each_device(al_adapter->adapter, filter_devices_notify,
+                                       notify_data);
+}
+
+static void pasp_notification(enum notify_type type)
+{
+       GSList *it;
+       struct alert_adapter *al_adapter;
+
+       for (it = alert_adapters; it; it = g_slist_next(it)) {
+               al_adapter = it->data;
+
+               notify_devices(al_adapter, type, NULL, 0);
+       }
+}
+
+static DBusMessage *register_alert(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       const char *sender = dbus_message_get_sender(msg);
+       char *path;
+       const char *category;
+       const char *c;
+       struct alert_data *alert;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &c,
+                       DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       category = valid_category(c);
+       if (!category) {
+               DBG("Invalid category: %s", c);
+               return btd_error_invalid_args(msg);
+       }
+
+       if (registered_category(category)) {
+               DBG("Category %s already registered", category);
+               return dbus_message_new_method_return(msg);
+       }
+
+       alert = g_new0(struct alert_data, 1);
+       alert->srv = g_strdup(sender);
+       alert->path = g_strdup(path);
+       alert->category = category;
+       alert->watcher = g_dbus_add_disconnect_watch(conn, alert->srv,
+                                       watcher_disconnect, alert, NULL);
+
+       if (alert->watcher == 0) {
+               alert_data_destroy(alert);
+               DBG("Could not register disconnect watcher");
+               return btd_error_failed(msg,
+                               "Could not register disconnect watcher");
+       }
+
+       registered_alerts = g_slist_append(registered_alerts, alert);
+
+       g_slist_foreach(alert_adapters, update_supported_categories, NULL);
+
+       DBG("RegisterAlert(\"%s\", \"%s\")", alert->category, alert->path);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static void update_new_alert(gpointer data, gpointer user_data)
+{
+       struct alert_adapter *al_adapter = data;
+       struct btd_adapter *adapter = al_adapter->adapter;
+       uint8_t *value = user_data;
+
+       attrib_db_update(adapter, al_adapter->hnd_value[NOTIFY_NEW_ALERT], NULL,
+                                               &value[1], value[0], NULL);
+
+       notify_devices(al_adapter, NOTIFY_NEW_ALERT, &value[1], value[0]);
+}
+
+static void update_phone_alerts(const char *category, const char *description)
+{
+       unsigned int i;
+
+       if (g_str_equal(category, "ringer")) {
+               if (g_str_equal(description, "enabled")) {
+                       ringer_setting = RINGER_NORMAL;
+                       pasp_notification(NOTIFY_RINGER_SETTING);
+                       return;
+               } else if (g_str_equal(description, "disabled")) {
+                       ringer_setting = RINGER_SILENT;
+                       pasp_notification(NOTIFY_RINGER_SETTING);
+                       return;
+               }
+       }
+
+       for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++) {
+               if (g_str_equal(pasp_categories[i], category)) {
+                       if (g_str_equal(description, "active")) {
+                               alert_status |= (1 << i);
+                               pasp_notification(NOTIFY_ALERT_STATUS);
+                       } else if (g_str_equal(description, "not active")) {
+                               alert_status &= ~(1 << i);
+                               pasp_notification(NOTIFY_ALERT_STATUS);
+                       }
+                       break;
+               }
+       }
+}
+
+static DBusMessage *new_alert(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       const char *sender = dbus_message_get_sender(msg);
+       const char *category, *description;
+       struct alert_data *alert;
+       uint16_t count;
+       unsigned int i;
+       size_t dlen;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &category,
+                       DBUS_TYPE_UINT16, &count, DBUS_TYPE_STRING,
+                       &description, DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       alert = get_alert_data_by_category(category);
+       if (!alert) {
+               DBG("Category %s not registered", category);
+               return btd_error_invalid_args(msg);
+       }
+
+       if (!g_str_equal(alert->srv, sender)) {
+               DBG("Sender %s is not registered in category %s", sender,
+                                                               category);
+               return btd_error_invalid_args(msg);
+       }
+
+       if (!valid_description(category, description)) {
+               DBG("Description %s is invalid for %s category",
+                                                       description, category);
+               return btd_error_invalid_args(msg);
+       }
+
+       if (!valid_count(category, count)) {
+               DBG("Count %d is invalid for %s category", count, category);
+               return btd_error_invalid_args(msg);
+       }
+
+       dlen = strlen(description);
+
+       for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
+               uint8_t value[NEW_ALERT_CHR_MAX_VALUE_SIZE + 1];
+               uint8_t *ptr = value;
+
+               if (!g_str_equal(anp_categories[i], category))
+                       continue;
+
+               memset(value, 0, sizeof(value));
+
+               *ptr++ = 2; /* Attribute value size */
+               *ptr++ = i; /* Category ID (mandatory) */
+               *ptr++ = count; /* Number of New Alert (mandatory) */
+               /* Text String Information (optional) */
+               strncpy((char *) ptr, description,
+                                               NEW_ALERT_MAX_INFO_SIZE - 1);
+
+               if (dlen > 0)
+                       *value += dlen + 1;
+
+               g_slist_foreach(alert_adapters, update_new_alert, value);
+       }
+
+       if (pasp_category(category))
+               update_phone_alerts(category, description);
+
+       DBG("NewAlert(\"%s\", %d, \"%s\")", category, count, description);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static int agent_ringer_mute_once(void)
+{
+       struct alert_data *alert;
+       DBusMessage *msg;
+
+       alert = get_alert_data_by_category("ringer");
+       if (!alert) {
+               DBG("Category ringer is not registered");
+               return -EINVAL;
+       }
+
+       msg = dbus_message_new_method_call(alert->srv, alert->path,
+                                       ALERT_AGENT_INTERFACE, "MuteOnce");
+       if (!msg)
+               return -ENOMEM;
+
+       dbus_message_set_no_reply(msg, TRUE);
+       g_dbus_send_message(btd_get_dbus_connection(), msg);
+
+       return 0;
+}
+
+static int agent_ringer_set_ringer(const char *mode)
+{
+       struct alert_data *alert;
+       DBusMessage *msg;
+
+       alert = get_alert_data_by_category("ringer");
+       if (!alert) {
+               DBG("Category ringer is not registered");
+               return -EINVAL;
+       }
+
+       msg = dbus_message_new_method_call(alert->srv, alert->path,
+                                       ALERT_AGENT_INTERFACE, "SetRinger");
+       if (!msg)
+               return -ENOMEM;
+
+       dbus_message_append_args(msg, DBUS_TYPE_STRING, &mode,
+                                                       DBUS_TYPE_INVALID);
+
+       dbus_message_set_no_reply(msg, TRUE);
+       g_dbus_send_message(btd_get_dbus_connection(), msg);
+
+       return 0;
+}
+
+static void update_unread_alert(gpointer data, gpointer user_data)
+{
+       struct alert_adapter *al_adapter = data;
+       struct btd_adapter *adapter = al_adapter->adapter;
+       uint8_t *value = user_data;
+
+       attrib_db_update(adapter,
+                       al_adapter->hnd_value[NOTIFY_UNREAD_ALERT], NULL, value,
+                       2, NULL);
+
+       notify_devices(al_adapter, NOTIFY_UNREAD_ALERT, value, 2);
+}
+
+static DBusMessage *unread_alert(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       const char *sender = dbus_message_get_sender(msg);
+       struct alert_data *alert;
+       const char *category;
+       unsigned int i;
+       uint16_t count;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &category,
+                                               DBUS_TYPE_UINT16, &count,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       alert = get_alert_data_by_category(category);
+       if (!alert) {
+               DBG("Category %s not registered", category);
+               return btd_error_invalid_args(msg);
+       }
+
+       if (!valid_count(category, count)) {
+               DBG("Count %d is invalid for %s category", count, category);
+               return btd_error_invalid_args(msg);
+       }
+
+       if (!g_str_equal(alert->srv, sender)) {
+               DBG("Sender %s is not registered in category %s", sender,
+                                                               category);
+               return btd_error_invalid_args(msg);
+       }
+
+       for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
+               if (g_str_equal(anp_categories[i], category)) {
+                       uint8_t value[2];
+
+                       value[0] = i; /* Category ID */
+                       value[1] = count; /* Unread count */
+
+                       g_slist_foreach(alert_adapters, update_unread_alert,
+                                                                       value);
+               }
+       }
+
+       DBG("category %s, count %d", category, count);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static uint8_t ringer_cp_write(struct attribute *a,
+                                               struct btd_device *device,
+                                               gpointer user_data)
+{
+       DBG("a = %p", a);
+
+       if (a->len > 1) {
+               DBG("Invalid command size (%zu)", a->len);
+               return 0;
+       }
+
+       switch (a->data[0]) {
+       case RINGER_SILENT_MODE:
+               DBG("Silent Mode");
+               agent_ringer_set_ringer("disabled");
+               break;
+       case RINGER_MUTE_ONCE:
+               DBG("Mute Once");
+               agent_ringer_mute_once();
+               break;
+       case RINGER_CANCEL_SILENT_MODE:
+               DBG("Cancel Silent Mode");
+               agent_ringer_set_ringer("enabled");
+               break;
+       default:
+               DBG("Invalid command (0x%02x)", a->data[0]);
+       }
+
+       return 0;
+}
+
+static uint8_t alert_status_read(struct attribute *a,
+                                               struct btd_device *device,
+                                               gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       DBG("a = %p", a);
+
+       if (a->data == NULL || a->data[0] != alert_status)
+               attrib_db_update(adapter, a->handle, NULL, &alert_status,
+                                               sizeof(alert_status), NULL);
+
+       return 0;
+}
+
+static uint8_t ringer_setting_read(struct attribute *a,
+                                               struct btd_device *device,
+                                               gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       DBG("a = %p", a);
+
+       if (a->data == NULL || a->data[0] != ringer_setting)
+               attrib_db_update(adapter, a->handle, NULL, &ringer_setting,
+                                               sizeof(ringer_setting), NULL);
+
+       return 0;
+}
+
+static void register_phone_alert_service(struct alert_adapter *al_adapter)
+{
+       bt_uuid_t uuid;
+
+       bt_uuid16_create(&uuid, PHONE_ALERT_STATUS_SVC_UUID);
+
+       /* Phone Alert Status Service */
+       gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid,
+                       /* Alert Status characteristic */
+                       GATT_OPT_CHR_UUID16, ALERT_STATUS_CHR_UUID,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
+                                                       GATT_CHR_PROP_NOTIFY,
+                       GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+                       alert_status_read, al_adapter->adapter,
+                       GATT_OPT_CCC_GET_HANDLE,
+                       &al_adapter->hnd_ccc[NOTIFY_ALERT_STATUS],
+                       GATT_OPT_CHR_VALUE_GET_HANDLE,
+                       &al_adapter->hnd_value[NOTIFY_ALERT_STATUS],
+                       /* Ringer Control Point characteristic */
+                       GATT_OPT_CHR_UUID16, RINGER_CP_CHR_UUID,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE_WITHOUT_RESP,
+                       GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+                       ringer_cp_write, NULL,
+                       /* Ringer Setting characteristic */
+                       GATT_OPT_CHR_UUID16, RINGER_SETTING_CHR_UUID,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
+                                                       GATT_CHR_PROP_NOTIFY,
+                       GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+                       ringer_setting_read, al_adapter->adapter,
+                       GATT_OPT_CCC_GET_HANDLE,
+                       &al_adapter->hnd_ccc[NOTIFY_RINGER_SETTING],
+                       GATT_OPT_CHR_VALUE_GET_HANDLE,
+                       &al_adapter->hnd_value[NOTIFY_RINGER_SETTING],
+                       GATT_OPT_INVALID);
+}
+
+static uint8_t supp_new_alert_cat_read(struct attribute *a,
+                                               struct btd_device *device,
+                                               gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       uint8_t value[] = { 0x00, 0x00 };
+
+       DBG("a = %p", a);
+
+       if (a->data == NULL)
+               attrib_db_update(adapter, a->handle, NULL, value, sizeof(value),
+                                                                       NULL);
+
+       return 0;
+}
+
+static uint8_t supp_unread_alert_cat_read(struct attribute *a,
+                                               struct btd_device *device,
+                                               gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       uint8_t value[] = { 0x00, 0x00 };
+
+       DBG("a = %p", a);
+
+       if (a->data == NULL)
+               attrib_db_update(adapter, a->handle, NULL, value, sizeof(value),
+                                                                       NULL);
+
+       return 0;
+}
+
+static uint8_t alert_notif_cp_write(struct attribute *a,
+                                               struct btd_device *device,
+                                               gpointer user_data)
+{
+       DBG("a = %p", a);
+
+       if (a->len < 2)
+               return 0;
+
+       switch (a->data[0]) {
+       case ENABLE_NEW_INCOMING:
+               DBG("ENABLE_NEW_INCOMING: 0x%02x", a->data[1]);
+               break;
+       case ENABLE_UNREAD_CAT:
+               DBG("ENABLE_UNREAD_CAT: 0x%02x", a->data[1]);
+               break;
+       case DISABLE_NEW_INCOMING:
+               DBG("DISABLE_NEW_INCOMING: 0x%02x", a->data[1]);
+               break;
+       case DISABLE_UNREAD_CAT:
+               DBG("DISABLE_UNREAD_CAT: 0x%02x", a->data[1]);
+               break;
+       case NOTIFY_NEW_INCOMING:
+               DBG("NOTIFY_NEW_INCOMING: 0x%02x", a->data[1]);
+               break;
+       case NOTIFY_UNREAD_CAT:
+               DBG("NOTIFY_UNREAD_CAT: 0x%02x", a->data[1]);
+               break;
+       default:
+               DBG("0x%02x 0x%02x", a->data[0], a->data[1]);
+       }
+
+       return 0;
+}
+
+static void register_alert_notif_service(struct alert_adapter *al_adapter)
+{
+       bt_uuid_t uuid;
+
+       bt_uuid16_create(&uuid, ALERT_NOTIF_SVC_UUID);
+
+       /* Alert Notification Service */
+       gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid,
+                       /* Supported New Alert Category */
+                       GATT_OPT_CHR_UUID16, SUPP_NEW_ALERT_CAT_CHR_UUID,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
+                       GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+                       supp_new_alert_cat_read, al_adapter->adapter,
+                       GATT_OPT_CHR_VALUE_GET_HANDLE,
+                       &al_adapter->supp_new_alert_cat_handle,
+                       /* New Alert */
+                       GATT_OPT_CHR_UUID16, NEW_ALERT_CHR_UUID,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY,
+                       GATT_OPT_CCC_GET_HANDLE,
+                       &al_adapter->hnd_ccc[NOTIFY_NEW_ALERT],
+                       GATT_OPT_CHR_VALUE_GET_HANDLE,
+                       &al_adapter->hnd_value[NOTIFY_NEW_ALERT],
+                       /* Supported Unread Alert Category */
+                       GATT_OPT_CHR_UUID16, SUPP_UNREAD_ALERT_CAT_CHR_UUID,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
+                       GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+                       supp_unread_alert_cat_read, al_adapter->adapter,
+                       GATT_OPT_CHR_VALUE_GET_HANDLE,
+                       &al_adapter->supp_unread_alert_cat_handle,
+                       /* Unread Alert Status */
+                       GATT_OPT_CHR_UUID16, UNREAD_ALERT_CHR_UUID,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY,
+                       GATT_OPT_CCC_GET_HANDLE,
+                       &al_adapter->hnd_ccc[NOTIFY_UNREAD_ALERT],
+                       GATT_OPT_CHR_VALUE_GET_HANDLE,
+                       &al_adapter->hnd_value[NOTIFY_UNREAD_ALERT],
+                       /* Alert Notification Control Point */
+                       GATT_OPT_CHR_UUID16, ALERT_NOTIF_CP_CHR_UUID,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE,
+                       GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+                       alert_notif_cp_write, NULL,
+                       GATT_OPT_INVALID);
+}
+
+static int alert_server_probe(struct btd_profile *p,
+                                               struct btd_adapter *adapter)
+{
+       struct alert_adapter *al_adapter;
+
+       al_adapter = g_new0(struct alert_adapter, 1);
+       al_adapter->adapter = btd_adapter_ref(adapter);
+
+       alert_adapters = g_slist_append(alert_adapters, al_adapter);
+
+       register_phone_alert_service(al_adapter);
+       register_alert_notif_service(al_adapter);
+
+       return 0;
+}
+
+static void alert_server_remove(struct btd_profile *p,
+                                               struct btd_adapter *adapter)
+{
+       struct alert_adapter *al_adapter;
+
+       al_adapter = find_alert_adapter(adapter);
+       if (!al_adapter)
+               return;
+
+       alert_adapters = g_slist_remove(alert_adapters, al_adapter);
+       btd_adapter_unref(al_adapter->adapter);
+
+       g_free(al_adapter);
+}
+
+static struct btd_profile alert_profile = {
+       .name = "gatt-alert-server",
+       .adapter_probe = alert_server_probe,
+       .adapter_remove = alert_server_remove,
+};
+
+static const GDBusMethodTable alert_methods[] = {
+       { GDBUS_METHOD("RegisterAlert",
+                       GDBUS_ARGS({ "category", "s" },
+                                  { "agent", "o" }), NULL,
+                       register_alert) },
+       { GDBUS_METHOD("NewAlert",
+                       GDBUS_ARGS({ "category", "s" },
+                                  { "count", "q" },
+                                  { "description", "s" }), NULL,
+                       new_alert) },
+       { GDBUS_METHOD("UnreadAlert",
+                       GDBUS_ARGS({ "category", "s" }, { "count", "q" }), NULL,
+                       unread_alert) },
+       { }
+};
+
+static int alert_server_init(void)
+{
+       if (!g_dbus_register_interface(btd_get_dbus_connection(),
+                                       ALERT_OBJECT_PATH, ALERT_INTERFACE,
+                                       alert_methods, NULL, NULL, NULL,
+                                       alert_destroy)) {
+               error("D-Bus failed to register %s interface",
+                                                       ALERT_INTERFACE);
+               return -EIO;
+       }
+
+       return btd_profile_register(&alert_profile);
+}
+
+static void alert_server_exit(void)
+{
+       btd_profile_unregister(&alert_profile);
+
+       g_dbus_unregister_interface(btd_get_dbus_connection(),
+                                       ALERT_OBJECT_PATH, ALERT_INTERFACE);
+}
+
+static int alert_init(void)
+{
+       return alert_server_init();
+}
+
+static void alert_exit(void)
+{
+       alert_server_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(alert, VERSION,
+                       BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                       alert_init, alert_exit)
index db0736d..5877219 100755 (executable)
@@ -72,6 +72,9 @@ struct a2dp_sep {
        struct avdtp *session;
        struct avdtp_stream *stream;
        guint suspend_timer;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       gboolean remote_suspended;
+#endif
        gboolean delay_reporting;
        gboolean locked;
        gboolean suspending;
@@ -207,11 +210,21 @@ static void finalize_setup_errno(struct a2dp_setup *s, int err,
 {
        GSourceFunc finalize;
        va_list args;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct avdtp_error *avdtp_err;
+#else
        struct avdtp_error avdtp_err;
+#endif
 
        if (err < 0) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               avdtp_err = g_new(struct avdtp_error, 1);
+               avdtp_error_init(avdtp_err, AVDTP_ERRNO, -err);
+               s->err = avdtp_err;
+#else
                avdtp_error_init(&avdtp_err, AVDTP_ERRNO, -err);
                s->err = &avdtp_err;
+#endif
        }
 
        va_start(args, cb1);
@@ -420,6 +433,13 @@ static void stream_state_changed(struct avdtp_stream *stream,
                return;
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (new_state == AVDTP_STATE_STREAMING && sep->suspend_timer) {
+               g_source_remove(sep->suspend_timer);
+               sep->suspend_timer = 0;
+       }
+#endif
+
        if (new_state != AVDTP_STATE_IDLE)
                return;
 
@@ -844,12 +864,25 @@ static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep,
        else
                DBG("Source %p: Start_Ind", sep);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (!a2dp_sep->locked) {
+               a2dp_sep->session = avdtp_ref(session);
+               if(a2dp_sep->remote_suspended == FALSE)
+                       a2dp_sep->suspend_timer = g_timeout_add_seconds(SUSPEND_TIMEOUT,
+                                                       (GSourceFunc) suspend_timeout,
+                                                       a2dp_sep);
+               else
+                       a2dp_sep->remote_suspended = FALSE;
+       }
+#else
+
        if (!a2dp_sep->locked) {
                a2dp_sep->session = avdtp_ref(session);
                a2dp_sep->suspend_timer = g_timeout_add_seconds(SUSPEND_TIMEOUT,
                                                (GSourceFunc) suspend_timeout,
                                                a2dp_sep);
        }
+#endif
 
        if (!a2dp_sep->starting)
                return TRUE;
@@ -903,6 +936,10 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep,
        else
                DBG("Source %p: Suspend_Ind", sep);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       a2dp_sep->remote_suspended = TRUE;
+#endif
+
        if (a2dp_sep->suspend_timer) {
                g_source_remove(a2dp_sep->suspend_timer);
                a2dp_sep->suspend_timer = 0;
@@ -1207,7 +1244,11 @@ static struct avdtp_sep_ind endpoint_ind = {
        .delayreport            = endpoint_delayreport_ind,
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static sdp_record_t *a2dp_record(uint8_t type, gboolean sink_enabled)
+#else
 static sdp_record_t *a2dp_record(uint8_t type)
+#endif
 {
        sdp_list_t *svclass_id, *pfseq, *apseq, *root;
        uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid;
@@ -1216,7 +1257,22 @@ static sdp_record_t *a2dp_record(uint8_t type)
        sdp_record_t *record;
        sdp_data_t *psm, *version, *features;
        uint16_t lp = AVDTP_UUID;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint16_t a2dp_ver, avdtp_ver, feat;
+       if (sink_enabled) {
+               DBG("A2DP record for Sink role");
+               a2dp_ver = 0x0102;
+               avdtp_ver = 0x0103;
+               feat = 0x0002;
+       } else {
+               DBG("A2DP record for Source role");
+               a2dp_ver = 0x0102;
+               avdtp_ver = 0x0103;
+               feat = 0x0001;
+       }
+#else
        uint16_t a2dp_ver = 0x0103, avdtp_ver = 0x0103, feat = 0x000f;
+#endif
 
        record = sdp_record_alloc();
        if (!record)
@@ -1400,6 +1456,13 @@ struct avdtp *a2dp_avdtp_get(struct btd_device *device)
                        return NULL;
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (chan->auth_id) {
+               DBG("auth is already going...");
+               return NULL;
+       }
+#endif
+
        if (chan->session)
                return avdtp_ref(chan->session);
 
@@ -1524,6 +1587,18 @@ static void confirm_cb(GIOChannel *io, gpointer data)
        if (!device)
                goto drop;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+{
+       gboolean restricted = FALSE;
+
+       restricted = device_is_profile_restricted(device, A2DP_SINK_UUID);
+       if (restricted) {
+               DBG("A2DP is restricted");
+               goto drop;
+       }
+}
+#endif
+
        chan = queue_find(server->channels, match_by_device, device);
        if (chan) {
                struct a2dp_setup *setup;
@@ -1564,13 +1639,34 @@ static bool a2dp_server_listen(struct a2dp_server *server)
        if (server->io)
                return true;
 
-       server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err,
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
+       if (btd_adapter_get_a2dp_role(server->adapter) == BLUETOOTH_A2DP_SINK_ROLE) {
+               server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err,
                                BT_IO_OPT_SOURCE_BDADDR,
                                btd_adapter_get_address(server->adapter),
                                BT_IO_OPT_PSM, AVDTP_PSM,
                                BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_IMTU, 895,
                                BT_IO_OPT_MASTER, true,
                                BT_IO_OPT_INVALID);
+       } else {
+               server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR,
+                               btd_adapter_get_address(server->adapter),
+                               BT_IO_OPT_PSM, AVDTP_PSM,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_MASTER, true,
+                               BT_IO_OPT_INVALID);
+       }
+#else
+       server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err,
+                       BT_IO_OPT_SOURCE_BDADDR,
+                       btd_adapter_get_address(server->adapter),
+                       BT_IO_OPT_PSM, AVDTP_PSM,
+                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                       BT_IO_OPT_MASTER, true,
+                       BT_IO_OPT_INVALID);
+#endif
        if (server->io)
                return true;
 
@@ -1697,7 +1793,14 @@ struct a2dp_sep *a2dp_add_sep(struct btd_adapter *adapter, uint8_t type,
        if (*record_id != 0)
                goto add;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (btd_adapter_get_a2dp_role(adapter) == BLUETOOTH_A2DP_SINK_ROLE)
+               record = a2dp_record(type, true);
+       else
+               record = a2dp_record(type, false);
+#else
        record = a2dp_record(type);
+#endif
        if (!record) {
                error("Unable to allocate new service record");
                a2dp_unregister_sep(sep);
@@ -1796,8 +1899,17 @@ done:
 static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
                                        const char *sender)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct a2dp_sep *selected_sep = NULL;
+#endif
+
        for (; list; list = list->next) {
                struct a2dp_sep *sep = list->data;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               struct avdtp_remote_sep *rsep;
+               struct avdtp_media_codec_capability *cap;
+               struct avdtp_service_capability *service;
+#endif
 
                /* Use sender's endpoint if available */
                if (sender) {
@@ -1811,14 +1923,35 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
                                continue;
                }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               rsep = avdtp_find_remote_sep(session, sep->lsep);
+               if (rsep == NULL)
+                       continue;
+
+               service = avdtp_get_codec(rsep);
+               cap = (struct avdtp_media_codec_capability *) service->data;
+
+               if (cap->media_codec_type != A2DP_CODEC_VENDOR) {
+                       selected_sep = sep;
+                       continue;
+               }
+#else
                if (avdtp_find_remote_sep(session, sep->lsep) == NULL)
                        continue;
 
+#endif
                return sep;
 
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (selected_sep)
+               return selected_sep;
+       else
+               return NULL;
+#else
        return NULL;
+#endif
 }
 
 static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
index 2a43d32..c223b01 100755 (executable)
@@ -321,16 +321,31 @@ static void send_key(int fd, uint16_t key, int pressed)
 static gboolean auto_release(gpointer user_data)
 {
        struct avctp *session = user_data;
-
-       session->key.timer = 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint16_t op = session->key.op;
+#endif
 
        DBG("AV/C: key press timeout");
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (op != KEY_FASTFORWARD && op != KEY_REWIND) {
+               session->key.timer = 0;
+               send_key(session->uinput, op, 0);
+       } else {
+               return TRUE;
+       }
+#else
+       session->key.timer = 0;
        send_key(session->uinput, session->key.op, 0);
+#endif
 
        return FALSE;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+extern void avrcp_stop_position_timer(void);
+#endif
+
 static void handle_press(struct avctp *session, uint16_t op)
 {
        if (session->key.timer > 0) {
@@ -339,8 +354,9 @@ static void handle_press(struct avctp *session, uint16_t op)
                /* Only auto release if keys are different */
                if (session->key.op == op)
                        goto done;
-
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                send_key(session->uinput, session->key.op, 0);
+#endif
        }
 
        session->key.op = op;
@@ -361,6 +377,9 @@ static void handle_release(struct avctp *session, uint16_t op)
 
        send_key(session->uinput, op, 0);
 }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+extern void avrcp_stop_position_timer(void);
+#endif
 
 static size_t handle_panel_passthrough(struct avctp *session,
                                        uint8_t transaction, uint8_t *code,
@@ -415,9 +434,13 @@ static size_t handle_panel_passthrough(struct avctp *session,
                        break;
                }
 
-               if (pressed)
+               if (pressed) {
                        handle_press(session, key_map[i].uinput);
-               else
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                       if (key_map[i].avc == AVC_REWIND)
+                               avrcp_stop_position_timer();
+#endif
+               } else
                        handle_release(session, key_map[i].uinput);
 
                break;
@@ -791,7 +814,11 @@ static gboolean process_queue(void *user_data)
                return FALSE;
 
        chan->p = p;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       p->timeout = g_timeout_add_seconds(5, req_timeout, chan);
+#else
        p->timeout = g_timeout_add_seconds(2, req_timeout, chan);
+#endif
 
        return FALSE;
 
@@ -1368,9 +1395,15 @@ static void avctp_control_confirm(struct avctp *session, GIOChannel *chan,
        src = btd_adapter_get_address(device_get_adapter(dev));
        dst = device_get_address(dev);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       session->auth_id = btd_request_authorization(src, dst,
+                                                       AVRCP_TARGET_UUID,
+                                                       auth_cb, session);
+#else
        session->auth_id = btd_request_authorization(src, dst,
                                                        AVRCP_REMOTE_UUID,
                                                        auth_cb, session);
+#endif
        if (session->auth_id == 0)
                goto drop;
 
@@ -1434,6 +1467,16 @@ static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
        if (!device)
                return;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char name[10];
+       device_get_name(device, name, sizeof(name));
+       DBG("name : %s", name);
+       if (g_str_equal(name, "PLT_M50")) {
+               DBG("Don't accept avrcp connection with this headset");
+               return;
+       }
+#endif
+
        session = avctp_get_internal(device);
        if (session == NULL)
                return;
@@ -1690,13 +1733,20 @@ static gboolean repeat_timeout(gpointer user_data)
        struct avctp *session = user_data;
 
        avctp_passthrough_release(session, session->key.op);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        avctp_passthrough_press(session, session->key.op);
 
        return TRUE;
+#else
+       return FALSE;
+#endif
 }
 
 static void release_pressed(struct avctp *session)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (session->key.op != AVC_FAST_FORWARD && session->key.op != AVC_REWIND)
+#endif
        avctp_passthrough_release(session, session->key.op);
 
        if (session->key.timer > 0)
@@ -1748,6 +1798,23 @@ int avctp_send_passthrough(struct avctp *session, uint8_t op)
 
        return avctp_passthrough_press(session, op);
 }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int avctp_send_release_passthrough(struct avctp *session, uint8_t op)
+{
+       DBG("+");
+
+       if (op != AVC_FAST_FORWARD && op != AVC_REWIND)
+               return FALSE;
+
+       /* Auto release if key pressed */
+       if (session->key.timer > 0)
+               g_source_remove(session->key.timer);
+       session->key.timer = 0;
+
+       DBG("-");
+       return avctp_passthrough_release(session, op);
+}
+#endif
 
 int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
                                uint8_t code, uint8_t subunit,
index 68a2735..90316ad 100755 (executable)
@@ -173,6 +173,9 @@ unsigned int avctp_register_browsing_pdu_handler(struct avctp *session,
 gboolean avctp_unregister_browsing_pdu_handler(unsigned int id);
 
 int avctp_send_passthrough(struct avctp *session, uint8_t op);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int avctp_send_release_passthrough(struct avctp *session, uint8_t op);
+#endif
 int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
                                uint8_t code, uint8_t subunit,
                                uint8_t *operands, size_t operand_count);
index 4ec9cca..bc5a735 100755 (executable)
 #include "lib/sdp_lib.h"
 #include "lib/uuid.h"
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include <sys/ioctl.h>
+#include <bluetooth/hci.h>
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
 #include "btio/btio.h"
 #include "src/log.h"
 #include "src/shared/util.h"
 #include "src/adapter.h"
 #include "src/device.h"
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "src/service.h"
+#endif
+
 #include "avdtp.h"
 #include "sink.h"
 #include "source.h"
+#include "../../profile.h"
 
 #define AVDTP_PSM 25
 
@@ -85,7 +95,11 @@ static unsigned int seids;
 #define AVDTP_MSG_TYPE_ACCEPT                  0x02
 #define AVDTP_MSG_TYPE_REJECT                  0x03
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define REQ_TIMEOUT 10
+#else
 #define REQ_TIMEOUT 6
+#endif
 #define ABORT_TIMEOUT 2
 #define DISCONNECT_TIMEOUT 1
 #define START_TIMEOUT 1
@@ -830,13 +844,23 @@ static void handle_transport_connect(struct avdtp *session, GIOChannel *io,
                goto proceed;
 
        DBG("sk %d, omtu %d, send buffer size %d", sk, omtu, buf_size);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       min_buf_size = omtu * 10;
+#else
        min_buf_size = omtu * 2;
+#endif
        if (buf_size < min_buf_size) {
                DBG("send buffer size to be increassed to %d",
                                min_buf_size);
                set_send_buffer_size(sk, min_buf_size);
        }
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       else {
+               DBG("send buffer size to be decreassed to %d",
+                               min_buf_size);
+               set_send_buffer_size(sk, min_buf_size);
+       }
+#endif
 proceed:
        if (!stream->open_acp && sep->cfm && sep->cfm->open)
                sep->cfm->open(session, sep, stream, NULL, sep->user_data);
@@ -940,11 +964,182 @@ static void handle_unanswered_req(struct avdtp *session,
        pending_req_free(req);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean send_broadcom_a2dp_qos(const bdaddr_t *dst, gboolean qos_high)
+{
+       int dd;
+       int err = 0;
+       struct hci_conn_info_req *cr;
+       broadcom_qos_cp cp;
+
+       dd = hci_open_dev(0);
+
+       if (dd < 0)
+               return FALSE;
+
+       cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info));
+
+       cr->type = ACL_LINK;
+       bacpy(&cr->bdaddr, dst);
+
+       err = ioctl(dd, HCIGETCONNINFO, cr);
+       if (err < 0) {
+               error("Fail to get HCIGETCOINFO");
+               g_free(cr);
+               hci_close_dev(dd);
+               return FALSE;
+       }
+
+       cp.handle = cr->conn_info->handle;
+       DBG("Handle %d", cp.handle);
+       g_free(cr);
+
+       if (qos_high)
+               cp.priority = BRCM_QOS_PRIORITY_HIGH;
+       else
+               cp.priority = BRCM_QOS_PRIORITY_NORMAL;
+
+       if (hci_send_cmd(dd, OGF_VENDOR_CMD, BROADCOM_QOS_CMD,
+                               BROADCOM_QOS_CP_SIZE, &cp) < 0) {
+               hci_close_dev(dd);
+               return FALSE;
+       }
+       DBG("Send Broadcom Qos Patch %s", qos_high ? "High" : "Low");
+
+       hci_close_dev(dd);
+
+       return TRUE;
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_SPRD_QOS
+static gboolean send_sprd_a2dp_qos(bdaddr_t *dst, gboolean qos_high)
+{
+       int dd;
+       int err = 0;
+       struct hci_conn_info_req *cr;
+       qos_setup_cp cp;
+
+       dd = hci_open_dev(0);
+
+       cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info));
+
+       cr->type = ACL_LINK;
+       bacpy(&cr->bdaddr, dst);
+
+       err = ioctl(dd, HCIGETCONNINFO, cr);
+       if (err < 0) {
+               error("Fail to get HCIGETCOINFO");
+               g_free(cr);
+               hci_close_dev(dd);
+               return FALSE;
+       }
+
+       cp.handle = cr->conn_info->handle;
+       cp.flags = 0x00;
+       DBG("Handle %d", cp.handle);
+       g_free(cr);
+
+       if (qos_high) {
+               cp.qos.service_type = 0x02;
+               cp.qos.token_rate = 0X000000C8;
+               cp.qos.peak_bandwidth = 0X000000C8;
+               cp.qos.latency = 0x00000001;
+               cp.qos.delay_variation = 0xFFFFFFFF;
+       } else {
+               cp.qos.service_type = 0x01;
+               cp.qos.token_rate = 0X00000000;
+               cp.qos.peak_bandwidth = 0X00000000;
+               cp.qos.latency = 0x00000001;
+               cp.qos.delay_variation = 0xFFFFFFFF;
+       }
+
+       if (hci_send_cmd(dd, OGF_LINK_POLICY, OCF_QOS_SETUP,
+                               QOS_SETUP_CP_SIZE, &cp) < 0) {
+               hci_close_dev(dd);
+               return FALSE;
+       }
+       DBG("Send Spreadtrum Qos Patch %s", qos_high ? "High" : "Low");
+
+       hci_close_dev(dd);
+
+       return TRUE;
+}
+#endif /* TIZEN_FEATURE_BLUEZ_SPRD_QOS */
+
+static gboolean fix_role_to_master(const bdaddr_t *dst, gboolean fix_to_master)
+{
+       int dd;
+       int err = 0;
+       struct hci_conn_info_req *cr;
+       switch_role_cp sr_cp;
+       write_link_policy_cp lp_cp;
+
+       dd = hci_open_dev(0);
+       if (dd < 0) {
+               error("hci_open_dev is failed");
+               return FALSE;
+       }
+
+       cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info));
+
+       cr->type = ACL_LINK;
+       bacpy(&cr->bdaddr, dst);
+       err = ioctl(dd, HCIGETCONNINFO, cr);
+       if (err < 0) {
+               error("Fail to get HCIGETCOINFO : %d", err);
+               g_free(cr);
+               hci_close_dev(dd);
+               return FALSE;
+       }
+
+       if (!(cr->conn_info->link_mode & HCI_LM_MASTER) && fix_to_master) {
+               DBG("Need to role switch");
+
+               bacpy(&sr_cp.bdaddr, dst);
+               sr_cp.role = 0x00;      /* 0x00 : Master, 0x01 : Slave */
+               if (hci_send_cmd(dd, OGF_LINK_POLICY, OCF_SWITCH_ROLE,
+                                       SWITCH_ROLE_CP_SIZE, &sr_cp) < 0) {
+                       error("switch role is failed");
+                       g_free(cr);
+                       hci_close_dev(dd);
+                       return FALSE;
+               }
+       }
+
+       lp_cp.handle = cr->conn_info->handle;
+       DBG("Handle %d", lp_cp.handle);
+       g_free(cr);
+
+       lp_cp.policy = fix_to_master ? 0x00 : HCI_LP_SNIFF | HCI_LP_RSWITCH;
+       DBG("Request link policy : 0x%X", lp_cp.policy);
+
+       if (hci_send_cmd(dd, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY,
+                               WRITE_LINK_POLICY_CP_SIZE, &lp_cp) < 0) {
+               error("write link policy is failed : %d", lp_cp.policy);
+               hci_close_dev(dd);
+               return FALSE;
+       }
+
+       hci_close_dev(dd);
+
+       return TRUE;
+}
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
 static void avdtp_sep_set_state(struct avdtp *session,
                                struct avdtp_local_sep *sep,
                                avdtp_state_t state)
 {
        struct avdtp_stream *stream = sep->stream;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       const bdaddr_t *dst;
+#if defined(TIZEN_FEATURE_BLUEZ_SPRD_QOS)
+       dst = device_get_address(session->device);
+#else
+       if (TIZEN_FEATURE_BLUEZ_BRCM_QOS || TIZEN_FEATURE_BLUEZ_ROLE_CHANGE)
+               dst = device_get_address(session->device);
+#endif
+#endif
        avdtp_state_t old_state;
        struct avdtp_error err, *err_ptr = NULL;
        GSList *l;
@@ -975,12 +1170,38 @@ static void avdtp_sep_set_state(struct avdtp *session,
                break;
        case AVDTP_STATE_OPEN:
                stream->starting = FALSE;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               if (TIZEN_FEATURE_BLUEZ_BRCM_QOS) {
+                       send_broadcom_a2dp_qos(dst, FALSE);
+               } else {
+#if defined(TIZEN_FEATURE_BLUEZ_SPRD_QOS)
+                       if (old_state == AVDTP_STATE_STREAMING)
+                               send_sprd_a2dp_qos(dst, FALSE);
+#endif
+               }
+               if (TIZEN_FEATURE_BLUEZ_ROLE_CHANGE)
+                       fix_role_to_master(dst, FALSE);
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
                break;
        case AVDTP_STATE_STREAMING:
                if (stream->start_timer) {
                        g_source_remove(stream->start_timer);
                        stream->start_timer = 0;
                }
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               if (TIZEN_FEATURE_BLUEZ_BRCM_QOS) {
+                       send_broadcom_a2dp_qos(dst, TRUE);
+               } else {
+#if defined(TIZEN_FEATURE_BLUEZ_SPRD_QOS)
+                       if (old_state == AVDTP_STATE_OPEN)
+                               send_sprd_a2dp_qos(dst, TRUE);
+#endif
+               }
+               if (TIZEN_FEATURE_BLUEZ_ROLE_CHANGE)
+                       fix_role_to_master(dst, TRUE);
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
                stream->open_acp = FALSE;
                break;
        case AVDTP_STATE_CLOSING:
@@ -1020,7 +1241,11 @@ static void avdtp_sep_set_state(struct avdtp *session,
        }
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void finalize_discovery(struct avdtp *session, int err)
+#else
 static void finalize_discovery(struct avdtp *session, int err)
+#endif
 {
        struct discover_callback *discover = session->discover;
        struct avdtp_error avdtp_err;
@@ -1104,12 +1329,20 @@ static void avdtp_free(void *data)
 static void connection_lost(struct avdtp *session, int err)
 {
        char address[18];
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct btd_service *service;
+#endif
 
        session = avdtp_ref(session);
 
        ba2str(device_get_address(session->device), address);
        DBG("Disconnected from %s", address);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       service = btd_device_get_service(session->device, A2DP_SINK_UUID);
+       if (service)
+               btd_service_connecting_complete(service, -err);
+#endif
        g_slist_foreach(session->streams, (GFunc) release_stream, session);
        session->streams = NULL;
 
@@ -1117,14 +1350,48 @@ static void connection_lost(struct avdtp *session, int err)
 
        avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
 
-       avdtp_unref(session);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       DBG("%p: ref=%d", session, session->ref);
+       if (err != EIO && session->ref > 0) /* link loss*/
+               return;
+#endif
+
+       avdtp_free(session);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean disconnect_acl_timeout(gpointer user_data)
+{
+       struct btd_device *device = user_data;
+
+       DBG("");
+
+       btd_device_disconnect(device);
+
+       return FALSE;
+}
+#endif
+
 static gboolean disconnect_timeout(gpointer user_data)
 {
        struct avdtp *session = user_data;
        struct btd_service *service;
        gboolean stream_setup;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct btd_device *device = NULL;
+       struct btd_adapter *adapter = NULL;
+       const bdaddr_t *bdaddr = NULL;
+
+       DBG("");
+#endif
+
+/* Fix : REVERSE_INULL */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (session->device == NULL) {
+               error("session device NOT found");
+               return FALSE;
+       }
+#endif
 
        session->dc_timer = 0;
 
@@ -1143,19 +1410,83 @@ static gboolean disconnect_timeout(gpointer user_data)
                return FALSE;
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (session->device) {
+               adapter = device_get_adapter(session->device);
+               bdaddr = device_get_address(session->device);
+               if (adapter && bdaddr)
+                       device = btd_adapter_find_device(adapter, bdaddr, BDADDR_BREDR);
+               if (!device)
+                       error("device is NOT found");
+       }
+#endif
+
        connection_lost(session, ETIMEDOUT);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (device)
+               g_timeout_add(100,
+                       disconnect_acl_timeout,
+                       device);
+#endif
+
        return FALSE;
 }
 
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+static void set_disconnect_timer_for_sink(struct avdtp *session, gboolean disconn)
+{
+       char name[6];
+
+       if (session->dc_timer)
+               remove_disconnect_timer(session);
+
+       device_get_name(session->device, name, sizeof(name));
+       DBG("name : [%s]", name);
+       if (g_str_equal(name, "VW BT") || g_str_equal(name, "VW MI") ||
+                                               g_str_equal(name, "Seat ")) {
+               session->dc_timer = g_timeout_add_seconds(3, disconnect_timeout,
+                               session);
+       } else if (g_str_equal(name, "CAR M")) {
+               session->dc_timer = g_timeout_add(200, disconnect_timeout,
+                               session);
+       } else {
+               if (disconn == TRUE)
+                       session->dc_timer = g_timeout_add(100,
+                                       disconnect_timeout,
+                                       session);
+               else
+                       session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
+                                       disconnect_timeout,
+                                       session);
+       }
+}
+#endif
+
 static void set_disconnect_timer(struct avdtp *session)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char name[6];
+#endif
        if (session->dc_timer)
                remove_disconnect_timer(session);
 
-       session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
-                                               disconnect_timeout,
-                                               session);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       device_get_name(session->device, name, sizeof(name));
+       DBG("name : [%s]", name);
+       if (g_str_equal(name, "VW BT") || g_str_equal(name, "VW MI") ||
+                                               g_str_equal(name, "Seat ")) {
+               session->dc_timer = g_timeout_add_seconds(3, disconnect_timeout,
+                               session);
+       } else if (g_str_equal(name, "CAR M")) {
+               session->dc_timer = g_timeout_add(200, disconnect_timeout,
+                               session);
+       } else {
+               session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
+                               disconnect_timeout,
+                               session);
+       }
+#endif
 }
 
 void avdtp_unref(struct avdtp *session)
@@ -1164,6 +1495,10 @@ void avdtp_unref(struct avdtp *session)
                return;
 
        session->ref--;
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+       struct btd_adapter *adapter;
+       adapter = avdtp_get_adapter(session);
+#endif
 
        DBG("%p: ref=%d", session, session->ref);
 
@@ -1172,7 +1507,14 @@ void avdtp_unref(struct avdtp *session)
 
        switch (session->state) {
        case AVDTP_SESSION_STATE_CONNECTED:
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+               if (btd_adapter_get_a2dp_role(adapter) == BLUETOOTH_A2DP_SINK_ROLE)
+                       set_disconnect_timer_for_sink(session, TRUE);
+               else
+                       set_disconnect_timer(session);
+#else
                set_disconnect_timer(session);
+#endif
                break;
        case AVDTP_SESSION_STATE_CONNECTING:
                connection_lost(session, ECONNABORTED);
@@ -2163,6 +2505,21 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
        }
 
        if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               struct btd_service *service;
+
+               service = btd_device_get_service(session->device, A2DP_SINK_UUID);
+               if (service != NULL) {
+                       DBG("A2dp state %d", btd_service_get_state(
+                                       btd_device_get_service(session->device, A2DP_SINK_UUID)));
+
+                       if (btd_service_get_state(btd_device_get_service(session->device,
+                                       A2DP_SINK_UUID)) == BTD_SERVICE_STATE_DISCONNECTING) {
+                               DBG("avdtp:%p , disconnect timer is going on", session);
+                               return FALSE;
+                       }
+               }
+#endif
                if (!avdtp_parse_cmd(session, session->in.transaction,
                                        session->in.signal_id,
                                        session->in.buf,
@@ -2272,6 +2629,10 @@ static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
        struct avdtp *session = user_data;
        char address[18];
        int err_no = EIO;
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+       struct btd_adapter *adapter;
+       adapter = avdtp_get_adapter(session);
+#endif
 
        if (err) {
                err_no = err->code;
@@ -2320,8 +2681,16 @@ static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
                                                (GIOFunc) session_cb, session,
                                                NULL);
 
-               if (session->stream_setup)
+               if (session->stream_setup) {
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+                       if (btd_adapter_get_a2dp_role(adapter) == BLUETOOTH_A2DP_SINK_ROLE)
+                               set_disconnect_timer_for_sink(session, FALSE);
+                       else
+                               set_disconnect_timer(session);
+#else
                        set_disconnect_timer(session);
+#endif
+               }
        } else if (session->pending_open)
                handle_transport_connect(session, chan, session->imtu,
                                                                session->omtu);
@@ -2385,10 +2754,26 @@ static GIOChannel *l2cap_connect(struct avdtp *session)
        GError *err = NULL;
        GIOChannel *io;
        const bdaddr_t *src;
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+       struct btd_adapter *adapter;
+       adapter = avdtp_get_adapter(session);
+#endif
 
        src = btd_adapter_get_address(device_get_adapter(session->device));
 
-       io = bt_io_connect(avdtp_connect_cb, session,
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+       if (btd_adapter_get_a2dp_role(adapter) == BLUETOOTH_A2DP_SINK_ROLE) {
+               io = bt_io_connect(avdtp_connect_cb, session,
+                               NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, src,
+                               BT_IO_OPT_DEST_BDADDR,
+                               device_get_address(session->device),
+                               BT_IO_OPT_PSM, AVDTP_PSM,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_IMTU, 895,
+                               BT_IO_OPT_INVALID);
+       } else {
+               io = bt_io_connect(avdtp_connect_cb, session,
                                NULL, &err,
                                BT_IO_OPT_SOURCE_BDADDR, src,
                                BT_IO_OPT_DEST_BDADDR,
@@ -2396,6 +2781,18 @@ static GIOChannel *l2cap_connect(struct avdtp *session)
                                BT_IO_OPT_PSM, AVDTP_PSM,
                                BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
                                BT_IO_OPT_INVALID);
+       }
+#else
+       io = bt_io_connect(avdtp_connect_cb, session,
+                       NULL, &err,
+                       BT_IO_OPT_SOURCE_BDADDR, src,
+                       BT_IO_OPT_DEST_BDADDR,
+                       device_get_address(session->device),
+                       BT_IO_OPT_PSM, AVDTP_PSM,
+                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                       BT_IO_OPT_INVALID);
+#endif
+
        if (!io) {
                error("%s", err->message);
                g_error_free(err);
@@ -2782,6 +3179,12 @@ static gboolean avdtp_abort_resp(struct avdtp *session,
                                        struct seid_rej *resp, int size)
 {
        struct avdtp_local_sep *sep = stream->lsep;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (!sep) {
+               error("Error in getting sep");
+               return FALSE;
+       }
+#endif
 
        avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
 
@@ -3403,11 +3806,44 @@ int avdtp_start(struct avdtp *session, struct avdtp_stream *stream)
                /* If timer already active wait it */
                if (stream->start_timer)
                        return 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               else {
+                       char address[18];
+                       uint32_t timeout_sec = START_TIMEOUT;
+
+                       ba2str(device_get_address(session->device), address);
+                       /* For Bose headset (Bose AE2w) 2 seconds timeout is required to avoid AVDTP ABORT_CMD */
+                       if (!strncasecmp(address, "00:0C:8A", 8))
+                               timeout_sec = 2;
+                       /* For Gear Circle, HS3000 headset, this headset doesn't initiate start command and
+                        * when we add timer for 1 second so idle may trigger callback after 1.2 sec or
+                        * 1.5 sec. So, don't timer for this headset.*/
+                       if (!strncasecmp(address, "10:92:66", 8) ||
+                               !strncasecmp(address, "A8:9F:BA", 8) ||
+                               !strncasecmp(address, "00:26:B4", 8)) {
+                               start_timeout(stream);
+                               return 0;
+                       }
+                        /* Here we can't use Mac address as there are changing so check for name */
+                       char name[10];
+                       device_get_name(session->device, name, sizeof(name));
+                       DBG("name : %s", name);
+                       if (g_str_equal(name, "HS3000")) {
+                               start_timeout(stream);
+                               return 0;
+                       }
 
+                       stream->start_timer = g_timeout_add_seconds(timeout_sec,
+                                                                       start_timeout,
+                                                                       stream);
+                       return 0;
+               }
+#else
                stream->start_timer = g_timeout_add_seconds(START_TIMEOUT,
                                                                start_timeout,
                                                                stream);
                return 0;
+#endif
        }
 
        if (stream->close_int == TRUE) {
index 621a6e3..54591aa 100755 (executable)
@@ -230,6 +230,10 @@ int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
 
 gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void finalize_discovery(struct avdtp *session, int err);
+#endif
+
 unsigned int avdtp_stream_add_cb(struct avdtp *session,
                                        struct avdtp_stream *stream,
                                        avdtp_stream_state_cb cb, void *data);
index 51a89b1..4d24510 100755 (executable)
 #define AVRCP_CHARSET_UTF8             106
 
 #define AVRCP_BROWSING_TIMEOUT         1
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define AVRCP_CT_VERSION               0x0103
+#define AVRCP_TG_VERSION               0x0103
+#else
 #define AVRCP_CT_VERSION               0x0106
 #define AVRCP_TG_VERSION               0x0105
-
+#endif
 #define AVRCP_SCOPE_MEDIA_PLAYER_LIST                  0x00
 #define AVRCP_SCOPE_MEDIA_PLAYER_VFS                   0x01
 #define AVRCP_SCOPE_SEARCH                             0x02
@@ -275,6 +279,9 @@ struct avrcp {
        uint8_t transaction;
        uint8_t transaction_events[AVRCP_EVENT_LAST + 1];
        struct pending_pdu *pending_pdu;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint32_t playback_status_id;
+#endif
 };
 
 struct passthrough_handler {
@@ -292,6 +299,15 @@ struct control_pdu_handler {
 static GSList *servers = NULL;
 static unsigned int avctp_id = 0;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
+static uint16_t adapter_avrcp_tg_ver = 0;
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
+static uint16_t adapter_avrcp_ct_ver = 0;
+#endif
+#endif
+
 /* Default feature bit mask for media player as per avctp.c:key_map */
 static const uint8_t features[16] = {
                                0xF8, 0xBF, 0xFF, 0xBF, 0x1F,
@@ -306,6 +322,13 @@ static uint32_t company_ids[] = {
 
 static void avrcp_register_notification(struct avrcp *session, uint8_t event);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static GList *player_list_settings(struct avrcp_player *player);
+void avrcp_stop_position_timer(void);
+unsigned int pos_timer_id = 0;
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
 static sdp_record_t *avrcp_ct_record(void)
 {
        sdp_list_t *svclass_id, *pfseq, *apseq, *apseq1, *root;
@@ -315,12 +338,23 @@ static sdp_record_t *avrcp_ct_record(void)
        sdp_record_t *record;
        sdp_data_t *psm[2], *version, *features;
        uint16_t lp = AVCTP_CONTROL_PSM, ap = AVCTP_BROWSING_PSM;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint16_t avctp_ver = 0x0104;
+       uint16_t feat = 0;
+#ifdef ENABLE_AVRCP_CATEGORY1
+       feat = AVRCP_FEATURE_CATEGORY_1;
+#endif
+#ifdef ENABLE_AVRCP_CATEGORY2
+       feat = feat | AVRCP_FEATURE_CATEGORY_2;
+#endif
+#else
        uint16_t avctp_ver = 0x0103;
        uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
                                                AVRCP_FEATURE_CATEGORY_2 |
                                                AVRCP_FEATURE_CATEGORY_3 |
                                                AVRCP_FEATURE_CATEGORY_4 |
                                                AVRCP_FEATURE_BROWSING);
+#endif
 
        record = sdp_record_alloc();
        if (!record)
@@ -371,6 +405,9 @@ static sdp_record_t *avrcp_ct_record(void)
        /* Bluetooth Profile Descriptor List */
        sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
        profile[0].version = AVRCP_CT_VERSION;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       adapter_avrcp_ct_ver = AVRCP_CT_VERSION;
+#endif
        pfseq = sdp_list_append(NULL, &profile[0]);
        sdp_set_profile_descs(record, pfseq);
 
@@ -396,7 +433,9 @@ static sdp_record_t *avrcp_ct_record(void)
 
        return record;
 }
+#endif
 
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
 static sdp_record_t *avrcp_tg_record(void)
 {
        sdp_list_t *svclass_id, *pfseq, *apseq, *root, *apseq_browsing;
@@ -405,9 +444,23 @@ static sdp_record_t *avrcp_tg_record(void)
        sdp_list_t *aproto_control, *proto_control[2];
        sdp_record_t *record;
        sdp_data_t *psm_control, *version, *features, *psm_browsing;
-       sdp_list_t *aproto_browsing, *proto_browsing[2] = {0};
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+       sdp_list_t *aproto_browsing;
+#endif
+       sdp_list_t *proto_browsing[2] = {0};
        uint16_t lp = AVCTP_CONTROL_PSM;
        uint16_t lp_browsing = AVCTP_BROWSING_PSM;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint16_t avctp_ver = 0x0104;
+       uint16_t feat = 0;
+#ifdef ENABLE_AVRCP_CATEGORY1
+       feat = AVRCP_FEATURE_CATEGORY_1 |
+               AVRCP_FEATURE_PLAYER_SETTINGS;
+#endif
+#ifdef ENABLE_AVRCP_CATEGORY2
+       feat = feat | AVRCP_FEATURE_CATEGORY_2;
+#endif
+#else
        uint16_t avctp_ver = 0x0103;
        uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
                                        AVRCP_FEATURE_CATEGORY_2 |
@@ -415,7 +468,7 @@ static sdp_record_t *avrcp_tg_record(void)
                                        AVRCP_FEATURE_CATEGORY_4 |
                                        AVRCP_FEATURE_BROWSING |
                                        AVRCP_FEATURE_PLAYER_SETTINGS );
-
+#endif
        record = sdp_record_alloc();
        if (!record)
                return NULL;
@@ -453,12 +506,17 @@ static sdp_record_t *avrcp_tg_record(void)
        proto_browsing[1] = sdp_list_append(proto_browsing[1], version);
        apseq_browsing = sdp_list_append(apseq_browsing, proto_browsing[1]);
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        aproto_browsing = sdp_list_append(NULL, apseq_browsing);
        sdp_set_add_access_protos(record, aproto_browsing);
+#endif
 
        /* Bluetooth Profile Descriptor List */
        sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
        profile[0].version = AVRCP_TG_VERSION;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       adapter_avrcp_tg_ver = AVRCP_TG_VERSION;
+#endif
        pfseq = sdp_list_append(NULL, &profile[0]);
        sdp_set_profile_descs(record, pfseq);
 
@@ -471,7 +529,9 @@ static sdp_record_t *avrcp_tg_record(void)
        sdp_list_free(proto_browsing[0], NULL);
        sdp_list_free(proto_browsing[1], NULL);
        sdp_list_free(apseq_browsing, NULL);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        sdp_list_free(aproto_browsing, NULL);
+#endif
 
        free(psm_control);
        free(version);
@@ -485,6 +545,7 @@ static sdp_record_t *avrcp_tg_record(void)
 
        return record;
 }
+#endif
 
 static unsigned int attr_get_max_val(uint8_t attr)
 {
@@ -492,9 +553,17 @@ static unsigned int attr_get_max_val(uint8_t attr)
        case AVRCP_ATTRIBUTE_EQUALIZER:
                return AVRCP_EQUALIZER_ON;
        case AVRCP_ATTRIBUTE_REPEAT_MODE:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               return AVRCP_REPEAT_MODE_ALL;
+#else
                return AVRCP_REPEAT_MODE_GROUP;
+#endif
        case AVRCP_ATTRIBUTE_SHUFFLE:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               return AVRCP_SHUFFLE_ALL;
+#else
                return AVRCP_SHUFFLE_GROUP;
+#endif
        case AVRCP_ATTRIBUTE_SCAN:
                return AVRCP_SCAN_GROUP;
        }
@@ -673,6 +742,10 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id,
        GSList *l;
        int attr;
        int val;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint32_t *position_val = NULL;
+       GList *settings;
+#endif
 
        if (player->sessions == NULL)
                return;
@@ -712,6 +785,24 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id,
                break;
        case AVRCP_EVENT_SETTINGS_CHANGED:
                size = 2;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               settings = player_list_settings(player);
+               pdu->params[1] = g_list_length(settings);
+               for (; settings; settings = settings->next) {
+                       const char *key = settings->data;
+
+                       attr = attr_to_val(key);
+                       if (attr < 0)
+                               continue;
+
+                       val = player_get_setting(player, attr);
+                       if (val < 0)
+                               continue;
+
+                       pdu->params[size++] = attr;
+                       pdu->params[size++] = val;
+               }
+#else
                pdu->params[1] = 1;
 
                attr = attr_to_val(data);
@@ -724,7 +815,19 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id,
 
                pdu->params[size++] = attr;
                pdu->params[size++] = val;
+#endif /* __TIZEN__PATCH__ */
                break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+               size = 5;
+               position_val = (uint32_t *) data;
+               *position_val = (*position_val & 0x000000ff) << 24 |
+                                (*position_val & 0x0000ff00) << 8 |
+                                (*position_val & 0x00ff0000) >> 8 |
+                                (*position_val & 0xff000000) >> 24;
+               memcpy(&pdu->params[1], position_val, sizeof(uint32_t));
+               break;
+#endif
        case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
                size = 5;
                memcpy(&pdu->params[1], &player->id, sizeof(uint16_t));
@@ -740,6 +843,17 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id,
 
 done:
        pdu->params_len = htons(size);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (id == AVRCP_EVENT_PLAYBACK_POS_CHANGED &&
+                       pos_timer_id > 0) {
+               /* Remove the timer function which was added for register notification.
+                * As we are sending changed event eariler then time interval.
+                */
+               DBG("Removing the timer function added by register notification");
+               g_source_remove(pos_timer_id);
+               pos_timer_id = 0;
+       }
+#endif
 
        for (l = player->sessions; l; l = l->next) {
                struct avrcp *session = l->data;
@@ -920,8 +1034,10 @@ static const char *attrval_to_str(uint8_t attr, uint8_t value)
                        return "singletrack";
                case AVRCP_REPEAT_MODE_ALL:
                        return "alltracks";
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                case AVRCP_REPEAT_MODE_GROUP:
                        return "group";
+#endif
                }
 
                break;
@@ -933,8 +1049,10 @@ static const char *attrval_to_str(uint8_t attr, uint8_t value)
                        return "off";
                case AVRCP_SCAN_ALL:
                        return "alltracks";
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                case AVRCP_SCAN_GROUP:
                        return "group";
+#endif
                }
 
                break;
@@ -1426,6 +1544,16 @@ static GList *player_list_settings(struct avrcp_player *player)
        return player->cb->list_settings(player->user_data);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static uint32_t player_get_playback_position(struct avrcp_player *player)
+{
+       if (player == NULL)
+               return UINT32_MAX;
+
+       return player->cb->get_position(player->user_data);
+}
+#endif
+
 static bool avrcp_handle_play(struct avrcp *session)
 {
        struct avrcp_player *player = target_get_player(session);
@@ -1507,6 +1635,33 @@ static bool handle_passthrough(struct avctp *conn, uint8_t op, bool pressed,
        return handler->func(session);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void avrcp_stop_position_timer(void)
+{
+       if (pos_timer_id > 0) {
+               DBG("Removing position timer id");
+               g_source_remove(pos_timer_id);
+               pos_timer_id = 0;
+       }
+}
+gboolean send_playback_position_event(gpointer user_data)
+{
+       struct avrcp_player *player = user_data;
+       uint32_t playback_position;
+       uint8_t play_status;
+
+       play_status = player_get_status(player);
+       if (play_status != AVRCP_PLAY_STATUS_PLAYING)
+               return FALSE;
+
+       playback_position = player_get_playback_position(player);
+       pos_timer_id = 0;
+       avrcp_player_event(player, AVRCP_EVENT_PLAYBACK_POS_CHANGED,
+                                               &playback_position);
+       return FALSE;
+}
+#endif
+
 static uint8_t avrcp_handle_register_notification(struct avrcp *session,
                                                struct avrcp_header *pdu,
                                                uint8_t transaction)
@@ -1515,6 +1670,11 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
        struct btd_device *dev = session->dev;
        uint16_t len = ntohs(pdu->params_len);
        uint64_t uid;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint32_t playback_interval;
+       uint32_t playback_position;
+       uint8_t play_status;
+#endif
        GList *settings;
 
        /*
@@ -1586,6 +1746,40 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
                len = 2;
 
                break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+               len = 5;
+
+               /* time interval in seconds at which the change in playback position
+               shall be notified */
+               memcpy(&playback_interval, &pdu->params[1], sizeof(uint32_t));
+               playback_interval = ((playback_interval>>24)&0xff) |
+                                   ((playback_interval<<8)&0xff0000) |
+                                   ((playback_interval>>8)&0xff00) |
+                                   ((playback_interval<<24)&0xff000000);
+
+               play_status = player_get_status(player);
+
+               if (play_status != AVRCP_PLAY_STATUS_PLAYING) {
+                       DBG("Play Pos Changed Event is skipped(%d)", play_status);
+               } else {
+                       DBG("Playback interval : %d secs", playback_interval);
+                       pos_timer_id = g_timeout_add_seconds(
+                                       playback_interval,
+                                       send_playback_position_event, player);
+               }
+
+               /* retrieve current playback position for interim response */
+               playback_position = player_get_playback_position(player);
+               playback_position = (playback_position & 0x000000ff) << 24 |
+                                   (playback_position & 0x0000ff00) << 8 |
+                                   (playback_position & 0x00ff0000) >> 8 |
+                                   (playback_position & 0xff000000) >> 24;
+               memcpy(&pdu->params[1], &playback_position, sizeof(uint32_t));
+
+               break;
+#endif
+
        default:
                /* All other events are not supported yet */
                goto err;
@@ -2918,6 +3112,26 @@ static int ct_press(struct avrcp_player *player, uint8_t op)
        return 0;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static int ct_release(struct avrcp_player *player, uint8_t op)
+{
+       DBG("+");
+       int err;
+       struct avrcp *session;
+
+       session = player->sessions->data;
+       if (session == NULL)
+               return -ENOTCONN;
+
+       err = avctp_send_release_passthrough(session->conn, op);
+       if (err < 0)
+               return err;
+
+       DBG("-");
+       return 0;
+}
+#endif
+
 static int ct_play(struct media_player *mp, void *user_data)
 {
        struct avrcp_player *player = user_data;
@@ -2953,6 +3167,43 @@ static int ct_previous(struct media_player *mp, void *user_data)
        return ct_press(player, AVC_BACKWARD);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static int ct_press_fast_forward(struct media_player *mp, void *user_data)
+{
+       DBG("+");
+       struct avrcp_player *player = user_data;
+
+       DBG("-");
+       return ct_press(player, AVC_FAST_FORWARD);
+}
+
+static int ct_release_fast_forward(struct media_player *mp, void *user_data)
+{
+       DBG("+");
+       struct avrcp_player *player = user_data;
+
+       DBG("-");
+       return ct_release(player, AVC_FAST_FORWARD);
+}
+
+static int ct_press_rewind(struct media_player *mp, void *user_data)
+{
+       DBG("+");
+       struct avrcp_player *player = user_data;
+
+       DBG("-");
+       return ct_press(player, AVC_REWIND);
+}
+
+static int ct_release_rewind(struct media_player *mp, void *user_data)
+{
+       DBG("+");
+       struct avrcp_player *player = user_data;
+
+       DBG("-");
+       return ct_release(player, AVC_REWIND);
+}
+#else
 static int ct_fast_forward(struct media_player *mp, void *user_data)
 {
        struct avrcp_player *player = user_data;
@@ -2966,6 +3217,7 @@ static int ct_rewind(struct media_player *mp, void *user_data)
 
        return ct_press(player, AVC_REWIND);
 }
+#endif
 
 static int ct_list_items(struct media_player *mp, const char *name,
                                uint32_t start, uint32_t end, void *user_data)
@@ -3274,8 +3526,15 @@ static const struct media_player_callback ct_cbs = {
        .stop           = ct_stop,
        .next           = ct_next,
        .previous       = ct_previous,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       .press_fast_forward     = ct_press_fast_forward,
+       .release_fast_forward   = ct_release_fast_forward,
+       .press_rewind           = ct_press_rewind,
+       .release_rewind         = ct_release_rewind,
+#else
        .fast_forward   = ct_fast_forward,
        .rewind         = ct_rewind,
+#endif
        .list_items     = ct_list_items,
        .change_folder  = ct_change_folder,
        .search         = ct_search,
@@ -3397,6 +3656,10 @@ static void player_destroy(gpointer data)
        if (player->destroy)
                player->destroy(player->user_data);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       avrcp_stop_position_timer();
+#endif
+
        if (player->changed_id > 0)
                g_source_remove(player->changed_id);
 
@@ -3724,8 +3987,42 @@ static void avrcp_register_notification(struct avrcp *session, uint8_t event)
                                        avrcp_handle_event, session);
 }
 
-static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code,
-                                       uint8_t subunit, uint8_t transaction,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static char *avrcp_event_to_string(uint8_t event)
+{
+
+       switch (event) {
+               case AVRCP_EVENT_STATUS_CHANGED:
+                       return "AVRCP EVENT STATUS CHANGED";
+               case AVRCP_EVENT_TRACK_CHANGED:
+                       return "AVRCP EVENT TRACK CHANGED";
+               case AVRCP_EVENT_SETTINGS_CHANGED:
+                       return "AVRCP EVENT SETTINGS CHANGED";
+               case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+                       return "AVRCP EVENT ADDRESSED PLAYER CHANGED";
+               case AVRCP_EVENT_UIDS_CHANGED:
+                       return "AVRCP EVENT UIDS CHANGED";
+               case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
+                       return "AVRCP EVENT AVAILABLE PLAYERS CHANGED";
+               case AVRCP_EVENT_VOLUME_CHANGED:
+                       return "AVRCP EVENT VOLUME CHANGED";
+               default:
+                       return "Unknown Event";
+               }
+}
+
+static gboolean avrcp_get_playback_status(gpointer user_data)
+{
+       struct avrcp *session = user_data;
+
+       avrcp_get_play_status(session);
+
+       return TRUE;
+}
+#endif
+
+static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
+                                       uint8_t code, uint8_t subunit,
                                        uint8_t *operands, size_t operand_count,
                                        void *user_data)
 {
@@ -3751,12 +4048,15 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code,
                uint8_t event = pdu->params[1 + count];
 
                events |= (1 << event);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               DBG("Supported Event %s", avrcp_event_to_string(event));
+#endif
                switch (event) {
                case AVRCP_EVENT_STATUS_CHANGED:
                case AVRCP_EVENT_TRACK_CHANGED:
                case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
                case AVRCP_EVENT_SETTINGS_CHANGED:
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
                case AVRCP_EVENT_UIDS_CHANGED:
                case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
@@ -3765,6 +4065,7 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code,
                                                !session->controller->player)
                                break;
                case AVRCP_EVENT_VOLUME_CHANGED:
+#endif
                        avrcp_register_notification(session, event);
                        break;
                }
@@ -3781,7 +4082,12 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code,
 
        if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED)))
                avrcp_get_element_attributes(session);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if ((events & (1 << AVRCP_EVENT_STATUS_CHANGED)) == 0) {
+               session->playback_status_id = g_timeout_add_seconds(1,
+                               avrcp_get_playback_status, session);
+       }
+#endif
        return FALSE;
 }
 
@@ -3893,6 +4199,7 @@ static void avrcp_connect_browsing(struct avrcp *session)
                                                        session);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
 static void target_init(struct avrcp *session)
 {
        struct avrcp_server *server = session->server;
@@ -3919,13 +4226,23 @@ static void target_init(struct avrcp *session)
 
        session->supported_events |= (1 << AVRCP_EVENT_STATUS_CHANGED) |
                                (1 << AVRCP_EVENT_TRACK_CHANGED) |
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                                (1 << AVRCP_EVENT_TRACK_REACHED_START) |
                                (1 << AVRCP_EVENT_TRACK_REACHED_END) |
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                               (1 << AVRCP_EVENT_PLAYBACK_POS_CHANGED) |
+#endif
                                (1 << AVRCP_EVENT_SETTINGS_CHANGED);
 
        if (target->version < 0x0104)
                return;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (adapter_avrcp_tg_ver < 0x0104)
+               return;
+#endif
+
        session->supported_events |=
                                (1 << AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED) |
                                (1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED) |
@@ -3940,7 +4257,9 @@ static void target_init(struct avrcp *session)
 
        avrcp_connect_browsing(session);
 }
+#endif
 
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
 static void controller_init(struct avrcp *session)
 {
        struct avrcp_player *player;
@@ -3955,6 +4274,11 @@ static void controller_init(struct avrcp *session)
 
        DBG("%p version 0x%04x", controller, controller->version);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if ((controller->version >= 0x0104) && (adapter_avrcp_ct_ver >= 0x0104))
+               session->supported_events |= (1 << AVRCP_EVENT_VOLUME_CHANGED);
+#endif
+
        service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
        btd_service_connecting_complete(service, 0);
 
@@ -3973,11 +4297,17 @@ static void controller_init(struct avrcp *session)
        if (controller->version < 0x0104)
                return;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (adapter_avrcp_ct_ver < 0x0104)
+               return;
+#endif
+
        if (!(controller->features & AVRCP_FEATURE_BROWSING))
                return;
 
        avrcp_connect_browsing(session);
 }
+#endif
 
 static void session_init_control(struct avrcp *session)
 {
@@ -3991,12 +4321,14 @@ static void session_init_control(struct avrcp *session)
                                                        handle_vendordep_pdu,
                                                        session);
        session->control_handlers = control_handlers;
-
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
        if (btd_device_get_service(session->dev, AVRCP_TARGET_UUID) != NULL)
                controller_init(session);
-
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
        if (btd_device_get_service(session->dev, AVRCP_REMOTE_UUID) != NULL)
                target_init(session);
+#endif
 }
 
 static void controller_destroy(struct avrcp *session)
@@ -4030,6 +4362,14 @@ static void session_destroy(struct avrcp *session, int err)
 
        server->sessions = g_slist_remove(server->sessions, session);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (session->playback_status_id > 0) {
+               DBG("Removing the timer for playback status polling");
+               g_source_remove(session->playback_status_id);
+               session->playback_status_id = 0;
+       }
+#endif
+
        session_abort_pending_pdu(session);
 
        service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
@@ -4350,9 +4690,19 @@ static int avrcp_connect(struct btd_service *service)
 {
        struct btd_device *dev = btd_service_get_device(service);
        const char *path = device_get_path(dev);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char name[10];
+#endif
        DBG("path %s", path);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       device_get_name(dev, name, sizeof(name));
+       DBG("name : %s", name);
+       if (g_str_equal(name, "PLT_M50")) {
+               DBG("Don't initiate avrcp connection with this headset");
+               return -ENOTSUP;
+       }
+#endif
        return control_connect(service);
 }
 
@@ -4380,6 +4730,7 @@ static void avrcp_target_remove(struct btd_service *service)
        control_unregister(service);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
 static void avrcp_target_server_remove(struct btd_profile *p,
                                                struct btd_adapter *adapter)
 {
@@ -4399,7 +4750,9 @@ static void avrcp_target_server_remove(struct btd_profile *p,
        if (server->ct_record_id == 0)
                avrcp_server_unregister(server);
 }
+#endif
 
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
 static int avrcp_target_server_probe(struct btd_profile *p,
                                                struct btd_adapter *adapter)
 {
@@ -4434,6 +4787,7 @@ done:
 
        return 0;
 }
+#endif
 
 static struct btd_profile avrcp_target_profile = {
        .name           = "audio-avrcp-target",
@@ -4444,9 +4798,10 @@ static struct btd_profile avrcp_target_profile = {
 
        .connect        = avrcp_connect,
        .disconnect     = avrcp_disconnect,
-
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
        .adapter_probe  = avrcp_target_server_probe,
        .adapter_remove = avrcp_target_server_remove,
+#endif
 };
 
 static int avrcp_controller_probe(struct btd_service *service)
@@ -4463,6 +4818,7 @@ static void avrcp_controller_remove(struct btd_service *service)
        control_unregister(service);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
 static void avrcp_controller_server_remove(struct btd_profile *p,
                                                struct btd_adapter *adapter)
 {
@@ -4482,7 +4838,9 @@ static void avrcp_controller_server_remove(struct btd_profile *p,
        if (server->tg_record_id == 0)
                avrcp_server_unregister(server);
 }
+#endif
 
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
 static int avrcp_controller_server_probe(struct btd_profile *p,
                                                struct btd_adapter *adapter)
 {
@@ -4517,6 +4875,7 @@ done:
 
        return 0;
 }
+#endif
 
 static struct btd_profile avrcp_controller_profile = {
        .name           = "avrcp-controller",
@@ -4527,9 +4886,10 @@ static struct btd_profile avrcp_controller_profile = {
 
        .connect        = avrcp_connect,
        .disconnect     = avrcp_disconnect,
-
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
        .adapter_probe  = avrcp_controller_server_probe,
        .adapter_remove = avrcp_controller_server_remove,
+#endif
 };
 
 static int avrcp_init(void)
index 23d1561..e7a16d7 100755 (executable)
 #include "src/dbus-common.h"
 #include "src/profile.h"
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "src/service.h"
+#endif
+
 #include "src/uuid-helper.h"
 #include "src/log.h"
 #include "src/error.h"
 #include "transport.h"
 #include "a2dp.h"
 #include "avrcp.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "sink.h"
+#endif
 
 #define MEDIA_INTERFACE "org.bluez.Media1"
 #define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1"
 #define MEDIA_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player"
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define A2DP_SINK_ROLE "sink"
+#define A2DP_SOURCE_ROLE "source"
+#endif
+
 #define REQUEST_TIMEOUT (3 * 1000)             /* 3 seconds */
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define SINK_SUSPEND_TIMEOUT 4         /* 4 seconds */
+
+unsigned int suspend_timer_id = 0;
+static gboolean a2dp_sink_support = false;
+static gboolean a2dp_source_support = true;
+#endif
+
 struct media_adapter {
        struct btd_adapter      *btd_adapter;
        GSList                  *endpoints;     /* Endpoints list */
@@ -102,6 +122,9 @@ struct media_player {
        guint                   watch;
        guint                   properties_watch;
        guint                   seek_watch;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       guint                   sink_watch;
+#endif
        char                    *status;
        uint32_t                position;
        uint32_t                duration;
@@ -117,6 +140,26 @@ struct media_player {
 
 static GSList *adapters = NULL;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean set_avrcp_status = FALSE;
+static gboolean send_track_changed_event = FALSE;
+
+gboolean current_delay_reporting = false;
+struct media_endpoint *source_endpoint = NULL;
+struct media_endpoint *sink_endpoint = NULL;
+
+static int media_set_sink_callback(struct btd_device *device,
+                               struct media_player *mp);
+static void media_sink_state_changed_cb(struct btd_service *service,
+                               sink_state_t old_state,
+                               sink_state_t new_state,
+                               void *user_data);
+void media_stop_suspend_timer(void);
+struct media_player *media_adapter_get_player(struct media_adapter *adapter);
+static struct media_adapter *find_adapter(struct btd_device *device);
+static uint32_t get_position(void *user_data);
+#endif
+
 static void endpoint_request_free(struct endpoint_request *request)
 {
        if (request->call)
@@ -199,7 +242,9 @@ static void media_endpoint_remove(struct media_endpoint *endpoint)
 
        if (endpoint->sep) {
                a2dp_remove_sep(endpoint->sep);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                return;
+#endif
        }
 
        info("Endpoint unregistered: sender=%s path=%s", endpoint->sender,
@@ -228,6 +273,9 @@ static void clear_configuration(struct media_endpoint *endpoint,
 {
        DBusMessage *msg;
        const char *path;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct media_player *mp;
+#endif
 
        msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
                                                MEDIA_ENDPOINT_INTERFACE,
@@ -243,6 +291,14 @@ static void clear_configuration(struct media_endpoint *endpoint,
        g_dbus_send_message(btd_get_dbus_connection(), msg);
 done:
        endpoint->transports = g_slist_remove(endpoint->transports, transport);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if ((mp = media_adapter_get_player(endpoint->adapter)))
+               if (mp->sink_watch) {
+                       sink_remove_state_cb(mp->sink_watch);
+                       mp->sink_watch = 0;
+               }
+       media_stop_suspend_timer();
+#endif
        media_transport_destroy(transport);
 }
 
@@ -405,6 +461,141 @@ static struct media_transport *find_device_transport(
        return match->data;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct media_player * media_adapter_get_player(struct media_adapter * adapter)
+{
+       GSList *l;
+       DBG(" ");
+
+       for (l = adapter->players; l; l = l->next) {
+               struct media_player *mp = l->data;
+               if (mp != NULL)
+                       return mp;
+       }
+       return NULL;
+}
+
+void media_stop_suspend_timer(void)
+{
+       if (suspend_timer_id > 0) {
+               DBG("Removing sink suspend timer");
+               g_source_remove(suspend_timer_id);
+               suspend_timer_id = 0;
+       }
+}
+
+gboolean media_reset_mp_status(gpointer user_data)
+{
+       struct media_player *mp = user_data;
+       DBG(" ");
+
+       /* PlayBackStatus already reset; so return */
+       if (g_strcmp0(mp->status, "playing") != 0)
+               return FALSE;
+
+       mp->position = get_position(mp);
+       g_timer_start(mp->timer);
+
+       g_free(mp->status);
+       mp->status = g_strdup("paused");
+       suspend_timer_id = 0;
+       avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, mp->status);
+
+       return FALSE;
+}
+
+static void media_sink_state_changed_cb(struct btd_service *service,
+                                       sink_state_t old_state,
+                                       sink_state_t new_state,
+                                       void *user_data)
+{
+       struct media_player *mp = user_data;
+       DBG(" ");
+
+       /* Check if A2DP streaming is suspended */
+       if ((old_state == SINK_STATE_PLAYING) &&
+               (new_state == SINK_STATE_CONNECTED)) {
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               struct btd_device *device = btd_service_get_device(service);
+               char name[20] = {0,};
+#endif
+
+               /* Check AVRCP play back status */
+               if (g_strcmp0(mp->status, "playing") != 0)
+                       return;
+
+               media_stop_suspend_timer();
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               device_get_name(device, name, sizeof(name));
+               DBG("Name : %s", name);
+
+               if (g_str_has_prefix(name, "LG HBS") != TRUE) {
+#endif
+               /* PlayBackStatus is still PLAYING; start a timer */
+               suspend_timer_id = g_timeout_add_seconds(SINK_SUSPEND_TIMEOUT,
+                               media_reset_mp_status, mp);
+               DBG("SINK SUSPEND TIMEOUT started");
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               }
+#endif
+       }
+
+       /* Check if A2DP streaming is started */
+       if ((old_state == SINK_STATE_CONNECTED) &&
+               (new_state == SINK_STATE_PLAYING)) {
+
+               struct btd_device *device = btd_service_get_device(service);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               char name[20] = {0,};
+#else
+               char name[20];
+#endif
+
+               media_stop_suspend_timer();
+
+               /* NULL packet streaming during initial connection */
+               if (set_avrcp_status == FALSE) {
+                       set_avrcp_status = TRUE;
+                       return;
+               }
+
+               /* Check for BMW, Audi, VW car kit */
+               device_get_name(device, name, sizeof(name));
+               DBG("Name : %s", name);
+               if ((g_str_has_prefix(name, "BMW") == TRUE) ||
+                               (g_str_has_prefix(name, "Audi") == TRUE) ||
+                               (g_str_has_prefix(name, "VW BT") == TRUE)) {
+
+                       /* Check AVRCP play back status */
+                       if (g_strcmp0(mp->status, "playing") == 0)
+                               return;
+
+                       g_free(mp->status);
+                       mp->status = g_strdup("playing");
+                       avrcp_player_event(mp->player,
+                               AVRCP_EVENT_STATUS_CHANGED, mp->status);
+               }
+       }
+}
+
+static int media_set_sink_callback(struct btd_device *device,
+                                       struct media_player *mp)
+{
+       struct btd_service *service;
+       DBG(" ");
+
+       service = btd_device_get_service(device, A2DP_SINK_UUID);
+       if (service == NULL)
+               return -EINVAL;
+
+       mp->sink_watch = sink_add_state_cb(service, media_sink_state_changed_cb, mp);
+
+       return 0;
+}
+#endif
+
 struct a2dp_config_data {
        struct a2dp_setup *setup;
        a2dp_endpoint_config_t cb;
@@ -423,6 +614,10 @@ static gboolean set_configuration(struct media_endpoint *endpoint,
        const char *path;
        DBusMessageIter iter;
        struct media_transport *transport;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct media_adapter *adapter;
+       struct media_player *mp;
+#endif
 
        transport = find_device_transport(endpoint, device);
 
@@ -443,6 +638,13 @@ static gboolean set_configuration(struct media_endpoint *endpoint,
                return FALSE;
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       set_avrcp_status = FALSE;
+       adapter = find_adapter(device);
+       if ((mp = media_adapter_get_player(adapter)))
+               media_set_sink_callback(device, mp);
+#endif
+
        endpoint->transports = g_slist_append(endpoint->transports, transport);
 
        dbus_message_iter_init_append(msg, &iter);
@@ -600,7 +802,11 @@ static gboolean endpoint_init_a2dp_source(struct media_endpoint *endpoint,
        endpoint->sep = a2dp_add_sep(endpoint->adapter->btd_adapter,
                                        AVDTP_SEP_TYPE_SOURCE, endpoint->codec,
                                        delay_reporting, &a2dp_endpoint,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                                       endpoint, NULL, err);
+#else
                                        endpoint, a2dp_destroy_endpoint, err);
+#endif
        if (endpoint->sep == NULL)
                return FALSE;
 
@@ -614,7 +820,11 @@ static gboolean endpoint_init_a2dp_sink(struct media_endpoint *endpoint,
        endpoint->sep = a2dp_add_sep(endpoint->adapter->btd_adapter,
                                        AVDTP_SEP_TYPE_SINK, endpoint->codec,
                                        delay_reporting, &a2dp_endpoint,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                                       endpoint, NULL, err);
+#else
                                        endpoint, a2dp_destroy_endpoint, err);
+#endif
        if (endpoint->sep == NULL)
                return FALSE;
 
@@ -744,13 +954,31 @@ static struct media_endpoint *media_endpoint_create(struct media_adapter *adapte
 
        endpoint->adapter = adapter;
 
-       if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0)
+       if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               source_endpoint = endpoint;
+               if (btd_adapter_get_a2dp_role(adapter->btd_adapter) == BLUETOOTH_A2DP_SINK_ROLE)
+                       return endpoint;
+               else
+                       succeeded = endpoint_init_a2dp_source(endpoint,
+                                                       delay_reporting, err);
+#else
                succeeded = endpoint_init_a2dp_source(endpoint,
                                                        delay_reporting, err);
-       else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0)
+#endif
+       } else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               sink_endpoint = endpoint;
+               if (btd_adapter_get_a2dp_role(adapter->btd_adapter) == BLUETOOTH_A2DP_SOURCE_ROLE)
+                       return endpoint;
+               else
+                       succeeded = endpoint_init_a2dp_sink(endpoint,
+                                                       delay_reporting, err);
+#else
                succeeded = endpoint_init_a2dp_sink(endpoint,
                                                        delay_reporting, err);
-       else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
+#endif
+       } else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
                                        strcasecmp(uuid, HSP_AG_UUID) == 0)
                succeeded = TRUE;
        else if (strcasecmp(uuid, HFP_HS_UUID) == 0 ||
@@ -787,6 +1015,36 @@ static struct media_endpoint *media_endpoint_create(struct media_adapter *adapte
        return endpoint;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static int parse_a2dp_uuid(DBusMessageIter *props, const char **uuid)
+{
+       gboolean has_uuid = FALSE;
+
+       while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
+               const char *key;
+               DBusMessageIter value, entry;
+               int var;
+
+               dbus_message_iter_recurse(props, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               var = dbus_message_iter_get_arg_type(&value);
+               if (strcasecmp(key, "UUID") == 0) {
+                       if (var != DBUS_TYPE_STRING)
+                               return -EINVAL;
+                       dbus_message_iter_get_basic(&value, uuid);
+                       has_uuid = TRUE;
+               }
+               dbus_message_iter_next(props);
+       }
+
+       return has_uuid ? 0 : -EINVAL;
+}
+#endif
+
 static int parse_properties(DBusMessageIter *props, const char **uuid,
                                gboolean *delay_reporting, uint8_t *codec,
                                uint8_t **capabilities, int *size)
@@ -837,6 +1095,42 @@ static int parse_properties(DBusMessageIter *props, const char **uuid,
        return (has_uuid && has_codec) ? 0 : -EINVAL;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *a2dp_select_role(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct media_adapter *adapter = data;
+       DBusMessageIter args, props;
+       const char *a2dp_role;
+       gboolean ret;
+       int err;
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_STRING, &a2dp_role,
+                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       if (!g_strcmp0(a2dp_role, A2DP_SINK_ROLE)) {
+               btd_adapter_set_a2dp_role(adapter->btd_adapter, BLUETOOTH_A2DP_SINK_ROLE);
+               a2dp_remove_sep(source_endpoint->sep);
+               ret = endpoint_init_a2dp_sink(sink_endpoint, current_delay_reporting, NULL);
+               if (!ret)
+                       DBG("could not init a2dp sink");
+       } else if (!g_strcmp0(a2dp_role, A2DP_SOURCE_ROLE)) {
+               btd_adapter_set_a2dp_role(adapter->btd_adapter, BLUETOOTH_A2DP_SOURCE_ROLE);
+               a2dp_remove_sep(sink_endpoint->sep);
+               ret = endpoint_init_a2dp_source(source_endpoint, current_delay_reporting, NULL);
+               if (!ret)
+                       DBG("could not init a2dp source");
+       } else {
+               DBG("invalid a2dp role");
+               return btd_error_invalid_args(msg);
+       }
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+#endif
+
 static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,
                                        void *data)
 {
@@ -848,7 +1142,15 @@ static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,
        uint8_t *capabilities;
        int size = 0;
        int err;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (btd_adapter_get_a2dp_role(adapter->btd_adapter) == BLUETOOTH_A2DP_SINK_ROLE) {
+               a2dp_sink_support = true;
+               a2dp_source_support = false;
+       } else {
+               a2dp_sink_support = false;
+               a2dp_source_support = true;
+       }
+#endif
        sender = dbus_message_get_sender(msg);
 
        dbus_message_iter_init(msg, &args);
@@ -866,7 +1168,9 @@ static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,
        if (parse_properties(&props, &uuid, &delay_reporting, &codec,
                                                &capabilities, &size) < 0)
                return btd_error_invalid_args(msg);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       current_delay_reporting = delay_reporting;
+#endif
        if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting,
                                codec, capabilities, size, &err) == NULL) {
                if (err == -EPROTONOSUPPORT)
@@ -961,6 +1265,13 @@ static void media_player_free(gpointer data)
        if (mp->settings)
                g_hash_table_unref(mp->settings);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       media_stop_suspend_timer();
+
+       if (mp->sink_watch)
+               sink_remove_state_cb(mp->sink_watch);
+#endif
+
        g_timer_destroy(mp->timer);
        g_free(mp->sender);
        g_free(mp->path);
@@ -1126,6 +1437,11 @@ static uint64_t get_uid(void *user_data)
        if (mp->track == NULL)
                return UINT64_MAX;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (!g_hash_table_lookup(mp->track, "Title"))
+               return UINT64_MAX;
+#endif
+
        return 0;
 }
 
@@ -1304,6 +1620,9 @@ static void media_player_exit(DBusConnection *connection, void *user_data)
 static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
 {
        const char *value;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint32_t playback_position;
+#endif
 
        if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
                return FALSE;
@@ -1321,26 +1640,49 @@ static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
        mp->status = g_strdup(value);
 
        avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, mp->status);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (strcasecmp(mp->status, "reverse-seek") != 0 &&
+               strcasecmp(mp->status, "playing") != 0) {
+               playback_position = get_position(mp);
+               avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED,
+                                                       &playback_position);
+       }
+#endif
 
        return TRUE;
 }
 
 static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint32_t value;
+#else
        uint64_t value;
        const char *status;
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint32_t playback_position;
+#endif
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INT64)
-                       return FALSE;
-
+               return FALSE;
+#else
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
+               return FALSE;
+#endif
        dbus_message_iter_get_basic(iter, &value);
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        value /= 1000;
-
+#endif
+       DBG("Value %d", value);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (value > get_position(mp))
                status = "forward-seek";
        else
                status = "reverse-seek";
+#endif
 
        mp->position = value;
        g_timer_start(mp->timer);
@@ -1350,6 +1692,12 @@ static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
        if (!mp->position) {
                avrcp_player_event(mp->player,
                                        AVRCP_EVENT_TRACK_REACHED_START, NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               playback_position = get_position(mp);
+               avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED,
+                                               &playback_position);
+#endif
+
                return TRUE;
        }
 
@@ -1360,11 +1708,23 @@ static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
        if (mp->position == UINT32_MAX || mp->position >= mp->duration) {
                avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_END,
                                                                        NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               playback_position = get_position(mp);
+               avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED,
+                                               &playback_position);
+#endif
                return TRUE;
        }
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        /* Send a status change to force resync the position */
        avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, status);
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       playback_position = get_position(mp);
+       avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED,
+                                               &playback_position);
+#endif
 
        return TRUE;
 }
@@ -1372,6 +1732,15 @@ static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
 static void set_metadata(struct media_player *mp, const char *key,
                                                        const char *value)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       const char *current_value = NULL;
+
+       current_value = g_hash_table_lookup(mp->track, key);
+
+       if ((g_strcmp0(value, current_value) != 0) &&
+               (send_track_changed_event == FALSE))
+               send_track_changed_event = TRUE;
+#endif
        DBG("%s=%s", key, value);
        g_hash_table_replace(mp->track, g_strdup(key), g_strdup(value));
 }
@@ -1431,7 +1800,9 @@ static gboolean parse_int64_metadata(struct media_player *mp, const char *key,
        dbus_message_iter_get_basic(iter, &value);
 
        if (strcasecmp(key, "Duration") == 0) {
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                value /= 1000;
+#endif
                mp->duration = value;
        }
 
@@ -1472,6 +1843,9 @@ static gboolean parse_player_metadata(struct media_player *mp,
        int ctype;
        gboolean title = FALSE;
        uint64_t uid;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint32_t playback_position;
+#endif
 
        ctype = dbus_message_iter_get_arg_type(iter);
        if (ctype != DBUS_TYPE_ARRAY)
@@ -1479,11 +1853,13 @@ static gboolean parse_player_metadata(struct media_player *mp,
 
        dbus_message_iter_recurse(iter, &dict);
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (mp->track != NULL)
                g_hash_table_unref(mp->track);
 
        mp->track = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
                                                                g_free);
+#endif
 
        while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
                                                        DBUS_TYPE_INVALID) {
@@ -1521,6 +1897,11 @@ static gboolean parse_player_metadata(struct media_player *mp,
                } else if (strcasecmp(key, "mpris:length") == 0) {
                        if (!parse_int64_metadata(mp, "Duration", &var))
                                return FALSE;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               } else if (strcasecmp(key, "xesam:totalTracks") == 0) {
+                       if (!parse_int32_metadata(mp, "NumberOfTracks", &var))
+                               return FALSE;
+#endif
                } else if (strcasecmp(key, "xesam:trackNumber") == 0) {
                        if (!parse_int32_metadata(mp, "TrackNumber", &var))
                                return FALSE;
@@ -1534,13 +1915,25 @@ static gboolean parse_player_metadata(struct media_player *mp,
                g_hash_table_insert(mp->track, g_strdup("Title"),
                                                                g_strdup(""));
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (send_track_changed_event) {
+               uid = get_uid(mp);
+               avrcp_player_event(mp->player,
+                       AVRCP_EVENT_TRACK_CHANGED, &uid);
+               send_track_changed_event = FALSE;
+
+               playback_position = get_position(mp);
+               avrcp_player_event(mp->player,
+                       AVRCP_EVENT_PLAYBACK_POS_CHANGED, &playback_position);
+       }
+#else
        mp->position = 0;
        g_timer_start(mp->timer);
        uid = get_uid(mp);
 
        avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, &uid);
        avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_START, NULL);
-
+#endif
        return TRUE;
 }
 
@@ -1791,6 +2184,10 @@ static struct media_player *media_player_create(struct media_adapter *adapter,
 
        mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
                                                                g_free);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       mp->track = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+                                                               g_free);
+#endif
 
        adapter->players = g_slist_append(adapter->players, mp);
 
@@ -1871,6 +2268,10 @@ static const GDBusMethodTable media_methods[] = {
                NULL, register_player) },
        { GDBUS_METHOD("UnregisterPlayer",
                GDBUS_ARGS({ "player", "o" }), NULL, unregister_player) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { GDBUS_METHOD("SelectRole",
+               GDBUS_ARGS({ "role", "s" }), NULL, a2dp_select_role) },
+#endif
        { },
 };
 
index 7944b49..f39b9ae 100755 (executable)
@@ -534,7 +534,83 @@ static DBusMessage *media_player_previous(DBusConnection *conn,
 
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *media_player_press_fast_forward(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       DBG("+");
+       struct media_player *mp = data;
+       struct player_callback *cb = mp->cb;
+       int err;
+
+       if (cb->cbs->press_fast_forward == NULL)
+               return btd_error_not_supported(msg);
+
+       err = cb->cbs->press_fast_forward(mp, cb->user_data);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       DBG("-");
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *media_player_release_fast_forward(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       DBG("+");
+       struct media_player *mp = data;
+       struct player_callback *cb = mp->cb;
+       int err;
+
+       if (cb->cbs->release_fast_forward == NULL)
+               return btd_error_not_supported(msg);
+
+       err = cb->cbs->release_fast_forward(mp, cb->user_data);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       DBG("-");
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *media_player_press_rewind(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       DBG("+");
+       struct media_player *mp = data;
+       struct player_callback *cb = mp->cb;
+       int err;
+
+       if (cb->cbs->press_rewind == NULL)
+               return btd_error_not_supported(msg);
+
+       err = cb->cbs->press_rewind(mp, cb->user_data);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       DBG("-");
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *media_player_release_rewind(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       DBG("+");
+       struct media_player *mp = data;
+       struct player_callback *cb = mp->cb;
+       int err;
+
+       if (cb->cbs->release_rewind == NULL)
+               return btd_error_not_supported(msg);
+
+       err = cb->cbs->release_rewind(mp, cb->user_data);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
 
+       DBG("-");
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+#else
 static DBusMessage *media_player_fast_forward(DBusConnection *conn,
                                                DBusMessage *msg, void *data)
 {
@@ -568,7 +644,7 @@ static DBusMessage *media_player_rewind(DBusConnection *conn, DBusMessage *msg,
 
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
-
+#endif
 static void parse_folder_list(gpointer data, gpointer user_data)
 {
        struct media_item *item = data;
@@ -725,8 +801,15 @@ static const GDBusMethodTable media_player_methods[] = {
        { GDBUS_METHOD("Stop", NULL, NULL, media_player_stop) },
        { GDBUS_METHOD("Next", NULL, NULL, media_player_next) },
        { GDBUS_METHOD("Previous", NULL, NULL, media_player_previous) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { GDBUS_METHOD("PressFastForward", NULL, NULL, media_player_press_fast_forward) },
+       { GDBUS_METHOD("ReleaseFastForward", NULL, NULL, media_player_release_fast_forward) },
+       { GDBUS_METHOD("PressRewind", NULL, NULL, media_player_press_rewind) },
+       { GDBUS_METHOD("ReleaseRewind", NULL, NULL, media_player_release_rewind) },
+#else
        { GDBUS_METHOD("FastForward", NULL, NULL, media_player_fast_forward) },
        { GDBUS_METHOD("Rewind", NULL, NULL, media_player_rewind) },
+#endif
        { }
 };
 
@@ -1328,18 +1411,37 @@ void media_player_set_metadata(struct media_player *mp,
                                struct media_item *item, const char *key,
                                void *data, size_t len)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char *value;
+       char *end, *temp;
+#else
        char *value, *curval;
+#endif
 
        value = g_strndup(data, len);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       temp = value;
+       while (g_utf8_validate(temp, -1, (const gchar **)&end) == FALSE) {
+               temp = g_utf8_find_next_char(end, NULL);
+               if (temp == NULL) {
+                       *end = '\0';
+                       break;
+               }
+               strcpy(end, temp);
+               temp = end;
+       }
+#endif
+
        DBG("%s: %s", key, value);
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        curval = g_hash_table_lookup(mp->track, key);
        if (g_strcmp0(curval, value) == 0) {
                g_free(value);
                return;
        }
-
+#endif
        if (mp->process_id == 0) {
                g_hash_table_remove_all(mp->track);
                mp->process_id = g_idle_add(process_metadata_changed, mp);
index 54e395a..21eab49 100755 (executable)
@@ -52,8 +52,15 @@ struct media_player_callback {
        int (*stop) (struct media_player *mp, void *user_data);
        int (*next) (struct media_player *mp, void *user_data);
        int (*previous) (struct media_player *mp, void *user_data);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       int (*press_fast_forward) (struct media_player *mp, void *user_data);
+       int (*release_fast_forward) (struct media_player *mp, void *user_data);
+       int (*press_rewind) (struct media_player *mp, void *user_data);
+       int (*release_rewind) (struct media_player *mp, void *user_data);
+#else
        int (*fast_forward) (struct media_player *mp, void *user_data);
        int (*rewind) (struct media_player *mp, void *user_data);
+#endif
        int (*list_items) (struct media_player *mp, const char *name,
                                uint32_t start, uint32_t end, void *user_data);
        int (*change_folder) (struct media_player *mp, const char *path,
index 7cac210..332d127 100755 (executable)
@@ -106,9 +106,17 @@ static void sink_set_state(struct sink *sink, sink_state_t new_state)
        if (new_state != SINK_STATE_DISCONNECTED)
                return;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       btd_service_disconnecting_complete(service, 0);
+#endif
+
        if (sink->session) {
                avdtp_unref(sink->session);
                sink->session = NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               sink->connect_id = 0;
+               sink->disconnect_id = 0;
+#endif
        }
 }
 
@@ -148,7 +156,9 @@ static void stream_state_changed(struct avdtp_stream *stream,
 
        switch (new_state) {
        case AVDTP_STATE_IDLE:
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                btd_service_disconnecting_complete(sink->service, 0);
+#endif
 
                if (sink->disconnect_id > 0) {
                        a2dp_cancel(sink->disconnect_id);
@@ -274,6 +284,7 @@ int sink_connect(struct btd_service *service)
 {
        struct sink *sink = btd_service_get_user_data(service);
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (!sink->session)
                sink->session = a2dp_avdtp_get(btd_service_get_device(service));
 
@@ -281,6 +292,7 @@ int sink_connect(struct btd_service *service)
                DBG("Unable to get a session");
                return -EIO;
        }
+#endif
 
        if (sink->connect_id > 0 || sink->disconnect_id > 0)
                return -EBUSY;
@@ -291,6 +303,16 @@ int sink_connect(struct btd_service *service)
        if (sink->stream_state >= AVDTP_STATE_OPEN)
                return -EALREADY;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (!sink->session)
+               sink->session = a2dp_avdtp_get(btd_service_get_device(service));
+
+       if (!sink->session) {
+               DBG("Unable to get a session");
+               return -EIO;
+       }
+#endif
+
        if (!sink_setup_stream(service, NULL)) {
                DBG("Failed to create a stream");
                return -EIO;
@@ -309,8 +331,16 @@ static void sink_free(struct btd_service *service)
                avdtp_stream_remove_cb(sink->session, sink->stream,
                                        sink->cb_id);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (sink->session) {
+               /* We need to clear the avdtp discovery procedure */
+               finalize_discovery(sink->session, ECANCELED);
+               avdtp_unref(sink->session);
+       }
+#else
        if (sink->session)
                avdtp_unref(sink->session);
+#endif
 
        if (sink->connect_id > 0) {
                btd_service_connecting_complete(sink->service, -ECANCELED);
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
new file mode 100755 (executable)
index 0000000..e447725
--- /dev/null
@@ -0,0 +1,1266 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Tieto Poland
+ *
+ *  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 <errno.h>
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/dbus-common.h"
+#include "src/shared/util.h"
+#include "src/error.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "src/attio.h"
+#include "src/log.h"
+
+/* min length for ATT indication or notification: opcode (1b) + handle (2b) */
+#define ATT_HDR_LEN 3
+
+#define ATT_TIMEOUT 30
+
+#define CYCLINGSPEED_INTERFACE         "org.bluez.CyclingSpeed1"
+#define CYCLINGSPEED_MANAGER_INTERFACE "org.bluez.CyclingSpeedManager1"
+#define CYCLINGSPEED_WATCHER_INTERFACE "org.bluez.CyclingSpeedWatcher1"
+
+#define WHEEL_REV_SUPPORT              0x01
+#define CRANK_REV_SUPPORT              0x02
+#define MULTI_SENSOR_LOC_SUPPORT       0x04
+
+#define WHEEL_REV_PRESENT      0x01
+#define CRANK_REV_PRESENT      0x02
+
+#define SET_CUMULATIVE_VALUE           0x01
+#define START_SENSOR_CALIBRATION       0x02
+#define UPDATE_SENSOR_LOC              0x03
+#define REQUEST_SUPPORTED_SENSOR_LOC   0x04
+#define RESPONSE_CODE                  0x10
+
+#define RSP_SUCCESS            0x01
+#define RSP_NOT_SUPPORTED      0x02
+#define RSP_INVALID_PARAM      0x03
+#define RSP_FAILED             0x04
+
+struct csc;
+
+struct controlpoint_req {
+       struct csc              *csc;
+       uint8_t                 opcode;
+       guint                   timeout;
+       GDBusPendingReply       reply_id;
+       DBusMessage             *msg;
+
+       uint8_t                 pending_location;
+};
+
+struct csc_adapter {
+       struct btd_adapter      *adapter;
+       GSList                  *devices;       /* list of registered devices */
+       GSList                  *watchers;
+};
+
+struct csc {
+       struct btd_device       *dev;
+       struct csc_adapter      *cadapter;
+
+       GAttrib                 *attrib;
+       guint                   attioid;
+       /* attio id for measurement characteristics value notifications */
+       guint                   attio_measurement_id;
+       /* attio id for SC Control Point characteristics value indications */
+       guint                   attio_controlpoint_id;
+
+       struct att_range        *svc_range;
+
+       uint16_t                measurement_ccc_handle;
+       uint16_t                controlpoint_val_handle;
+
+       uint16_t                feature;
+       gboolean                has_location;
+       uint8_t                 location;
+       uint8_t                 num_locations;
+       uint8_t                 *locations;
+
+       struct controlpoint_req *pending_req;
+};
+
+struct watcher {
+       struct csc_adapter      *cadapter;
+       guint                   id;
+       char                    *srv;
+       char                    *path;
+};
+
+struct measurement {
+       struct csc      *csc;
+
+       bool            has_wheel_rev;
+       uint32_t        wheel_rev;
+       uint16_t        last_wheel_time;
+
+       bool            has_crank_rev;
+       uint16_t        crank_rev;
+       uint16_t        last_crank_time;
+};
+
+struct characteristic {
+       struct csc      *csc;
+       char            uuid[MAX_LEN_UUID_STR + 1];
+};
+
+static GSList *csc_adapters = NULL;
+
+static const char * const location_enum[] = {
+       "other", "top-of-shoe", "in-shoe", "hip", "front-wheel", "left-crank",
+       "right-crank", "left-pedal", "right-pedal", "front-hub",
+       "rear-dropout", "chainstay", "rear-wheel", "rear-hub"
+};
+
+static const char *location2str(uint8_t value)
+{
+       if (value < G_N_ELEMENTS(location_enum))
+               return location_enum[value];
+
+       info("Body Sensor Location [%d] is RFU", value);
+
+       return location_enum[0];
+}
+
+static int str2location(const char *location)
+{
+       size_t i;
+
+       for (i = 0; i < G_N_ELEMENTS(location_enum); i++)
+               if (!strcmp(location_enum[i], location))
+                       return i;
+
+       return -1;
+}
+
+static int cmp_adapter(gconstpointer a, gconstpointer b)
+{
+       const struct csc_adapter *cadapter = a;
+       const struct btd_adapter *adapter = b;
+
+       if (adapter == cadapter->adapter)
+               return 0;
+
+       return -1;
+}
+
+static int cmp_device(gconstpointer a, gconstpointer b)
+{
+       const struct csc *csc = a;
+       const struct btd_device *dev = b;
+
+       if (dev == csc->dev)
+               return 0;
+
+       return -1;
+}
+
+static int cmp_watcher(gconstpointer a, gconstpointer b)
+{
+       const struct watcher *watcher = a;
+       const struct watcher *match = b;
+       int ret;
+
+       ret = g_strcmp0(watcher->srv, match->srv);
+       if (ret != 0)
+               return ret;
+
+       return g_strcmp0(watcher->path, match->path);
+}
+
+static struct csc_adapter *find_csc_adapter(struct btd_adapter *adapter)
+{
+       GSList *l = g_slist_find_custom(csc_adapters, adapter, cmp_adapter);
+
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+static void destroy_watcher(gpointer user_data)
+{
+       struct watcher *watcher = user_data;
+
+       g_free(watcher->path);
+       g_free(watcher->srv);
+       g_free(watcher);
+}
+
+static struct watcher *find_watcher(GSList *list, const char *sender,
+                                                       const char *path)
+{
+       struct watcher *match;
+       GSList *l;
+
+       match = g_new0(struct watcher, 1);
+       match->srv = g_strdup(sender);
+       match->path = g_strdup(path);
+
+       l = g_slist_find_custom(list, match, cmp_watcher);
+       destroy_watcher(match);
+
+       if (l != NULL)
+               return l->data;
+
+       return NULL;
+}
+
+static void remove_watcher(gpointer user_data)
+{
+       struct watcher *watcher = user_data;
+
+       g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+}
+
+static void destroy_csc_adapter(gpointer user_data)
+{
+       struct csc_adapter *cadapter = user_data;
+
+       g_slist_free_full(cadapter->watchers, remove_watcher);
+
+       g_free(cadapter);
+}
+
+static void destroy_csc(gpointer user_data)
+{
+       struct csc *csc = user_data;
+
+       if (csc->attioid > 0)
+               btd_device_remove_attio_callback(csc->dev, csc->attioid);
+
+       if (csc->attrib != NULL) {
+               g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
+               g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
+               g_attrib_unref(csc->attrib);
+       }
+
+       btd_device_unref(csc->dev);
+       g_free(csc->svc_range);
+       g_free(csc->locations);
+       g_free(csc);
+}
+
+static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       char *msg = user_data;
+
+       if (status != 0)
+               error("%s failed", msg);
+
+       g_free(msg);
+}
+
+static gboolean controlpoint_timeout(gpointer user_data)
+{
+       struct controlpoint_req *req = user_data;
+
+       if (req->opcode == UPDATE_SENSOR_LOC) {
+               g_dbus_pending_property_error(req->reply_id,
+                                               ERROR_INTERFACE ".Failed",
+                                               "Operation failed (timeout)");
+       } else if (req->opcode == SET_CUMULATIVE_VALUE) {
+               DBusMessage *reply;
+
+               reply = btd_error_failed(req->msg,
+                                               "Operation failed (timeout)");
+
+               g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+               dbus_message_unref(req->msg);
+       }
+
+       req->csc->pending_req = NULL;
+       g_free(req);
+
+       return FALSE;
+}
+
+static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct controlpoint_req *req = user_data;
+
+       if (status == 0) {
+               req->timeout = g_timeout_add_seconds(ATT_TIMEOUT,
+                                                       controlpoint_timeout,
+                                                       req);
+               return;
+       }
+
+       error("SC Control Point write failed (opcode=%d)", req->opcode);
+
+       if (req->opcode == UPDATE_SENSOR_LOC) {
+               g_dbus_pending_property_error(req->reply_id,
+                                       ERROR_INTERFACE ".Failed",
+                                       "Operation failed (%d)", status);
+       } else if  (req->opcode == SET_CUMULATIVE_VALUE) {
+               DBusMessage *reply;
+
+               reply = btd_error_failed(req->msg, "Operation failed");
+
+               g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+               dbus_message_unref(req->msg);
+       }
+
+       req->csc->pending_req = NULL;
+       g_free(req);
+}
+
+static void read_supported_locations(struct csc *csc)
+{
+       struct controlpoint_req *req;
+
+       req = g_new0(struct controlpoint_req, 1);
+       req->csc = csc;
+       req->opcode = REQUEST_SUPPORTED_SENSOR_LOC;
+
+       csc->pending_req = req;
+
+       gatt_write_char(csc->attrib, csc->controlpoint_val_handle,
+                                       &req->opcode, sizeof(req->opcode),
+                                       controlpoint_write_cb, req);
+}
+
+static void read_feature_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct csc *csc = user_data;
+       uint8_t value[2];
+       ssize_t vlen;
+
+       if (status) {
+               error("CSC Feature read failed: %s", att_ecode2str(status));
+               return;
+       }
+
+       vlen = dec_read_resp(pdu, len, value, sizeof(value));
+       if (vlen < 0) {
+               error("Protocol error");
+               return;
+       }
+
+       if (vlen != sizeof(value)) {
+               error("Invalid value length for CSC Feature");
+               return;
+       }
+
+       csc->feature = get_le16(value);
+
+       if ((csc->feature & MULTI_SENSOR_LOC_SUPPORT)
+                                               && (csc->locations == NULL))
+               read_supported_locations(csc);
+}
+
+static void read_location_cb(guint8 status, const guint8 *pdu,
+                                               guint16 len, gpointer user_data)
+{
+       struct csc *csc = user_data;
+       uint8_t value;
+       ssize_t vlen;
+
+       if (status) {
+               error("Sensor Location read failed: %s", att_ecode2str(status));
+               return;
+       }
+
+       vlen = dec_read_resp(pdu, len, &value, sizeof(value));
+       if (vlen < 0) {
+               error("Protocol error");
+               return;
+       }
+
+       if (vlen != sizeof(value)) {
+               error("Invalid value length for Sensor Location");
+               return;
+       }
+
+       csc->has_location = TRUE;
+       csc->location = value;
+
+       g_dbus_emit_property_changed(btd_get_dbus_connection(),
+                                       device_get_path(csc->dev),
+                                       CYCLINGSPEED_INTERFACE, "Location");
+}
+
+static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data)
+{
+       struct characteristic *ch = user_data;
+       struct gatt_desc *desc;
+       uint8_t attr_val[2];
+       char *msg = NULL;
+
+       if (status != 0) {
+               error("Discover %s descriptors failed: %s", ch->uuid,
+                                                       att_ecode2str(status));
+               goto done;
+       }
+
+       /* There will be only one descriptor on list and it will be CCC */
+       desc = descs->data;
+
+       if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) {
+               ch->csc->measurement_ccc_handle = desc->handle;
+
+               if (g_slist_length(ch->csc->cadapter->watchers) == 0) {
+                       put_le16(0x0000, attr_val);
+                       msg = g_strdup("Disable measurement");
+               } else {
+                       put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT,
+                                                       attr_val);
+                       msg = g_strdup("Enable measurement");
+               }
+       } else if (g_strcmp0(ch->uuid, SC_CONTROL_POINT_UUID) == 0) {
+               put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, attr_val);
+               msg = g_strdup("Enable SC Control Point indications");
+       } else {
+               goto done;
+       }
+
+       gatt_write_char(ch->csc->attrib, desc->handle, attr_val,
+                                       sizeof(attr_val), char_write_cb, msg);
+
+done:
+       g_free(ch);
+}
+
+static void discover_desc(struct csc *csc, struct gatt_char *c,
+                                               struct gatt_char *c_next)
+{
+       struct characteristic *ch;
+       uint16_t start, end;
+       bt_uuid_t uuid;
+
+       start = c->value_handle + 1;
+
+       if (c_next != NULL) {
+               if (start == c_next->handle)
+                       return;
+               end = c_next->handle - 1;
+       } else if (c->value_handle != csc->svc_range->end) {
+               end = csc->svc_range->end;
+       } else {
+               return;
+       }
+
+       ch = g_new0(struct characteristic, 1);
+       ch->csc = csc;
+       memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
+
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+       gatt_discover_desc(csc->attrib, start, end, &uuid, discover_desc_cb,
+                                                                       ch);
+}
+
+static void update_watcher(gpointer data, gpointer user_data)
+{
+       struct watcher *w = data;
+       struct measurement *m = user_data;
+       struct csc *csc = m->csc;
+       const char *path = device_get_path(csc->dev);
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(w->srv, w->path,
+                       CYCLINGSPEED_WATCHER_INTERFACE, "MeasurementReceived");
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       if (m->has_wheel_rev) {
+               dict_append_entry(&dict, "WheelRevolutions",
+                                       DBUS_TYPE_UINT32, &m->wheel_rev);
+               dict_append_entry(&dict, "LastWheelEventTime",
+                                       DBUS_TYPE_UINT16, &m->last_wheel_time);
+       }
+
+       if (m->has_crank_rev) {
+               dict_append_entry(&dict, "CrankRevolutions",
+                                       DBUS_TYPE_UINT16, &m->crank_rev);
+               dict_append_entry(&dict, "LastCrankEventTime",
+                                       DBUS_TYPE_UINT16, &m->last_crank_time);
+       }
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       dbus_message_set_no_reply(msg, TRUE);
+       g_dbus_send_message(btd_get_dbus_connection(), msg);
+}
+
+static void process_measurement(struct csc *csc, const uint8_t *pdu,
+                                                               uint16_t len)
+{
+       struct measurement m;
+       uint8_t flags;
+
+       flags = *pdu;
+
+       pdu++;
+       len--;
+
+       memset(&m, 0, sizeof(m));
+
+       if ((flags & WHEEL_REV_PRESENT) && (csc->feature & WHEEL_REV_SUPPORT)) {
+               if (len < 6) {
+                       error("Wheel revolutions data fields missing");
+                       return;
+               }
+
+               m.has_wheel_rev = true;
+               m.wheel_rev = get_le32(pdu);
+               m.last_wheel_time = get_le16(pdu + 4);
+               pdu += 6;
+               len -= 6;
+       }
+
+       if ((flags & CRANK_REV_PRESENT) && (csc->feature & CRANK_REV_SUPPORT)) {
+               if (len < 4) {
+                       error("Crank revolutions data fields missing");
+                       return;
+               }
+
+               m.has_crank_rev = true;
+               m.crank_rev = get_le16(pdu);
+               m.last_crank_time = get_le16(pdu + 2);
+               pdu += 4;
+               len -= 4;
+       }
+
+       /* Notify all registered watchers */
+       m.csc = csc;
+       g_slist_foreach(csc->cadapter->watchers, update_watcher, &m);
+}
+
+static void measurement_notify_handler(const uint8_t *pdu, uint16_t len,
+                                                       gpointer user_data)
+{
+       struct csc *csc = user_data;
+
+       /* should be at least opcode (1b) + handle (2b) */
+       if (len < 3) {
+               error("Invalid PDU received");
+               return;
+       }
+
+       process_measurement(csc, pdu + 3, len - 3);
+}
+
+static void controlpoint_property_reply(struct controlpoint_req *req,
+                                                               uint8_t code)
+{
+       switch (code) {
+       case RSP_SUCCESS:
+               g_dbus_pending_property_success(req->reply_id);
+               break;
+
+       case RSP_NOT_SUPPORTED:
+               g_dbus_pending_property_error(req->reply_id,
+                                       ERROR_INTERFACE ".NotSupported",
+                                       "Feature is not supported");
+               break;
+
+       case RSP_INVALID_PARAM:
+               g_dbus_pending_property_error(req->reply_id,
+                                       ERROR_INTERFACE ".InvalidArguments",
+                                       "Invalid arguments in method call");
+               break;
+
+       case RSP_FAILED:
+               g_dbus_pending_property_error(req->reply_id,
+                                       ERROR_INTERFACE ".Failed",
+                                       "Operation failed");
+               break;
+
+       default:
+               g_dbus_pending_property_error(req->reply_id,
+                                       ERROR_INTERFACE ".Failed",
+                                       "Operation failed (%d)", code);
+               break;
+       }
+}
+
+static void controlpoint_method_reply(struct controlpoint_req *req,
+                                                               uint8_t code)
+{
+       DBusMessage *reply;
+
+       switch (code) {
+       case RSP_SUCCESS:
+               reply = dbus_message_new_method_return(req->msg);
+               break;
+       case RSP_NOT_SUPPORTED:
+               reply = btd_error_not_supported(req->msg);
+               break;
+       case RSP_INVALID_PARAM:
+               reply = btd_error_invalid_args(req->msg);
+               break;
+       case RSP_FAILED:
+               reply = btd_error_failed(req->msg, "Failed");
+               break;
+       default:
+               reply = btd_error_failed(req->msg, "Unknown error");
+               break;
+       }
+
+       g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+       dbus_message_unref(req->msg);
+}
+
+static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
+                                                       gpointer user_data)
+{
+       struct csc *csc = user_data;
+       struct controlpoint_req *req = csc->pending_req;
+       uint8_t opcode;
+       uint8_t req_opcode;
+       uint8_t rsp_code;
+       uint8_t *opdu;
+       uint16_t olen;
+       size_t plen;
+
+       if (len < ATT_HDR_LEN) {
+               error("Invalid PDU received");
+               return;
+       }
+
+       /* skip ATT header */
+       pdu += ATT_HDR_LEN;
+       len -= ATT_HDR_LEN;
+
+       if (len < 1) {
+               error("Op Code missing");
+               goto done;
+       }
+
+       opcode = *pdu;
+       pdu++;
+       len--;
+
+       if (opcode != RESPONSE_CODE) {
+               DBG("Unsupported Op Code received (%d)", opcode);
+               goto done;
+       }
+
+       if (len < 2) {
+               error("Invalid Response Code PDU received");
+               goto done;
+       }
+
+       req_opcode = *pdu;
+       rsp_code = *(pdu + 1);
+       pdu += 2;
+       len -= 2;
+
+       if (req == NULL || req->opcode != req_opcode) {
+               DBG("Indication received without pending request");
+               goto done;
+       }
+
+       switch (req->opcode) {
+       case SET_CUMULATIVE_VALUE:
+               controlpoint_method_reply(req, rsp_code);
+               break;
+
+       case REQUEST_SUPPORTED_SENSOR_LOC:
+               if (rsp_code == RSP_SUCCESS) {
+                       csc->num_locations = len;
+                       csc->locations = g_memdup(pdu, len);
+               } else {
+                       error("Failed to read Supported Sendor Locations");
+               }
+               break;
+
+       case UPDATE_SENSOR_LOC:
+               csc->location = req->pending_location;
+
+               controlpoint_property_reply(req, rsp_code);
+
+               g_dbus_emit_property_changed(btd_get_dbus_connection(),
+                                       device_get_path(csc->dev),
+                                       CYCLINGSPEED_INTERFACE, "Location");
+               break;
+       }
+
+       csc->pending_req = NULL;
+       g_source_remove(req->timeout);
+       g_free(req);
+
+done:
+       opdu = g_attrib_get_buffer(csc->attrib, &plen);
+       olen = enc_confirmation(opdu, plen);
+       if (olen > 0)
+               g_attrib_send(csc->attrib, 0, opdu, olen, NULL, NULL, NULL);
+}
+
+static void discover_char_cb(uint8_t status, GSList *chars, void *user_data)
+{
+       struct csc *csc = user_data;
+       uint16_t feature_val_handle = 0;
+
+       if (status) {
+               error("Discover CSCS characteristics: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       for (; chars; chars = chars->next) {
+               struct gatt_char *c = chars->data;
+               struct gatt_char *c_next =
+                               (chars->next ? chars->next->data : NULL);
+
+               if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
+                       csc->attio_measurement_id =
+                               g_attrib_register(csc->attrib,
+                                       ATT_OP_HANDLE_NOTIFY, c->value_handle,
+                                       measurement_notify_handler, csc, NULL);
+
+                       discover_desc(csc, c, c_next);
+               } else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
+                       feature_val_handle = c->value_handle;
+               } else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
+                       DBG("Sensor Location supported");
+                       gatt_read_char(csc->attrib, c->value_handle,
+                                                       read_location_cb, csc);
+               } else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
+                       DBG("SC Control Point supported");
+                       csc->controlpoint_val_handle = c->value_handle;
+
+                       csc->attio_controlpoint_id = g_attrib_register(
+                                       csc->attrib, ATT_OP_HANDLE_IND,
+                                       c->value_handle,
+                                       controlpoint_ind_handler, csc, NULL);
+
+                       discover_desc(csc, c, c_next);
+               }
+       }
+
+       if (feature_val_handle > 0)
+               gatt_read_char(csc->attrib, feature_val_handle,
+                                                       read_feature_cb, csc);
+}
+
+static void enable_measurement(gpointer data, gpointer user_data)
+{
+       struct csc *csc = data;
+       uint16_t handle = csc->measurement_ccc_handle;
+       uint8_t value[2];
+       char *msg;
+
+       if (csc->attrib == NULL || !handle)
+               return;
+
+       put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+       msg = g_strdup("Enable measurement");
+
+       gatt_write_char(csc->attrib, handle, value, sizeof(value),
+                                                       char_write_cb, msg);
+}
+
+static void disable_measurement(gpointer data, gpointer user_data)
+{
+       struct csc *csc = data;
+       uint16_t handle = csc->measurement_ccc_handle;
+       uint8_t value[2];
+       char *msg;
+
+       if (csc->attrib == NULL || !handle)
+               return;
+
+       put_le16(0x0000, value);
+       msg = g_strdup("Disable measurement");
+
+       gatt_write_char(csc->attrib, handle, value, sizeof(value),
+                                                       char_write_cb, msg);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+       struct csc *csc = user_data;
+
+       DBG("");
+
+       csc->attrib = g_attrib_ref(attrib);
+
+       gatt_discover_char(csc->attrib, csc->svc_range->start,
+                                               csc->svc_range->end, NULL,
+                                               discover_char_cb, csc);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+       struct csc *csc = user_data;
+
+       DBG("");
+
+       if (csc->attio_measurement_id > 0) {
+               g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
+               csc->attio_measurement_id = 0;
+       }
+
+       if (csc->attio_controlpoint_id > 0) {
+               g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
+               csc->attio_controlpoint_id = 0;
+       }
+
+       g_attrib_unref(csc->attrib);
+       csc->attrib = NULL;
+}
+
+static void watcher_exit_cb(DBusConnection *conn, void *user_data)
+{
+       struct watcher *watcher = user_data;
+       struct csc_adapter *cadapter = watcher->cadapter;
+
+       DBG("cycling watcher [%s] disconnected", watcher->path);
+
+       cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
+       g_dbus_remove_watch(conn, watcher->id);
+
+       if (g_slist_length(cadapter->watchers) == 0)
+               g_slist_foreach(cadapter->devices, disable_measurement, 0);
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct csc_adapter *cadapter = data;
+       struct watcher *watcher;
+       const char *sender = dbus_message_get_sender(msg);
+       char *path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       watcher = find_watcher(cadapter->watchers, sender, path);
+       if (watcher != NULL)
+               return btd_error_already_exists(msg);
+
+       watcher = g_new0(struct watcher, 1);
+       watcher->cadapter = cadapter;
+       watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit_cb,
+                                               watcher, destroy_watcher);
+       watcher->srv = g_strdup(sender);
+       watcher->path = g_strdup(path);
+
+       if (g_slist_length(cadapter->watchers) == 0)
+               g_slist_foreach(cadapter->devices, enable_measurement, 0);
+
+       cadapter->watchers = g_slist_prepend(cadapter->watchers, watcher);
+
+       DBG("cycling watcher [%s] registered", path);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct csc_adapter *cadapter = data;
+       struct watcher *watcher;
+       const char *sender = dbus_message_get_sender(msg);
+       char *path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       watcher = find_watcher(cadapter->watchers, sender, path);
+       if (watcher == NULL)
+               return btd_error_does_not_exist(msg);
+
+       cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
+       g_dbus_remove_watch(conn, watcher->id);
+
+       if (g_slist_length(cadapter->watchers) == 0)
+               g_slist_foreach(cadapter->devices, disable_measurement, 0);
+
+       DBG("cycling watcher [%s] unregistered", path);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable cyclingspeed_manager_methods[] = {
+       { GDBUS_METHOD("RegisterWatcher",
+                       GDBUS_ARGS({ "agent", "o" }), NULL,
+                       register_watcher) },
+       { GDBUS_METHOD("UnregisterWatcher",
+                       GDBUS_ARGS({ "agent", "o" }), NULL,
+                       unregister_watcher) },
+       { }
+};
+
+static int csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+       struct csc_adapter *cadapter;
+
+       cadapter = g_new0(struct csc_adapter, 1);
+       cadapter->adapter = adapter;
+
+       if (!g_dbus_register_interface(btd_get_dbus_connection(),
+                                               adapter_get_path(adapter),
+                                               CYCLINGSPEED_MANAGER_INTERFACE,
+                                               cyclingspeed_manager_methods,
+                                               NULL, NULL, cadapter,
+                                               destroy_csc_adapter)) {
+               error("D-Bus failed to register %s interface",
+                                               CYCLINGSPEED_MANAGER_INTERFACE);
+               destroy_csc_adapter(cadapter);
+               return -EIO;
+       }
+
+       csc_adapters = g_slist_prepend(csc_adapters, cadapter);
+
+       return 0;
+}
+
+static void csc_adapter_remove(struct btd_profile *p,
+                                               struct btd_adapter *adapter)
+{
+       struct csc_adapter *cadapter;
+
+       cadapter = find_csc_adapter(adapter);
+       if (cadapter == NULL)
+               return;
+
+       csc_adapters = g_slist_remove(csc_adapters, cadapter);
+
+       g_dbus_unregister_interface(btd_get_dbus_connection(),
+                                       adapter_get_path(cadapter->adapter),
+                                       CYCLINGSPEED_MANAGER_INTERFACE);
+}
+
+static gboolean property_get_location(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct csc *csc = data;
+       const char *loc;
+
+       if (!csc->has_location)
+               return FALSE;
+
+       loc = location2str(csc->location);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &loc);
+
+       return TRUE;
+}
+
+static void property_set_location(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter,
+                                       GDBusPendingPropertySet id, void *data)
+{
+       struct csc *csc = data;
+       char *loc;
+       int loc_val;
+       uint8_t att_val[2];
+       struct controlpoint_req *req;
+
+       if (csc->pending_req != NULL) {
+               g_dbus_pending_property_error(id,
+                                       ERROR_INTERFACE ".InProgress",
+                                       "Operation already in progress");
+               return;
+       }
+
+       if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT)) {
+               g_dbus_pending_property_error(id,
+                                       ERROR_INTERFACE ".NotSupported",
+                                       "Feature is not supported");
+               return;
+       }
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+               g_dbus_pending_property_error(id,
+                                       ERROR_INTERFACE ".InvalidArguments",
+                                       "Invalid arguments in method call");
+               return;
+       }
+
+       dbus_message_iter_get_basic(iter, &loc);
+
+       loc_val = str2location(loc);
+
+       if (loc_val < 0) {
+               g_dbus_pending_property_error(id,
+                                       ERROR_INTERFACE ".InvalidArguments",
+                                       "Invalid arguments in method call");
+               return;
+       }
+
+       req = g_new(struct controlpoint_req, 1);
+       req->csc = csc;
+       req->reply_id = id;
+       req->opcode = UPDATE_SENSOR_LOC;
+       req->pending_location = loc_val;
+
+       csc->pending_req = req;
+
+       att_val[0] = UPDATE_SENSOR_LOC;
+       att_val[1] = loc_val;
+
+       gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
+                               sizeof(att_val), controlpoint_write_cb, req);
+}
+
+static gboolean property_exists_location(const GDBusPropertyTable *property,
+                                                               void *data)
+{
+       struct csc *csc = data;
+
+       return csc->has_location;
+}
+
+static gboolean property_get_locations(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct csc *csc = data;
+       DBusMessageIter entry;
+       int i;
+
+       if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT))
+               return FALSE;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                                       DBUS_TYPE_STRING_AS_STRING, &entry);
+       for (i = 0; i < csc->num_locations; i++) {
+               char *loc = g_strdup(location2str(csc->locations[i]));
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &loc);
+               g_free(loc);
+       }
+
+       dbus_message_iter_close_container(iter, &entry);
+
+       return TRUE;
+}
+
+static gboolean property_exists_locations(const GDBusPropertyTable *property,
+                                                               void *data)
+{
+       struct csc *csc = data;
+
+       return !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
+}
+
+static gboolean property_get_wheel_rev_sup(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct csc *csc = data;
+       dbus_bool_t val;
+
+       val = !!(csc->feature & WHEEL_REV_SUPPORT);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+       return TRUE;
+}
+
+static gboolean property_get_multi_loc_sup(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct csc *csc = data;
+       dbus_bool_t val;
+
+       val = !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+       return TRUE;
+}
+
+static const GDBusPropertyTable cyclingspeed_device_properties[] = {
+       { "Location", "s", property_get_location, property_set_location,
+                                               property_exists_location },
+       { "SupportedLocations", "as", property_get_locations, NULL,
+                                               property_exists_locations },
+       { "WheelRevolutionDataSupported", "b", property_get_wheel_rev_sup },
+       { "MultipleLocationsSupported", "b", property_get_multi_loc_sup },
+       { }
+};
+
+static DBusMessage *set_cumulative_wheel_rev(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct csc *csc = data;
+       dbus_uint32_t value;
+       struct controlpoint_req *req;
+       uint8_t att_val[5]; /* uint8 opcode + uint32 value */
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &value,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       if (csc->pending_req != NULL)
+               return btd_error_in_progress(msg);
+
+       req = g_new(struct controlpoint_req, 1);
+       req->csc = csc;
+       req->opcode = SET_CUMULATIVE_VALUE;
+       req->msg = dbus_message_ref(msg);
+
+       csc->pending_req = req;
+
+       att_val[0] = SET_CUMULATIVE_VALUE;
+       put_le32(value, att_val + 1);
+
+       gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
+               sizeof(att_val), controlpoint_write_cb, req);
+
+       return NULL;
+}
+
+static const GDBusMethodTable cyclingspeed_device_methods[] = {
+       { GDBUS_ASYNC_METHOD("SetCumulativeWheelRevolutions",
+                               GDBUS_ARGS({ "value", "u" }), NULL,
+                                               set_cumulative_wheel_rev) },
+       { }
+};
+
+static int csc_device_probe(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct btd_adapter *adapter;
+       struct csc_adapter *cadapter;
+       struct csc *csc;
+       struct gatt_primary *prim;
+
+       prim = btd_device_get_primary(device, CYCLING_SC_UUID);
+       if (prim == NULL)
+               return -EINVAL;
+
+       adapter = device_get_adapter(device);
+
+       cadapter = find_csc_adapter(adapter);
+       if (cadapter == NULL)
+               return -1;
+
+       csc = g_new0(struct csc, 1);
+       csc->dev = btd_device_ref(device);
+       csc->cadapter = cadapter;
+
+       if (!g_dbus_register_interface(btd_get_dbus_connection(),
+                                               device_get_path(device),
+                                               CYCLINGSPEED_INTERFACE,
+                                               cyclingspeed_device_methods,
+                                               NULL,
+                                               cyclingspeed_device_properties,
+                                               csc, destroy_csc)) {
+               error("D-Bus failed to register %s interface",
+                                               CYCLINGSPEED_INTERFACE);
+               destroy_csc(csc);
+               return -EIO;
+       }
+
+       csc->svc_range = g_new0(struct att_range, 1);
+       csc->svc_range->start = prim->range.start;
+       csc->svc_range->end = prim->range.end;
+
+       cadapter->devices = g_slist_prepend(cadapter->devices, csc);
+
+       csc->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+                                               attio_disconnected_cb, csc);
+
+       return 0;
+}
+
+static void csc_device_remove(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct btd_adapter *adapter;
+       struct csc_adapter *cadapter;
+       struct csc *csc;
+       GSList *l;
+
+       adapter = device_get_adapter(device);
+
+       cadapter = find_csc_adapter(adapter);
+       if (cadapter == NULL)
+               return;
+
+       l = g_slist_find_custom(cadapter->devices, device, cmp_device);
+       if (l == NULL)
+               return;
+
+       csc = l->data;
+
+       cadapter->devices = g_slist_remove(cadapter->devices, csc);
+
+       g_dbus_unregister_interface(btd_get_dbus_connection(),
+                                               device_get_path(device),
+                                               CYCLINGSPEED_INTERFACE);
+}
+
+static struct btd_profile cscp_profile = {
+       .name           = "Cycling Speed and Cadence GATT Driver",
+       .remote_uuid    = CYCLING_SC_UUID,
+
+       .adapter_probe  = csc_adapter_probe,
+       .adapter_remove = csc_adapter_remove,
+
+       .device_probe   = csc_device_probe,
+       .device_remove  = csc_device_remove,
+};
+
+static int cyclingspeed_init(void)
+{
+       return btd_profile_register(&cscp_profile);
+}
+
+static void cyclingspeed_exit(void)
+{
+       btd_profile_unregister(&cscp_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(cyclingspeed, VERSION,
+                                       BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                                       cyclingspeed_init, cyclingspeed_exit)
index 47c8c25..ac9d4ac 100755 (executable)
@@ -154,6 +154,40 @@ static void handle_appearance(struct gas *gas, uint16_t value_handle)
                DBG("Failed to send request to read appearance");
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void read_rpa_res_characteristic_value_cb(bool success, uint8_t att_ecode,
+                                       const uint8_t *value, uint16_t length,
+                                       void *user_data)
+{
+       struct gas *gas = user_data;
+       uint8_t rpa_res_support;
+
+       if (!success) {
+               DBG("Reading RPA Resolution Char Value failed with ATT error: %u", att_ecode);
+               return;
+       }
+
+       /* The RPA Resolution Char Value value is a 8-bit unsigned integer */
+       if (length != 1) {
+               DBG("Malformed RPA resolution char value");
+               return;
+       }
+
+       rpa_res_support = *value;
+
+       DBG("GAP RPA Resolution Char Value: %d", rpa_res_support);
+
+       device_set_rpa_res_char_value(gas->device, rpa_res_support);
+}
+
+static void handle_rpa_res_characteristic_value(struct gas *gas, uint16_t value_handle)
+{
+       if (!bt_gatt_client_read_value(gas->client, value_handle,
+                                               read_rpa_res_characteristic_value_cb, gas, NULL))
+               DBG("Failed to send request to read RPA resolution Char Value");
+}
+#endif
+
 static bool uuid_cmp(uint16_t u16, const bt_uuid_t *uuid)
 {
        bt_uuid_t lhs;
@@ -180,6 +214,10 @@ static void handle_characteristic(struct gatt_db_attribute *attr,
                handle_device_name(gas, value_handle);
        else if (uuid_cmp(GATT_CHARAC_APPEARANCE, &uuid))
                handle_appearance(gas, value_handle);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       else if (uuid_cmp(GATT_CHARAC_CENTRAL_RPA_RESOLUTION, &uuid))
+               handle_rpa_res_characteristic_value(gas, value_handle);
+#endif
        else {
                char uuid_str[MAX_LEN_UUID_STR];
 
diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
new file mode 100755 (executable)
index 0000000..9e8c499
--- /dev/null
@@ -0,0 +1,870 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Tieto Poland
+ *
+ *  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 <errno.h>
+#include <stdbool.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/dbus-common.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/shared/util.h"
+#include "src/service.h"
+#include "src/error.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "src/attio.h"
+#include "src/log.h"
+
+#define HEART_RATE_INTERFACE           "org.bluez.HeartRate1"
+#define HEART_RATE_MANAGER_INTERFACE   "org.bluez.HeartRateManager1"
+#define HEART_RATE_WATCHER_INTERFACE   "org.bluez.HeartRateWatcher1"
+
+#define HR_VALUE_FORMAT                0x01
+#define SENSOR_CONTACT_DETECTED        0x02
+#define SENSOR_CONTACT_SUPPORT 0x04
+#define ENERGY_EXP_STATUS      0x08
+#define RR_INTERVAL            0x10
+
+struct heartrate_adapter {
+       struct btd_adapter      *adapter;
+       GSList                  *devices;
+       GSList                  *watchers;
+};
+
+struct heartrate {
+       struct btd_device               *dev;
+       struct heartrate_adapter        *hradapter;
+       GAttrib                         *attrib;
+       guint                           attioid;
+       guint                           attionotid;
+
+       struct att_range                *svc_range;     /* primary svc range */
+
+       uint16_t                        measurement_ccc_handle;
+       uint16_t                        hrcp_val_handle;
+
+       gboolean                        has_location;
+       uint8_t                         location;
+};
+
+struct watcher {
+       struct heartrate_adapter        *hradapter;
+       guint                           id;
+       char                            *srv;
+       char                            *path;
+};
+
+struct measurement {
+       struct heartrate        *hr;
+       uint16_t                value;
+       gboolean                has_energy;
+       uint16_t                energy;
+       gboolean                has_contact;
+       gboolean                contact;
+       uint16_t                num_interval;
+       uint16_t                *interval;
+};
+
+static GSList *heartrate_adapters = NULL;
+
+static const char * const location_enum[] = {
+       "other",
+       "chest",
+       "wrist",
+       "finger",
+       "hand",
+       "earlobe",
+       "foot",
+};
+
+static const char *location2str(uint8_t value)
+{
+        if (value < G_N_ELEMENTS(location_enum))
+               return location_enum[value];
+
+       error("Body Sensor Location [%d] is RFU", value);
+
+       return NULL;
+}
+
+static int cmp_adapter(gconstpointer a, gconstpointer b)
+{
+       const struct heartrate_adapter *hradapter = a;
+       const struct btd_adapter *adapter = b;
+
+       if (adapter == hradapter->adapter)
+               return 0;
+
+       return -1;
+}
+
+static int cmp_device(gconstpointer a, gconstpointer b)
+{
+       const struct heartrate *hr = a;
+       const struct btd_device *dev = b;
+
+       if (dev == hr->dev)
+               return 0;
+
+       return -1;
+}
+
+static int cmp_watcher(gconstpointer a, gconstpointer b)
+{
+       const struct watcher *watcher = a;
+       const struct watcher *match = b;
+       int ret;
+
+       ret = g_strcmp0(watcher->srv, match->srv);
+       if (ret != 0)
+               return ret;
+
+       return g_strcmp0(watcher->path, match->path);
+}
+
+static struct heartrate_adapter *
+find_heartrate_adapter(struct btd_adapter *adapter)
+{
+       GSList *l = g_slist_find_custom(heartrate_adapters, adapter,
+                                                               cmp_adapter);
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+static void destroy_watcher(gpointer user_data)
+{
+       struct watcher *watcher = user_data;
+
+       g_free(watcher->path);
+       g_free(watcher->srv);
+       g_free(watcher);
+}
+
+static struct watcher *find_watcher(GSList *list, const char *sender,
+                                                       const char *path)
+{
+       struct watcher *match;
+       GSList *l;
+
+       match = g_new0(struct watcher, 1);
+       match->srv = g_strdup(sender);
+       match->path = g_strdup(path);
+
+       l = g_slist_find_custom(list, match, cmp_watcher);
+       destroy_watcher(match);
+
+       if (l != NULL)
+               return l->data;
+
+       return NULL;
+}
+
+static void destroy_heartrate(gpointer user_data)
+{
+       struct heartrate *hr = user_data;
+
+       if (hr->attioid > 0)
+               btd_device_remove_attio_callback(hr->dev, hr->attioid);
+
+       if (hr->attrib != NULL) {
+               g_attrib_unregister(hr->attrib, hr->attionotid);
+               g_attrib_unref(hr->attrib);
+       }
+
+       btd_device_unref(hr->dev);
+       g_free(hr->svc_range);
+       g_free(hr);
+}
+
+static void remove_watcher(gpointer user_data)
+{
+       struct watcher *watcher = user_data;
+
+       g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+}
+
+static void destroy_heartrate_adapter(gpointer user_data)
+{
+       struct heartrate_adapter *hradapter = user_data;
+
+       g_slist_free_full(hradapter->watchers, remove_watcher);
+
+       g_free(hradapter);
+}
+
+static void read_sensor_location_cb(guint8 status, const guint8 *pdu,
+                                               guint16 len, gpointer user_data)
+{
+       struct heartrate *hr = user_data;
+       uint8_t value;
+       ssize_t vlen;
+
+       if (status != 0) {
+               error("Body Sensor Location read failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       vlen = dec_read_resp(pdu, len, &value, sizeof(value));
+       if (vlen < 0) {
+               error("Protocol error");
+               return;
+       }
+
+       if (vlen != sizeof(value)) {
+               error("Invalid length for Body Sensor Location");
+               return;
+       }
+
+       hr->has_location = TRUE;
+       hr->location = value;
+}
+
+static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       char *msg = user_data;
+
+       if (status != 0)
+               error("%s failed", msg);
+
+       g_free(msg);
+}
+
+static void update_watcher(gpointer data, gpointer user_data)
+{
+       struct watcher *w = data;
+       struct measurement *m = user_data;
+       struct heartrate *hr = m->hr;
+       const char *path = device_get_path(hr->dev);
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(w->srv, w->path,
+                       HEART_RATE_WATCHER_INTERFACE, "MeasurementReceived");
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       dict_append_entry(&dict, "Value", DBUS_TYPE_UINT16, &m->value);
+
+       if (m->has_energy)
+               dict_append_entry(&dict, "Energy", DBUS_TYPE_UINT16,
+                                                               &m->energy);
+
+       if (m->has_contact)
+               dict_append_entry(&dict, "Contact", DBUS_TYPE_BOOLEAN,
+                                                               &m->contact);
+
+       if (m->num_interval > 0)
+               dict_append_array(&dict, "Interval", DBUS_TYPE_UINT16,
+                                               &m->interval, m->num_interval);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       dbus_message_set_no_reply(msg, TRUE);
+       g_dbus_send_message(btd_get_dbus_connection(), msg);
+}
+
+static void process_measurement(struct heartrate *hr, const uint8_t *pdu,
+                                                               uint16_t len)
+{
+       struct measurement m;
+       uint8_t flags;
+
+       flags = *pdu;
+
+       pdu++;
+       len--;
+
+       memset(&m, 0, sizeof(m));
+
+       if (flags & HR_VALUE_FORMAT) {
+               if (len < 2) {
+                       error("Heart Rate Measurement field missing");
+                       return;
+               }
+
+               m.value = get_le16(pdu);
+               pdu += 2;
+               len -= 2;
+       } else {
+               if (len < 1) {
+                       error("Heart Rate Measurement field missing");
+                       return;
+               }
+
+               m.value = *pdu;
+               pdu++;
+               len--;
+       }
+
+       if (flags & ENERGY_EXP_STATUS) {
+               if (len < 2) {
+                       error("Energy Expended field missing");
+                       return;
+               }
+
+               m.has_energy = TRUE;
+               m.energy = get_le16(pdu);
+               pdu += 2;
+               len -= 2;
+       }
+
+       if (flags & RR_INTERVAL) {
+               int i;
+
+               if (len == 0 || (len % 2 != 0)) {
+                       error("RR-Interval field malformed");
+                       return;
+               }
+
+               m.num_interval = len / 2;
+               m.interval = g_new(uint16_t, m.num_interval);
+
+               for (i = 0; i < m.num_interval; pdu += 2, i++)
+                       m.interval[i] = get_le16(pdu);
+       }
+
+       if (flags & SENSOR_CONTACT_SUPPORT) {
+               m.has_contact = TRUE;
+               m.contact = !!(flags & SENSOR_CONTACT_DETECTED);
+       }
+
+       /* Notify all registered watchers */
+       m.hr = hr;
+       g_slist_foreach(hr->hradapter->watchers, update_watcher, &m);
+
+       g_free(m.interval);
+}
+
+static void notify_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+       struct heartrate *hr = user_data;
+
+       /* should be at least opcode (1b) + handle (2b) */
+       if (len < 3) {
+               error("Invalid PDU received");
+               return;
+       }
+
+       process_measurement(hr, pdu + 3, len - 3);
+}
+
+static void discover_ccc_cb(uint8_t status, GSList *descs, void *user_data)
+{
+       struct heartrate *hr = user_data;
+       struct gatt_desc *desc;
+       uint8_t attr_val[2];
+       char *msg;
+
+       if (status != 0) {
+               error("Discover Heart Rate Measurement descriptors failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       /* There will be only one descriptor on list and it will be CCC */
+       desc = descs->data;
+
+       hr->measurement_ccc_handle = desc->handle;
+
+       if (g_slist_length(hr->hradapter->watchers) == 0) {
+               put_le16(0x0000, attr_val);
+               msg = g_strdup("Disable measurement");
+       } else {
+               put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, attr_val);
+               msg = g_strdup("Enable measurement");
+       }
+
+       gatt_write_char(hr->attrib, desc->handle, attr_val, sizeof(attr_val),
+                                                       char_write_cb, msg);
+}
+
+static void discover_measurement_ccc(struct heartrate *hr,
+                               struct gatt_char *c, struct gatt_char *c_next)
+{
+       uint16_t start, end;
+       bt_uuid_t uuid;
+
+       start = c->value_handle + 1;
+
+       if (c_next != NULL) {
+               if (start == c_next->handle)
+                       return;
+               end = c_next->handle - 1;
+       } else if (c->value_handle != hr->svc_range->end) {
+               end = hr->svc_range->end;
+       } else {
+               return;
+       }
+
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+       gatt_discover_desc(hr->attrib, start, end, &uuid, discover_ccc_cb, hr);
+}
+
+static void discover_char_cb(uint8_t status, GSList *chars, void *user_data)
+{
+       struct heartrate *hr = user_data;
+
+       if (status) {
+               error("Discover HRS characteristics failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       for (; chars; chars = chars->next) {
+               struct gatt_char *c = chars->data;
+
+               if (g_strcmp0(c->uuid, HEART_RATE_MEASUREMENT_UUID) == 0) {
+                       struct gatt_char *c_next =
+                               (chars->next ? chars->next->data : NULL);
+
+                       hr->attionotid = g_attrib_register(hr->attrib,
+                                               ATT_OP_HANDLE_NOTIFY,
+                                               c->value_handle,
+                                               notify_handler, hr, NULL);
+
+                       discover_measurement_ccc(hr, c, c_next);
+               } else if (g_strcmp0(c->uuid, BODY_SENSOR_LOCATION_UUID) == 0) {
+                       DBG("Body Sensor Location supported");
+
+                       gatt_read_char(hr->attrib, c->value_handle,
+                                               read_sensor_location_cb, hr);
+               } else if (g_strcmp0(c->uuid,
+                                       HEART_RATE_CONTROL_POINT_UUID) == 0) {
+                       DBG("Heart Rate Control Point supported");
+                       hr->hrcp_val_handle = c->value_handle;
+               }
+       }
+}
+
+static void enable_measurement(gpointer data, gpointer user_data)
+{
+       struct heartrate *hr = data;
+       uint16_t handle = hr->measurement_ccc_handle;
+       uint8_t value[2];
+       char *msg;
+
+       if (hr->attrib == NULL || !handle)
+               return;
+
+       put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+       msg = g_strdup("Enable measurement");
+
+       gatt_write_char(hr->attrib, handle, value, sizeof(value),
+                                                       char_write_cb, msg);
+}
+
+static void disable_measurement(gpointer data, gpointer user_data)
+{
+       struct heartrate *hr = data;
+       uint16_t handle = hr->measurement_ccc_handle;
+       uint8_t value[2];
+       char *msg;
+
+       if (hr->attrib == NULL || !handle)
+               return;
+
+       put_le16(0x0000, value);
+       msg = g_strdup("Disable measurement");
+
+       gatt_write_char(hr->attrib, handle, value, sizeof(value),
+                                                       char_write_cb, msg);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+       struct heartrate *hr = user_data;
+
+       DBG("");
+
+       hr->attrib = g_attrib_ref(attrib);
+
+       gatt_discover_char(hr->attrib, hr->svc_range->start, hr->svc_range->end,
+                                               NULL, discover_char_cb, hr);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+       struct heartrate *hr = user_data;
+
+       DBG("");
+
+       if (hr->attionotid > 0) {
+               g_attrib_unregister(hr->attrib, hr->attionotid);
+               hr->attionotid = 0;
+       }
+
+       g_attrib_unref(hr->attrib);
+       hr->attrib = NULL;
+}
+
+static void watcher_exit_cb(DBusConnection *conn, void *user_data)
+{
+       struct watcher *watcher = user_data;
+       struct heartrate_adapter *hradapter = watcher->hradapter;
+
+       DBG("heartrate watcher [%s] disconnected", watcher->path);
+
+       hradapter->watchers = g_slist_remove(hradapter->watchers, watcher);
+       g_dbus_remove_watch(conn, watcher->id);
+
+       if (g_slist_length(hradapter->watchers) == 0)
+               g_slist_foreach(hradapter->devices, disable_measurement, 0);
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct heartrate_adapter *hradapter = data;
+       struct watcher *watcher;
+       const char *sender = dbus_message_get_sender(msg);
+       char *path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       watcher = find_watcher(hradapter->watchers, sender, path);
+       if (watcher != NULL)
+               return btd_error_already_exists(msg);
+
+       watcher = g_new0(struct watcher, 1);
+       watcher->hradapter = hradapter;
+       watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit_cb,
+                                               watcher, destroy_watcher);
+       watcher->srv = g_strdup(sender);
+       watcher->path = g_strdup(path);
+
+       if (g_slist_length(hradapter->watchers) == 0)
+               g_slist_foreach(hradapter->devices, enable_measurement, 0);
+
+       hradapter->watchers = g_slist_prepend(hradapter->watchers, watcher);
+
+       DBG("heartrate watcher [%s] registered", path);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct heartrate_adapter *hradapter = data;
+       struct watcher *watcher;
+       const char *sender = dbus_message_get_sender(msg);
+       char *path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       watcher = find_watcher(hradapter->watchers, sender, path);
+       if (watcher == NULL)
+               return btd_error_does_not_exist(msg);
+
+       hradapter->watchers = g_slist_remove(hradapter->watchers, watcher);
+       g_dbus_remove_watch(conn, watcher->id);
+
+       if (g_slist_length(hradapter->watchers) == 0)
+               g_slist_foreach(hradapter->devices, disable_measurement, 0);
+
+       DBG("heartrate watcher [%s] unregistered", path);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable heartrate_manager_methods[] = {
+       { GDBUS_METHOD("RegisterWatcher",
+                       GDBUS_ARGS({ "agent", "o" }), NULL,
+                       register_watcher) },
+       { GDBUS_METHOD("UnregisterWatcher",
+                       GDBUS_ARGS({ "agent", "o" }), NULL,
+                       unregister_watcher) },
+       { }
+};
+
+static gboolean property_get_location(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct heartrate *hr = data;
+       char *loc;
+
+       if (!hr->has_location)
+               return FALSE;
+
+       loc = g_strdup(location2str(hr->location));
+
+       if (loc == NULL)
+               return FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &loc);
+
+       g_free(loc);
+
+       return TRUE;
+}
+
+static gboolean property_exists_location(const GDBusPropertyTable *property,
+                                                               void *data)
+{
+       struct heartrate *hr = data;
+
+       if (!hr->has_location || location2str(hr->location) == NULL)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean property_get_reset_supported(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct heartrate *hr = data;
+       dbus_bool_t has_reset = !!hr->hrcp_val_handle;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &has_reset);
+
+       return TRUE;
+}
+
+static DBusMessage *hrcp_reset(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct heartrate *hr = data;
+       uint8_t value;
+       char *vmsg;
+
+       if (!hr->hrcp_val_handle)
+               return btd_error_not_supported(msg);
+
+       if (!hr->attrib)
+               return btd_error_not_available(msg);
+
+       value = 0x01;
+       vmsg = g_strdup("Reset Control Point");
+       gatt_write_char(hr->attrib, hr->hrcp_val_handle, &value,
+                                       sizeof(value), char_write_cb, vmsg);
+
+       DBG("Energy Expended Value has been reset");
+
+       return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable heartrate_device_methods[] = {
+       { GDBUS_METHOD("Reset", NULL, NULL, hrcp_reset) },
+       { }
+};
+
+static const GDBusPropertyTable heartrate_device_properties[] = {
+       { "Location", "s", property_get_location, NULL,
+                                               property_exists_location },
+       { "ResetSupported", "b", property_get_reset_supported },
+       { }
+};
+
+static int heartrate_adapter_register(struct btd_adapter *adapter)
+{
+       struct heartrate_adapter *hradapter;
+
+       hradapter = g_new0(struct heartrate_adapter, 1);
+       hradapter->adapter = adapter;
+
+       if (!g_dbus_register_interface(btd_get_dbus_connection(),
+                                               adapter_get_path(adapter),
+                                               HEART_RATE_MANAGER_INTERFACE,
+                                               heartrate_manager_methods,
+                                               NULL, NULL, hradapter,
+                                               destroy_heartrate_adapter)) {
+               error("D-Bus failed to register %s interface",
+                                               HEART_RATE_MANAGER_INTERFACE);
+               destroy_heartrate_adapter(hradapter);
+               return -EIO;
+       }
+
+       heartrate_adapters = g_slist_prepend(heartrate_adapters, hradapter);
+
+       return 0;
+}
+
+static void heartrate_adapter_unregister(struct btd_adapter *adapter)
+{
+       struct heartrate_adapter *hradapter;
+
+       hradapter = find_heartrate_adapter(adapter);
+       if (hradapter == NULL)
+               return;
+
+       heartrate_adapters = g_slist_remove(heartrate_adapters, hradapter);
+
+       g_dbus_unregister_interface(btd_get_dbus_connection(),
+                                       adapter_get_path(hradapter->adapter),
+                                       HEART_RATE_MANAGER_INTERFACE);
+}
+
+static int heartrate_device_register(struct btd_device *device,
+                                               struct gatt_primary *prim)
+{
+       struct btd_adapter *adapter;
+       struct heartrate_adapter *hradapter;
+       struct heartrate *hr;
+
+       adapter = device_get_adapter(device);
+
+       hradapter = find_heartrate_adapter(adapter);
+
+       if (hradapter == NULL)
+               return -1;
+
+       hr = g_new0(struct heartrate, 1);
+       hr->dev = btd_device_ref(device);
+       hr->hradapter = hradapter;
+
+       if (!g_dbus_register_interface(btd_get_dbus_connection(),
+                                               device_get_path(device),
+                                               HEART_RATE_INTERFACE,
+                                               heartrate_device_methods,
+                                               NULL,
+                                               heartrate_device_properties,
+                                               hr, destroy_heartrate)) {
+               error("D-Bus failed to register %s interface",
+                                               HEART_RATE_INTERFACE);
+               destroy_heartrate(hr);
+               return -EIO;
+       }
+
+       hr->svc_range = g_new0(struct att_range, 1);
+       hr->svc_range->start = prim->range.start;
+       hr->svc_range->end = prim->range.end;
+
+       hradapter->devices = g_slist_prepend(hradapter->devices, hr);
+
+       hr->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+                                               attio_disconnected_cb, hr);
+
+       return 0;
+}
+
+static void heartrate_device_unregister(struct btd_device *device)
+{
+       struct btd_adapter *adapter;
+       struct heartrate_adapter *hradapter;
+       struct heartrate *hr;
+       GSList *l;
+
+       adapter = device_get_adapter(device);
+
+       hradapter = find_heartrate_adapter(adapter);
+       if (hradapter == NULL)
+               return;
+
+       l = g_slist_find_custom(hradapter->devices, device, cmp_device);
+       if (l == NULL)
+               return;
+
+       hr = l->data;
+
+       hradapter->devices = g_slist_remove(hradapter->devices, hr);
+
+       g_dbus_unregister_interface(btd_get_dbus_connection(),
+                               device_get_path(device), HEART_RATE_INTERFACE);
+}
+
+static int heartrate_adapter_probe(struct btd_profile *p,
+                                               struct btd_adapter *adapter)
+{
+       return heartrate_adapter_register(adapter);
+}
+
+static void heartrate_adapter_remove(struct btd_profile *p,
+                                               struct btd_adapter *adapter)
+{
+       heartrate_adapter_unregister(adapter);
+}
+
+static int heartrate_device_probe(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct gatt_primary *prim;
+
+       prim = btd_device_get_primary(device, HEART_RATE_UUID);
+       if (prim == NULL)
+               return -EINVAL;
+
+       return heartrate_device_register(device, prim);
+}
+
+static void heartrate_device_remove(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+
+       heartrate_device_unregister(device);
+}
+
+static struct btd_profile hrp_profile = {
+       .name           = "Heart Rate GATT Driver",
+       .remote_uuid    = HEART_RATE_UUID,
+
+       .device_probe   = heartrate_device_probe,
+       .device_remove  = heartrate_device_remove,
+
+       .adapter_probe  = heartrate_adapter_probe,
+       .adapter_remove = heartrate_adapter_remove,
+};
+
+static int heartrate_init(void)
+{
+       return btd_profile_register(&hrp_profile);
+}
+
+static void heartrate_exit(void)
+{
+       btd_profile_unregister(&hrp_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(heartrate, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                                       heartrate_init, heartrate_exit)
index a494ea2..ff553cb 100755 (executable)
@@ -87,6 +87,9 @@ struct input_device {
        uint8_t                 report_req_pending;
        guint                   report_req_timer;
        uint32_t                report_rsp_id;
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+       char *role;
+#endif
 };
 
 static int idle_timeout = 0;
@@ -333,8 +336,12 @@ static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data
        btd_service_disconnecting_complete(idev->service, 0);
 
        /* Enter the auto-reconnect mode if needed */
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+       if (idev->role == NULL)
+               input_device_enter_reconnect_mode(idev);
+#else
        input_device_enter_reconnect_mode(idev);
-
+#endif
        return FALSE;
 }
 
@@ -1002,10 +1009,21 @@ cleanup:
 
 static bool is_connected(struct input_device *idev)
 {
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+       if (idev->role == NULL) {
+               if (idev->uhid)
+                       return (idev->intr_io != NULL && idev->ctrl_io != NULL);
+               else
+                       return ioctl_is_connected(idev);
+       } else {
+               return (idev->intr_io != NULL && idev->ctrl_io != NULL);
+       }
+#else
        if (idev->uhid)
                return (idev->intr_io != NULL && idev->ctrl_io != NULL);
        else
                return ioctl_is_connected(idev);
+#endif
 }
 
 static int connection_disconnect(struct input_device *idev, uint32_t flags)
@@ -1019,6 +1037,10 @@ static int connection_disconnect(struct input_device *idev, uint32_t flags)
        if (idev->ctrl_io)
                g_io_channel_shutdown(idev->ctrl_io, TRUE, NULL);
 
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+       if (idev->role != NULL)
+               btd_service_disconnecting_complete(idev->service, 0);
+#endif
        if (idev->uhid)
                return 0;
        else
@@ -1031,10 +1053,17 @@ static int input_device_connected(struct input_device *idev)
 
        if (idev->intr_io == NULL || idev->ctrl_io == NULL)
                return -ENOTCONN;
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+       if (idev->role == NULL) {
+               err = hidp_add_connection(idev);
+               if (err < 0)
+                       return err;
+       }
+#else
        err = hidp_add_connection(idev);
        if (err < 0)
                return err;
+#endif
 
        btd_service_connecting_complete(idev->service, 0);
 
@@ -1052,11 +1081,19 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err,
                err = -EIO;
                goto failed;
        }
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+       if (idev->role == NULL) {
+               err = input_device_connected(idev);
+               if (err < 0)
+                       goto failed;
+       } else {
+               btd_service_connecting_complete(idev->service, 0);
+       }
+#else
        err = input_device_connected(idev);
        if (err < 0)
                goto failed;
-
+#endif
        if (idev->uhid)
                cond |= G_IO_IN;
 
@@ -1230,7 +1267,9 @@ int input_device_connect(struct btd_service *service)
        DBG("");
 
        idev = btd_service_get_user_data(service);
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+       DBG("Role=%s", idev->role);
+#endif
        if (idev->ctrl_io)
                return -EBUSY;
 
@@ -1251,7 +1290,9 @@ int input_device_disconnect(struct btd_service *service)
 
        flags = device_is_temporary(idev->device) ?
                                        (1 << HIDP_VIRTUAL_CABLE_UNPLUG) : 0;
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+       DBG("Role=%s", idev->role);
+#endif
        err = connection_disconnect(idev, flags);
        if (err < 0)
                return err;
@@ -1328,6 +1369,27 @@ static struct input_device *input_device_new(struct btd_service *service)
        return idev;
 }
 
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static struct input_device *input_device_role_new(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       const char *path = device_get_path(device);
+       struct btd_adapter *adapter = device_get_adapter(device);
+       struct input_device *idev;
+
+       idev = g_new0(struct input_device, 1);
+       bacpy(&idev->src, btd_adapter_get_address(adapter));
+       bacpy(&idev->dst, device_get_address(device));
+       idev->service = btd_service_ref(service);
+       idev->device = btd_device_ref(device);
+       idev->path = g_strdup(path);
+       idev->role = g_strdup("device");
+       idev->disable_sdp = 0;
+       idev->uhid = NULL;
+       return idev;
+}
+#endif
+
 static gboolean property_get_reconnect_mode(
                                        const GDBusPropertyTable *property,
                                        DBusMessageIter *iter, void *data)
@@ -1345,6 +1407,37 @@ static const GDBusPropertyTable input_properties[] = {
        { }
 };
 
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static DBusMessage *hid_device_fd(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct input_device *idev = user_data;
+       GError *gerr = NULL;
+       DBusMessage *reply;
+       int ctrl_fd = -1;
+       int intr_fd = -1;
+       if (idev->ctrl_io == NULL || idev->intr_io == NULL) {
+               DBG("Return error reply");
+               reply = g_dbus_create_error(msg, ERROR_INTERFACE ".InputError",
+                                       "%s", "NotConnected");
+               g_error_free(gerr);
+       } else {
+               ctrl_fd = g_io_channel_unix_get_fd(idev->ctrl_io);
+               intr_fd = g_io_channel_unix_get_fd(idev->intr_io);
+               reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD,
+                                                       &ctrl_fd, DBUS_TYPE_UNIX_FD, &intr_fd ,DBUS_TYPE_INVALID);
+       }
+
+       return reply;
+}
+static const GDBusMethodTable input_device_methods[] = {
+       { GDBUS_ASYNC_METHOD("GetFD",
+                       NULL, GDBUS_ARGS({ "fd", "h" } , {"fd", "h"}),
+                       hid_device_fd) },
+       { }
+};
+#endif
+
 int input_device_register(struct btd_service *service)
 {
        struct btd_device *device = btd_service_get_device(service);
@@ -1381,6 +1474,34 @@ int input_device_register(struct btd_service *service)
        return 0;
 }
 
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+int input_device_role_register(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       const char *path = device_get_path(device);
+       struct input_device *idev;
+
+       DBG("%s", path);
+
+       idev = input_device_role_new(service);
+       if (!idev)
+               return -EINVAL;
+       if (g_dbus_register_interface(btd_get_dbus_connection(),
+                                       idev->path, INPUT_INTERFACE,
+                                       input_device_methods, NULL,
+                                       NULL, idev,
+                                       NULL) == FALSE) {
+               error("Unable to register %s interface", INPUT_INTERFACE);
+               input_device_free(idev);
+               return -EINVAL;
+       }
+       btd_service_set_user_data(service, idev);
+
+       return 0;
+}
+
+#endif
+
 static struct input_device *find_device(const bdaddr_t *src,
                                        const bdaddr_t *dst)
 {
@@ -1398,6 +1519,25 @@ static struct input_device *find_device(const bdaddr_t *src,
        return btd_service_get_user_data(service);
 }
 
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static struct input_device *find_device_role(const bdaddr_t *src,
+                                       const bdaddr_t *dst)
+{
+       struct btd_device *device;
+       struct btd_service *service;
+
+       device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR);
+       if (device == NULL)
+               return NULL;
+
+       service = btd_device_get_service(device, HID_DEVICE_UUID);
+       if (service == NULL)
+               return NULL;
+
+       return btd_service_get_user_data(service);
+}
+#endif
+
 void input_device_unregister(struct btd_service *service)
 {
        struct btd_device *device = btd_service_get_device(service);
@@ -1412,6 +1552,19 @@ void input_device_unregister(struct btd_service *service)
        input_device_free(idev);
 }
 
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+void input_device_role_unregister(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       const char *path = device_get_path(device);
+       struct input_device *idev = btd_service_get_user_data(service);
+
+       DBG("%s", path);
+
+       input_device_free(idev);
+}
+#endif
+
 static int input_device_connadd(struct input_device *idev)
 {
        int err;
@@ -1443,6 +1596,16 @@ bool input_device_exists(const bdaddr_t *src, const bdaddr_t *dst)
        return false;
 }
 
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+bool input_device_role_exists(const bdaddr_t *src, const bdaddr_t *dst)
+{
+       if (find_device_role(src, dst))
+               return true;
+
+       return false;
+}
+#endif
+
 int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
                                                                GIOChannel *io)
 {
@@ -1480,6 +1643,58 @@ int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
        return 0;
 }
 
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+int input_device_role_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
+                                                               GIOChannel *io)
+{
+       struct input_device *idev = find_device_role(src, dst);
+       GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+
+       DBG("idev %p psm %d", idev, psm);
+
+       if (!idev)
+               return -ENOENT;
+
+       switch (psm) {
+       case L2CAP_PSM_HIDP_CTRL:
+               if (idev->ctrl_io)
+                       return -EALREADY;
+               idev->ctrl_io = g_io_channel_ref(io);
+               idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, cond,
+                                                       ctrl_watch_cb, idev);
+               break;
+       case L2CAP_PSM_HIDP_INTR:
+               if (idev->intr_io)
+                       return -EALREADY;
+               idev->intr_io = g_io_channel_ref(io);
+               idev->intr_watch = g_io_add_watch(idev->intr_io, cond,
+                                                       intr_watch_cb, idev);
+               break;
+       }
+       if (idev->intr_io && idev->ctrl_io) {
+               btd_service_connecting_complete(idev->service, 0);
+       }
+       return 0;
+}
+
+int input_device_role_close_channels(const bdaddr_t *src, const bdaddr_t *dst)
+{
+       struct input_device *idev = find_device(src, dst);
+
+       if (!idev)
+               return -ENOENT;
+
+       if (idev->intr_io)
+               g_io_channel_shutdown(idev->intr_io, TRUE, NULL);
+
+       if (idev->ctrl_io)
+               g_io_channel_shutdown(idev->ctrl_io, TRUE, NULL);
+
+       return 0;
+}
+
+#endif
+
 int input_device_close_channels(const bdaddr_t *src, const bdaddr_t *dst)
 {
        struct input_device *idev = find_device(src, dst);
index 51a9aee..20aba31 100755 (executable)
@@ -33,6 +33,15 @@ void input_enable_userspace_hid(bool state);
 int input_device_register(struct btd_service *service);
 void input_device_unregister(struct btd_service *service);
 
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+int input_device_role_register(struct btd_service *service);
+void input_device_role_unregister(struct btd_service *service);
+bool input_device_role_exists(const bdaddr_t *src, const bdaddr_t *dst);
+int input_device_role_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
+                                                       GIOChannel *io);
+int input_device_role_close_channels(const bdaddr_t *src, const bdaddr_t *dst);
+#endif
+
 bool input_device_exists(const bdaddr_t *src, const bdaddr_t *dst);
 int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
                                                        GIOChannel *io);
index 1d31b06..2aaeb15 100755 (executable)
@@ -53,6 +53,19 @@ static void hid_server_remove(struct btd_profile *p,
 {
        server_stop(btd_adapter_get_address(adapter));
 }
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static int hid_device_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+       DBG("hid device probe");
+       return server_start(btd_adapter_get_address(adapter));
+}
+
+static void hid_device_remove(struct btd_profile *p,
+                                               struct btd_adapter *adapter)
+{
+       server_stop(btd_adapter_get_address(adapter));
+}
+#endif
 
 static struct btd_profile input_profile = {
        .name           = "input-hid",
@@ -70,6 +83,24 @@ static struct btd_profile input_profile = {
        .adapter_remove = hid_server_remove,
 };
 
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static struct btd_profile input_device_profile = {
+       .name           = "hid-device",
+       .local_uuid     = HID_DEVICE_UUID,
+       .remote_uuid    = HID_DEVICE_UUID,
+
+       .auto_connect   = false,
+       .connect        = input_device_connect,
+       .disconnect     = input_device_disconnect,
+
+       .device_probe   = input_device_role_register,
+       .device_remove  = input_device_role_unregister,
+
+       .adapter_probe  = hid_device_probe,
+       .adapter_remove = hid_device_remove,
+};
+#endif
+
 static GKeyFile *load_config_file(const char *file)
 {
        GKeyFile *keyfile;
@@ -117,7 +148,9 @@ static int input_init(void)
        }
 
        btd_profile_register(&input_profile);
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+       btd_profile_register(&input_device_profile);
+#endif
        if (config)
                g_key_file_free(config);
 
@@ -127,6 +160,9 @@ static int input_init(void)
 static void input_exit(void)
 {
        btd_profile_unregister(&input_profile);
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+       btd_profile_unregister(&input_device_profile);
+#endif
 }
 
 BLUETOOTH_PLUGIN_DEFINE(input, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
index eb3fcf8..2ee3b9b 100755 (executable)
@@ -57,6 +57,9 @@ struct input_server {
        GIOChannel *ctrl;
        GIOChannel *intr;
        struct confirm_data *confirm;
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+       char *role;
+#endif
 };
 
 static int server_cmp(gconstpointer s, gconstpointer user_data)
@@ -182,7 +185,14 @@ static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data)
                sixaxis_browse_sdp(&src, &dst, chan, psm);
                return;
        }
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+       if (ret == -ENOENT) {
+               DBG("Connection request for device role");
+               ret = input_device_role_set_channel(&src, &dst, psm, chan);
+               if (ret == 0)
+                       return;
+       }
+#endif
        error("Refusing input device connect: %s (%d)", strerror(-ret), -ret);
 
        /* Send unplug virtual cable to unknown devices */
@@ -208,8 +218,15 @@ static void auth_callback(DBusError *derr, void *user_data)
        }
 
        if (!input_device_exists(&server->src, &confirm->dst) &&
-                               !dev_is_sixaxis(&server->src, &confirm->dst))
+                               !dev_is_sixaxis(&server->src, &confirm->dst)) {
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+               if (!input_device_role_exists(&server->src, &confirm->dst)) {
+                       return;
+               }
+#else
                return;
+#endif
+       }
 
        if (!bt_io_accept(confirm->io, connect_event_cb, server, NULL, &err)) {
                error("bt_io_accept: %s", err->message);
@@ -260,8 +277,15 @@ static void confirm_event_cb(GIOChannel *chan, gpointer user_data)
        }
 
        if (!input_device_exists(&src, &dst) && !dev_is_sixaxis(&src, &dst)) {
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+               if (!input_device_role_exists(&src, &dst)) {
+                       error("Refusing connection from %s: unknown device", addr);
+                       goto drop;
+               }
+#else
                error("Refusing connection from %s: unknown device", addr);
                goto drop;
+#endif
        }
 
        server->confirm = g_new0(struct confirm_data, 1);
@@ -344,3 +368,59 @@ void server_stop(const bdaddr_t *src)
        servers = g_slist_remove(servers, server);
        g_free(server);
 }
+
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+int server_device_start(const bdaddr_t *src)
+{
+       struct input_server *server;
+       GError *err = NULL;
+
+       server = g_new0(struct input_server, 1);
+       bacpy(&server->src, src);
+
+       server->ctrl = bt_io_listen(connect_event_cb, NULL,
+                               server, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, src,
+                               BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+                               BT_IO_OPT_INVALID);
+       if (!server->ctrl) {
+               error("Failed to listen on control channel");
+       }
+
+       server->intr = bt_io_listen(NULL, confirm_event_cb,
+                               server, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, src,
+                               BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+                               BT_IO_OPT_INVALID);
+       if (!server->intr) {
+               error("Failed to listen on interrupt channel");
+       }
+       server->role = strdup("device");
+       servers = g_slist_append(servers, server);
+
+       return 0;
+}
+
+void server_device_stop(const bdaddr_t *src)
+{
+       struct input_server *server;
+       GSList *l;
+
+       l = g_slist_find_custom(servers, src, server_cmp);
+       if (!l)
+               return;
+
+       server = l->data;
+
+       g_io_channel_shutdown(server->intr, TRUE, NULL);
+       g_io_channel_unref(server->intr);
+
+       g_io_channel_shutdown(server->ctrl, TRUE, NULL);
+       g_io_channel_unref(server->ctrl);
+       g_free(server->role);
+       servers = g_slist_remove(servers, server);
+       g_free(server);
+}
+#endif
index 9bf0b18..86d1eb9 100755 (executable)
 
 static int ctl;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+/* Compatibility with old ioctls */
+#define OLD_BNEPCONADD      1
+#define OLD_BNEPCONDEL      2
+#define OLD_BNEPGETCONLIST  3
+#define OLD_BNEPGETCONINFO  4
+
+static unsigned long bnepconnadd;
+static unsigned long bnepconndel;
+static unsigned long bnepgetconnlist;
+static unsigned long bnepgetconninfo;
+#endif
+
 struct __service_16 {
        uint16_t dst;
        uint16_t src;
@@ -88,7 +101,30 @@ int bnep_init(void)
 
                return err;
        }
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+/* Temporary ioctl compatibility hack */
+{
+       struct bnep_connlist_req req;
+       struct bnep_conninfo ci[1];
+
+       req.cnum = 1;
+       req.ci   = ci;
+
+       if (!ioctl(ctl, BNEPGETCONNLIST, &req)) {
+               /* New ioctls */
+               bnepconnadd     = BNEPCONNADD;
+               bnepconndel     = BNEPCONNDEL;
+               bnepgetconnlist = BNEPGETCONNLIST;
+               bnepgetconninfo = BNEPGETCONNINFO;
+       } else {
+               /* Old ioctls */
+               bnepconnadd     = OLD_BNEPCONADD;
+               bnepconndel     = OLD_BNEPCONDEL;
+               bnepgetconnlist = OLD_BNEPGETCONLIST;
+               bnepgetconninfo = OLD_BNEPGETCONINFO;
+       }
+}
+#endif
        return 0;
 }
 
@@ -179,6 +215,11 @@ static int bnep_if_down(const char *devname)
 
        sk = socket(AF_INET, SOCK_DGRAM, 0);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (sk < 0)
+               return -1;
+#endif
+
        memset(&ifr, 0, sizeof(ifr));
        strncpy(ifr.ifr_name, devname, IF_NAMESIZE - 1);
 
@@ -420,11 +461,12 @@ void bnep_disconnect(struct bnep *session)
        if (!session)
                return;
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (session->watch > 0) {
                g_source_remove(session->watch);
                session->watch = 0;
        }
-
+#endif
        if (session->io) {
                g_io_channel_unref(session->io);
                session->io = NULL;
@@ -434,6 +476,7 @@ void bnep_disconnect(struct bnep *session)
        bnep_conndel(&session->dst_addr);
 }
 
+#ifndef  TIZEN_FEATURE_BLUEZ_MODIFY
 static int bnep_add_to_bridge(const char *devname, const char *bridge)
 {
        int ifindex;
@@ -448,7 +491,15 @@ static int bnep_add_to_bridge(const char *devname, const char *bridge)
        sk = socket(AF_INET, SOCK_STREAM, 0);
        if (sk < 0)
                return -1;
-
+#ifdef  TIZEN_FEATURE_BLUEZ_MODIFY
+       err = ioctl(sk, SIOCBRADDBR, bridge);
+       if (err < 0)
+       {
+               info("bridge create err: %d", err);
+               close(sk);
+               return -errno;
+       }
+#endif
        memset(&ifr, 0, sizeof(ifr));
        strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
        ifr.ifr_ifindex = ifindex;
@@ -465,6 +516,7 @@ static int bnep_add_to_bridge(const char *devname, const char *bridge)
 
        return err;
 }
+#endif
 
 static int bnep_del_from_bridge(const char *devname, const char *bridge)
 {
@@ -602,6 +654,20 @@ static uint16_t bnep_setup_decode(int sk, struct bnep_setup_conn_req *req,
        return BNEP_CONN_INVALID_DST;
 }
 
+#ifdef  TIZEN_FEATURE_BLUEZ_MODIFY
+int bnep_if_down_wrapper(const char *devname)
+{
+       bnep_if_down(devname);
+       return 0;
+}
+
+int bnep_conndel_wrapper(const bdaddr_t *dst)
+{
+       bnep_conndel(dst);
+       return 0;
+}
+#endif
+
 static int bnep_server_add_legacy(int sk, uint16_t dst, char *bridge,
                                        char *iface, const bdaddr_t *addr,
                                        uint8_t *setup_data, int len)
@@ -622,12 +688,14 @@ static int bnep_server_add_legacy(int sk, uint16_t dst, char *bridge,
                goto reply;
        }
 
+#ifndef  TIZEN_FEATURE_BLUEZ_MODIFY
        err = bnep_add_to_bridge(iface, bridge);
        if (err < 0) {
                bnep_conndel(addr);
                rsp = BNEP_CONN_NOT_ALLOWED;
                goto reply;
        }
+#endif
 
        err = bnep_if_up(iface);
        if (err < 0) {
@@ -697,9 +765,11 @@ int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr,
                goto failed;
        }
 
+#ifndef  TIZEN_FEATURE_BLUEZ_MODIFY
        err = bnep_add_to_bridge(iface, bridge);
        if (err < 0)
                goto failed_conn;
+#endif
 
        err = bnep_if_up(iface);
        if (err < 0)
@@ -710,7 +780,9 @@ int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr,
 failed_bridge:
        bnep_del_from_bridge(iface, bridge);
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
 failed_conn:
+#endif
        bnep_conndel(addr);
 
        return err;
index e9f4c1c..d34ed03 100755 (executable)
@@ -40,3 +40,7 @@ void bnep_disconnect(struct bnep *session);
 int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr,
                                                uint8_t *setup_data, int len);
 void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr);
+#ifdef  TIZEN_FEATURE_BLUEZ_MODIFY
+int bnep_if_down_wrapper(const char *devname);
+int bnep_conndel_wrapper(const bdaddr_t *dst);
+#endif
index 5305ace..53d35fe 100755 (executable)
@@ -123,12 +123,14 @@ static void bnep_disconn_cb(gpointer data)
        DBusConnection *conn = btd_get_dbus_connection();
        const char *path = device_get_path(nc->peer->device);
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        g_dbus_emit_property_changed(conn, path,
                                        NETWORK_PEER_INTERFACE, "Connected");
        g_dbus_emit_property_changed(conn, path,
                                        NETWORK_PEER_INTERFACE, "Interface");
        g_dbus_emit_property_changed(conn, path,
                                        NETWORK_PEER_INTERFACE, "UUID");
+#endif
        device_remove_disconnect_watch(nc->peer->device, nc->dc_id);
        nc->dc_id = 0;
 
@@ -137,6 +139,14 @@ static void bnep_disconn_cb(gpointer data)
        info("%s disconnected", nc->dev);
 
        nc->state = DISCONNECTED;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       g_dbus_emit_property_changed(conn, path,
+                                       NETWORK_PEER_INTERFACE, "Connected");
+       g_dbus_emit_property_changed(conn, path,
+                                       NETWORK_PEER_INTERFACE, "Interface");
+       g_dbus_emit_property_changed(conn, path,
+                                       NETWORK_PEER_INTERFACE, "UUID");
+#endif
        memset(nc->dev, 0, sizeof(nc->dev));
        strncpy(nc->dev, BNEP_INTERFACE, 16);
        nc->dev[15] = '\0';
@@ -179,9 +189,10 @@ static void cancel_connection(struct network_conn *nc, int err)
        if (nc->state == CONNECTED)
                bnep_disconnect(nc->session);
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        bnep_free(nc->session);
        nc->session = NULL;
-
+#endif
        nc->state = DISCONNECTED;
 }
 
@@ -226,6 +237,9 @@ static void bnep_conn_cb(char *iface, int err, void *data)
        conn = btd_get_dbus_connection();
        path = device_get_path(nc->peer->device);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       nc->state = CONNECTED;
+#endif
        g_dbus_emit_property_changed(conn, path,
                                        NETWORK_PEER_INTERFACE, "Connected");
        g_dbus_emit_property_changed(conn, path,
@@ -233,7 +247,9 @@ static void bnep_conn_cb(char *iface, int err, void *data)
        g_dbus_emit_property_changed(conn, path,
                                        NETWORK_PEER_INTERFACE, "UUID");
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        nc->state = CONNECTED;
+#endif
        nc->dc_id = device_add_disconnect_watch(nc->peer->device, disconnect_cb,
                                                                nc, NULL);
 
index e69ffaf..19206a1 100755 (executable)
@@ -86,6 +86,11 @@ struct network_server {
 static GSList *adapters = NULL;
 static gboolean security = TRUE;
 
+#ifdef  TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean server_disconnected_cb(GIOChannel *chan,
+                       GIOCondition cond, gpointer user_data);
+#endif
+
 static struct network_adapter *find_adapter(GSList *list,
                                        struct btd_adapter *adapter)
 {
@@ -151,6 +156,38 @@ static struct network_server *find_server_by_uuid(GSList *list,
        return NULL;
 }
 
+#ifdef  TIZEN_FEATURE_BLUEZ_MODIFY
+static struct network_session *find_session(GSList *list, GIOChannel *io)
+{
+       GSList *l;
+
+       for (l = list; l; l = l->next) {
+               struct network_session *session = l->data;
+
+               if (session && session->io == io)
+                       return session;
+       }
+
+       return NULL;
+}
+
+static struct network_session *find_session_by_addr(GSList *list,
+                                                                                                       bdaddr_t dst_addr)
+{
+       GSList *l;
+
+       for (l = list; l; l = l->next) {
+               struct network_session *session = l->data;
+
+               if (!bacmp(&session->dst, &dst_addr))
+                       return session;
+
+       }
+
+       return NULL;
+}
+#endif
+
 static sdp_record_t *server_record_new(const char *name, uint16_t id)
 {
        sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
@@ -160,8 +197,13 @@ static sdp_record_t *server_record_new(const char *name, uint16_t id)
        sdp_data_t *v, *p;
        uint16_t psm = BNEP_PSM, version = 0x0100;
        uint16_t security_desc = (security ? 0x0001 : 0x0000);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint16_t net_access_type = 0x000a;
+       uint32_t max_net_access_rate = 0x001312d0;
+#else
        uint16_t net_access_type = 0xfffe;
        uint32_t max_net_access_rate = 0;
+#endif
        const char *desc = "Network service";
        sdp_record_t *record;
 
@@ -303,6 +345,56 @@ static void setup_destroy(void *user_data)
        session_free(setup);
 }
 
+#ifdef  TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean server_disconnected_cb(GIOChannel *chan,
+                       GIOCondition cond, gpointer user_data)
+{
+       struct network_server *ns = NULL;
+       struct network_session *session = NULL;
+       char address[20] = {0};
+       const char* paddr = address;
+       char *name_str = NULL;
+
+       info("server_disconnected_cb");
+
+       if (!user_data)
+               return FALSE;
+
+       ns = (struct network_server *) user_data;
+
+       session = find_session(ns->sessions, chan);
+       if (session) {
+               name_str = g_strdup(session->dev);
+               ba2str(&session->dst, address);
+       } else {
+               info("Session is not exist!");
+               name_str = g_strdup("bnep");
+       }
+
+       g_dbus_emit_signal(btd_get_dbus_connection(),
+                       adapter_get_path(ns->na->adapter),
+                       NETWORK_SERVER_INTERFACE, "PeerDisconnected",
+                       DBUS_TYPE_STRING, &name_str,
+                       DBUS_TYPE_STRING, &paddr,
+                       DBUS_TYPE_INVALID);
+
+       if (session) {
+               ns->sessions = g_slist_remove(ns->sessions, session);
+               session_free(session);
+       }
+
+       if (g_slist_length(ns->sessions) == 0 &&
+               name_str != NULL) {
+               bnep_if_down_wrapper(name_str);
+               ns->sessions = NULL;
+       }
+
+       g_free(name_str);
+
+       return FALSE;
+}
+#endif
+
 static gboolean bnep_setup(GIOChannel *chan,
                        GIOCondition cond, gpointer user_data)
 {
@@ -381,6 +473,30 @@ static gboolean bnep_setup(GIOChannel *chan,
                                                        packet, n) < 0)
                error("BNEP server cannot be added");
 
+#ifdef  TIZEN_FEATURE_BLUEZ_MODIFY
+if (ns) {
+       /* Emit connected signal to BT application */
+       const gchar *adapter_path = adapter_get_path(na->adapter);
+       const char *pdev = na->setup->dev;
+       char address[24] = { 0 };
+       char *paddr = address;
+
+       ba2str(&na->setup->dst, paddr);
+
+       ns->sessions = g_slist_append(ns->sessions, na->setup);
+
+       g_dbus_emit_signal(btd_get_dbus_connection(), adapter_path,
+                       NETWORK_SERVER_INTERFACE, "PeerConnected",
+                       DBUS_TYPE_STRING, &pdev,
+                       DBUS_TYPE_STRING, &paddr,
+                       DBUS_TYPE_INVALID);
+
+       na->setup->watch = g_io_add_watch_full(chan, G_PRIORITY_DEFAULT,
+                                       G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                                       server_disconnected_cb, ns, NULL);
+}
+#endif
+
        na->setup = NULL;
 
        return FALSE;
@@ -515,9 +631,11 @@ static void server_remove_sessions(struct network_server *ns)
                bnep_server_delete(ns->bridge, session->dev, &session->dst);
        }
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        g_slist_free_full(ns->sessions, session_free);
 
        ns->sessions = NULL;
+#endif
 }
 
 static void server_disconnect(DBusConnection *conn, void *user_data)
@@ -590,6 +708,11 @@ static DBusMessage *unregister_server(DBusConnection *conn,
        if (!ns)
                return btd_error_failed(msg, "Invalid UUID");
 
+#ifdef  TIZEN_FEATURE_BLUEZ_MODIFY
+       if (!ns->record_id)
+               return btd_error_not_available(msg);
+#endif
+
        reply = dbus_message_new_method_return(msg);
        if (!reply)
                return NULL;
@@ -645,6 +768,94 @@ static void path_unregister(void *data)
        adapter_free(na);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *disconnect_device(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct network_adapter *na = data;
+       struct network_server *ns;
+       struct network_session *session;
+       const char *addr = NULL;
+       bdaddr_t dst_addr;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       ns = find_server(na->servers, BNEP_SVC_NAP);
+
+       str2ba(addr, &dst_addr);
+       session = find_session_by_addr(ns->sessions, dst_addr);
+
+       if (session == NULL)
+               return btd_error_failed(msg, "No active session");
+
+       if (session->io == NULL)
+               return btd_error_not_connected(msg);
+
+       bnep_if_down_wrapper(session->dev);
+       bnep_conndel_wrapper(&dst_addr);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct network_adapter *na = data;
+       struct network_server *ns;
+       struct network_session *session;
+       const char *addr = NULL;
+       bdaddr_t dst_addr;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       dbus_bool_t connected;
+       const char *property;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+               DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+               DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       ns = find_server(na->servers, BNEP_SVC_NAP);
+
+       str2ba(addr, &dst_addr);
+       session = find_session_by_addr(ns->sessions, dst_addr);
+
+       connected = (session && session->io) ? TRUE : FALSE;
+       dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+       /* Interface */
+       property = session ? session->dev : "";
+       dict_append_entry(&dict, "Interface", DBUS_TYPE_STRING, &property);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+#endif
+
+#ifdef  TIZEN_FEATURE_BLUEZ_MODIFY
+static GDBusSignalTable server_signals[] = {
+       { GDBUS_SIGNAL("PeerConnected",
+                       GDBUS_ARGS({ "device", "s" }, { "address", "s" })) },
+       { GDBUS_SIGNAL("PeerDisconnected",
+                       GDBUS_ARGS({ "device", "s" }, { "address", "s" })) },
+       { }
+};
+#endif
+
 static const GDBusMethodTable server_methods[] = {
        { GDBUS_METHOD("Register",
                        GDBUS_ARGS({ "uuid", "s" }, { "bridge", "s" }), NULL,
@@ -652,6 +863,15 @@ static const GDBusMethodTable server_methods[] = {
        { GDBUS_METHOD("Unregister",
                        GDBUS_ARGS({ "uuid", "s" }), NULL,
                        unregister_server) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { GDBUS_METHOD("Disconnect",
+                       GDBUS_ARGS({ "address", "s" }), NULL,
+                       disconnect_device) },
+       { GDBUS_METHOD("GetProperties",
+                       GDBUS_ARGS({ "address", "s" }),
+                       GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+#endif
        { }
 };
 
@@ -709,6 +929,7 @@ int server_register(struct btd_adapter *adapter, uint16_t id)
        if (g_slist_length(na->servers) > 0)
                goto done;
 
+#ifndef  TIZEN_FEATURE_BLUEZ_MODIFY
        if (!g_dbus_register_interface(btd_get_dbus_connection(), path,
                                                NETWORK_SERVER_INTERFACE,
                                                server_methods, NULL, NULL, na,
@@ -718,6 +939,20 @@ int server_register(struct btd_adapter *adapter, uint16_t id)
                server_free(ns);
                return -1;
        }
+#else
+       ns->sessions = NULL;
+
+       if (!g_dbus_register_interface(btd_get_dbus_connection(),
+                                       path, NETWORK_SERVER_INTERFACE,
+                                       server_methods, server_signals,
+                                       NULL,
+                                       na, path_unregister)) {
+               error("D-Bus failed to register %s interface",
+                                       NETWORK_SERVER_INTERFACE);
+               server_free(ns);
+               return -1;
+       }
+#endif
 
        DBG("Registered interface %s on path %s", NETWORK_SERVER_INTERFACE,
                                                                        path);
diff --git a/profiles/proximity/immalert.c b/profiles/proximity/immalert.c
new file mode 100755 (executable)
index 0000000..adf9140
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Texas Instruments Corporation
+ *
+ *  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 <stdbool.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/adapter.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "attrib/gatt-service.h"
+#include "src/attrib-server.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/attio.h"
+#include "src/dbus-common.h"
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/att.h"
+ #include "btio/btio.h"
+#include "src/gatt-database.h"
+#endif
+
+#include "reporter.h"
+#include "immalert.h"
+
+struct imm_alert_adapter {
+       struct btd_adapter *adapter;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct gatt_db_attribute *imservice;
+#endif
+       GSList *connected_devices;
+};
+
+struct connected_device {
+       struct btd_device *device;
+       struct imm_alert_adapter *adapter;
+       uint8_t alert_level;
+       guint callback_id;
+};
+
+static GSList *imm_alert_adapters;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static bool get_dest_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
+{
+       GIOChannel *io = NULL;
+       GError *gerr = NULL;
+
+       io = g_io_channel_unix_new(bt_att_get_fd(att));
+       if (!io)
+               return false;
+
+       bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst,
+                                       BT_IO_OPT_DEST_TYPE, dst_type,
+                                       BT_IO_OPT_INVALID);
+
+       if (gerr) {
+               error("gatt: bt_io_get: %s", gerr->message);
+               g_error_free(gerr);
+               g_io_channel_unref(io);
+               return false;
+       }
+
+       g_io_channel_unref(io);
+       return true;
+}
+#endif
+
+static int imdevice_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct connected_device *condev = a;
+       const struct btd_device *device = b;
+
+       if (condev->device == device)
+               return 0;
+
+       return -1;
+}
+
+static struct connected_device *
+find_connected_device(struct imm_alert_adapter *ia, struct btd_device *device)
+{
+       GSList *l = g_slist_find_custom(ia->connected_devices, device,
+                                                               imdevice_cmp);
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+static int imadapter_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct imm_alert_adapter *imadapter = a;
+       const struct btd_adapter *adapter = b;
+
+       if (imadapter->adapter == adapter)
+               return 0;
+
+       return -1;
+}
+
+static struct imm_alert_adapter *
+find_imm_alert_adapter(struct btd_adapter *adapter)
+{
+       GSList *l = g_slist_find_custom(imm_alert_adapters, adapter,
+                                                               imadapter_cmp);
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+const char *imm_alert_get_level(struct btd_device *device)
+{
+       struct imm_alert_adapter *imadapter;
+       struct connected_device *condev;
+
+       if (!device)
+               return get_alert_level_string(NO_ALERT);
+
+       imadapter = find_imm_alert_adapter(device_get_adapter(device));
+       if (!imadapter)
+               return get_alert_level_string(NO_ALERT);
+
+       condev = find_connected_device(imadapter, device);
+       if (!condev)
+               return get_alert_level_string(NO_ALERT);
+
+       return get_alert_level_string(condev->alert_level);
+}
+
+static void imm_alert_emit_alert_signal(struct connected_device *condev,
+                                                       uint8_t alert_level)
+{
+       const char *path, *alert_level_str;
+
+       if (!condev)
+               return;
+
+       path = device_get_path(condev->device);
+       alert_level_str = get_alert_level_string(alert_level);
+
+       DBG("alert %s remote %s", alert_level_str, path);
+
+       g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+                       PROXIMITY_REPORTER_INTERFACE, "ImmediateAlertLevel");
+}
+
+static void imm_alert_remove_condev(struct connected_device *condev)
+{
+       struct imm_alert_adapter *ia;
+
+       if (!condev)
+               return;
+
+       ia = condev->adapter;
+
+       if (condev->callback_id && condev->device)
+               btd_device_remove_attio_callback(condev->device,
+                                                       condev->callback_id);
+
+       if (condev->device)
+               btd_device_unref(condev->device);
+
+       ia->connected_devices = g_slist_remove(ia->connected_devices, condev);
+       g_free(condev);
+}
+
+/* condev can be NULL */
+static void imm_alert_disc_cb(gpointer user_data)
+{
+       struct connected_device *condev = user_data;
+
+       if (!condev)
+               return;
+
+       DBG("immediate alert remove device %p", condev->device);
+
+       imm_alert_emit_alert_signal(condev, NO_ALERT);
+       imm_alert_remove_condev(condev);
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void imm_alert_alert_lvl_write(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       const uint8_t *value, size_t len,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct imm_alert_adapter *ia = user_data;
+       struct connected_device *condev = NULL;
+       uint8_t ecode = 0;
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+       struct btd_device *device = NULL;
+
+       if (!value || len == 0) {
+               ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+               goto done;
+       }
+
+       if (offset != 0) {
+               ecode = BT_ATT_ERROR_INVALID_OFFSET;
+               goto done;
+       }
+
+       if (!get_dest_info(att, &bdaddr, &bdaddr_type)) {
+               ecode = BT_ATT_ERROR_UNLIKELY;
+               goto done;
+       }
+
+       device = btd_adapter_get_device(ia->adapter, &bdaddr, bdaddr_type);
+       if (!device) {
+               ecode = BT_ATT_ERROR_UNLIKELY;
+               goto done;
+       }
+
+       /* Write value should be anyone of 0x00, 0x01, 0x02 */
+       if (value[0] > 0x02) {
+               ecode = 0x80;
+                goto done;
+        }
+
+       /* condev might remain NULL here if nothing is found */
+       condev = find_connected_device(ia, device);
+
+       /* Register a disconnect cb if the alert level is non-zero */
+       if (value[0] != NO_ALERT && !condev) {
+               condev = g_new0(struct connected_device, 1);
+               condev->device = btd_device_ref(device);
+               condev->adapter = ia;
+               condev->callback_id = btd_device_add_attio_callback(device,
+                                       NULL, imm_alert_disc_cb, condev);
+               ia->connected_devices = g_slist_append(ia->connected_devices,
+                                                               condev);
+               DBG("added connected dev %p", device);
+       }
+
+       if (condev) {
+               if (value[0] != NO_ALERT) {
+                       condev->alert_level = value[0];
+                       imm_alert_emit_alert_signal(condev, value[0]);
+               } else {
+                       imm_alert_emit_alert_signal(condev, value[0]);
+                       imm_alert_disc_cb(condev);
+               }
+       }
+
+       DBG("alert level set to %d by device %p", value[0], device);
+       gatt_db_attribute_write_result(attrib, id, ecode);
+       return;
+
+done:
+       error("Set immediate alert level for dev %p", device);
+       /* remove alerts by erroneous devices */
+       imm_alert_disc_cb(condev);
+       gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+void imm_alert_register(struct btd_adapter *adapter)
+{
+       bt_uuid_t uuid;
+       struct imm_alert_adapter *imadapter;
+       struct gatt_db_attribute *service, *charc;
+       struct gatt_db *db;
+
+       imadapter = g_new0(struct imm_alert_adapter, 1);
+       imadapter->adapter = adapter;
+
+       imm_alert_adapters = g_slist_append(imm_alert_adapters, imadapter);
+       db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(adapter));
+
+       /* Immediate Alert Service */
+       bt_uuid16_create(&uuid, IMMEDIATE_ALERT_SVC_UUID);
+       service = gatt_db_add_service(db, &uuid, true, 3);
+       if (!service)
+               goto err;
+
+       imadapter->imservice = service;
+
+        /*
+         * Alert Level characteristic.
+         */
+        bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+        charc = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_WRITE,
+                                               BT_GATT_CHRC_PROP_WRITE,
+                                                NULL,
+                                               imm_alert_alert_lvl_write, imadapter);
+       if (!charc)
+               goto err;
+
+       gatt_db_service_set_active(service, true);
+
+       DBG("Immediate Alert service added");
+       return;
+err:
+       DBG("Error adding Immediate Alert service");
+       imm_alert_unregister(adapter);
+}
+#else
+static uint8_t imm_alert_alert_lvl_write(struct attribute *a,
+                               struct btd_device *device, gpointer user_data)
+{
+       uint8_t value;
+       struct imm_alert_adapter *ia = user_data;
+       struct connected_device *condev = NULL;
+
+       if (!device)
+               goto set_error;
+
+       condev = find_connected_device(ia, device);
+
+       if (a->len == 0) {
+               DBG("Illegal alert level length");
+               goto set_error;
+       }
+
+       value = a->data[0];
+       if (value != NO_ALERT && value != MILD_ALERT && value != HIGH_ALERT) {
+               DBG("Illegal alert value");
+               goto set_error;
+       }
+
+       /* Register a disconnect cb if the alert level is non-zero */
+       if (value != NO_ALERT && !condev) {
+               condev = g_new0(struct connected_device, 1);
+               condev->device = btd_device_ref(device);
+               condev->adapter = ia;
+               condev->callback_id = btd_device_add_attio_callback(device,
+                                       NULL, imm_alert_disc_cb, condev);
+               ia->connected_devices = g_slist_append(ia->connected_devices,
+                                                               condev);
+               DBG("added connected dev %p", device);
+       }
+
+       if (value != NO_ALERT) {
+               condev->alert_level = value;
+               imm_alert_emit_alert_signal(condev, value);
+       }
+
+       /*
+        * Emit NO_ALERT if the alert level was non-zero before. This is
+        * guaranteed when there's a condev.
+        */
+       if (value == NO_ALERT && condev)
+               imm_alert_disc_cb(condev);
+
+       DBG("alert level set to %d by device %p", value, device);
+       return 0;
+
+set_error:
+       error("Set immediate alert level for dev %p", device);
+       /* remove alerts by erroneous devices */
+       imm_alert_disc_cb(condev);
+       return ATT_ECODE_IO;
+}
+
+void imm_alert_register(struct btd_adapter *adapter)
+{
+       gboolean svc_added;
+       bt_uuid_t uuid;
+       struct imm_alert_adapter *imadapter;
+
+       bt_uuid16_create(&uuid, IMMEDIATE_ALERT_SVC_UUID);
+
+       imadapter = g_new0(struct imm_alert_adapter, 1);
+       imadapter->adapter = adapter;
+
+       imm_alert_adapters = g_slist_append(imm_alert_adapters, imadapter);
+
+       /* Immediate Alert Service */
+       svc_added = gatt_service_add(adapter,
+                               GATT_PRIM_SVC_UUID, &uuid,
+                               /* Alert level characteristic */
+                               GATT_OPT_CHR_UUID16, ALERT_LEVEL_CHR_UUID,
+                               GATT_OPT_CHR_PROPS,
+                                       GATT_CHR_PROP_WRITE_WITHOUT_RESP,
+                               GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+                                       imm_alert_alert_lvl_write, imadapter,
+                               GATT_OPT_INVALID);
+
+       if (!svc_added) {
+               imm_alert_unregister(adapter);
+               return;
+       }
+
+       DBG("Immediate Alert service added");
+}
+#endif
+
+static void remove_condev_list_item(gpointer data, gpointer user_data)
+{
+       struct connected_device *condev = data;
+
+       imm_alert_remove_condev(condev);
+}
+
+void imm_alert_unregister(struct btd_adapter *adapter)
+{
+       struct imm_alert_adapter *imadapter;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct gatt_db *db;
+#endif
+
+       imadapter = find_imm_alert_adapter(adapter);
+       if (!imadapter)
+               return;
+
+       g_slist_foreach(imadapter->connected_devices, remove_condev_list_item,
+                                                                       NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* Remove registered service */
+       if (imadapter->imservice) {
+               db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(adapter));
+               gatt_db_remove_service(db, imadapter->imservice);
+       }
+#endif
+
+       imm_alert_adapters = g_slist_remove(imm_alert_adapters, imadapter);
+       g_free(imadapter);
+}
diff --git a/profiles/proximity/immalert.h b/profiles/proximity/immalert.h
new file mode 100755 (executable)
index 0000000..1a09fa9
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Texas Instruments Corporation
+ *
+ *
+ *  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
+ *
+ */
+
+void imm_alert_register(struct btd_adapter *adapter);
+void imm_alert_unregister(struct btd_adapter *adapter);
+const char *imm_alert_get_level(struct btd_device *device);
diff --git a/profiles/proximity/linkloss.c b/profiles/proximity/linkloss.c
new file mode 100755 (executable)
index 0000000..60b1064
--- /dev/null
@@ -0,0 +1,547 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Texas Instruments Corporation
+ *
+ *  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 <stdbool.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "attrib/att-database.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/gatt-service.h"
+#include "src/attrib-server.h"
+#include "src/service.h"
+#include "src/profile.h"
+#include "src/attio.h"
+#include "src/dbus-common.h"
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/att.h"
+#include "btio/btio.h"
+#include "src/gatt-database.h"
+#endif
+
+#include "reporter.h"
+#include "linkloss.h"
+
+struct link_loss_adapter {
+       struct btd_adapter *adapter;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct gatt_db_attribute *llservice;
+#else
+       uint16_t alert_lvl_value_handle;
+#endif
+       GSList *connected_devices;
+};
+
+struct connected_device {
+       struct btd_device *device;
+       struct link_loss_adapter *adapter;
+       uint8_t alert_level;
+       guint callback_id;
+       guint local_disc_id;
+};
+
+static GSList *link_loss_adapters;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static bool get_dest_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
+{
+       GIOChannel *io = NULL;
+       GError *gerr = NULL;
+
+       io = g_io_channel_unix_new(bt_att_get_fd(att));
+       if (!io)
+               return false;
+
+       bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst,
+                                       BT_IO_OPT_DEST_TYPE, dst_type,
+                                       BT_IO_OPT_INVALID);
+
+       if (gerr) {
+               error("gatt: bt_io_get: %s", gerr->message);
+               g_error_free(gerr);
+               g_io_channel_unref(io);
+               return false;
+       }
+
+       g_io_channel_unref(io);
+       return true;
+}
+#endif
+
+static int lldevice_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct connected_device *llcondev = a;
+       const struct btd_device *device = b;
+
+       if (llcondev->device == device)
+               return 0;
+
+       return -1;
+}
+
+static struct connected_device *
+find_connected_device(struct link_loss_adapter *la, struct btd_device *device)
+{
+       GSList *l = g_slist_find_custom(la->connected_devices, device,
+                                                               lldevice_cmp);
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+static int lladapter_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct link_loss_adapter *lladapter = a;
+       const struct btd_adapter *adapter = b;
+
+       if (lladapter->adapter == adapter)
+               return 0;
+
+       return -1;
+}
+
+static struct link_loss_adapter *
+find_link_loss_adapter(struct btd_adapter *adapter)
+{
+       GSList *l = g_slist_find_custom(link_loss_adapters, adapter,
+                                                       lladapter_cmp);
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+const char *link_loss_get_alert_level(struct btd_device *device)
+{
+       struct link_loss_adapter *lladapter;
+       struct connected_device *condev;
+
+       if (!device)
+               return get_alert_level_string(NO_ALERT);
+
+       lladapter = find_link_loss_adapter(device_get_adapter(device));
+       if (!lladapter)
+               return get_alert_level_string(NO_ALERT);
+
+       condev = find_connected_device(lladapter, device);
+       if (!condev)
+               return get_alert_level_string(NO_ALERT);
+
+       return get_alert_level_string(condev->alert_level);
+}
+
+static void link_loss_emit_alert_signal(struct connected_device *condev)
+{
+       const char *alert_level_str, *path;
+
+       if (!condev->device)
+               return;
+
+       path = device_get_path(condev->device);
+       alert_level_str = get_alert_level_string(condev->alert_level);
+
+       DBG("alert %s remote %s", alert_level_str, path);
+
+       g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+                       PROXIMITY_REPORTER_INTERFACE, "LinkLossAlertLevel");
+}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void link_loss_alert_lvl_read(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct link_loss_adapter *la = user_data;
+       struct connected_device *condev;
+       uint8_t value;
+       uint8_t ecode = 0;
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+       struct btd_device *device;
+
+       value = NO_ALERT;
+
+       if (offset != 0) {
+               ecode = BT_ATT_ERROR_INVALID_OFFSET;
+               goto out;
+       }
+
+       if (!get_dest_info(att, &bdaddr, &bdaddr_type)) {
+               ecode = BT_ATT_ERROR_UNLIKELY;
+               goto out;
+       }
+
+       device = btd_adapter_get_device(la->adapter, &bdaddr, bdaddr_type);
+       if (!device) {
+               ecode = BT_ATT_ERROR_UNLIKELY;
+               goto out;
+       }
+
+       condev = find_connected_device(la, device);
+       if (!condev) {
+               ecode = BT_ATT_ERROR_UNLIKELY;
+               goto out;
+       }
+
+       if (condev->alert_level)
+               value = condev->alert_level;
+       else
+               DBG("Alert Level is NULL");
+
+       DBG("Alert Level %d", value);
+out:
+       gatt_db_attribute_read_result(attrib, id, ecode, &value, sizeof(value));
+}
+#else
+static uint8_t link_loss_alert_lvl_read(struct attribute *a,
+                               struct btd_device *device, gpointer user_data)
+{
+       struct link_loss_adapter *la = user_data;
+       struct connected_device *condev;
+       uint8_t alert_level = NO_ALERT;
+
+       if (!device)
+               goto out;
+
+       condev = find_connected_device(la, device);
+       if (!condev)
+               goto out;
+
+       alert_level = condev->alert_level;
+
+out:
+       DBG("return alert level %d for dev %p", alert_level, device);
+
+       /* update the alert level according to the requesting device */
+       attrib_db_update(la->adapter, a->handle, NULL, &alert_level,
+                                               sizeof(alert_level), NULL);
+
+       return 0;
+}
+#endif
+
+/* condev can be NULL */
+static void link_loss_remove_condev(struct connected_device *condev)
+{
+       struct link_loss_adapter *la;
+
+       if (!condev)
+               return;
+
+       la = condev->adapter;
+
+       if (condev->callback_id && condev->device)
+               btd_device_remove_attio_callback(condev->device,
+                                                       condev->callback_id);
+
+       if (condev->local_disc_id && condev->device)
+               device_remove_disconnect_watch(condev->device,
+                                                       condev->local_disc_id);
+
+       if (condev->device)
+               btd_device_unref(condev->device);
+
+       la->connected_devices = g_slist_remove(la->connected_devices, condev);
+       g_free(condev);
+}
+
+static void link_loss_disc_cb(gpointer user_data)
+{
+       struct connected_device *condev = user_data;
+
+       DBG("alert loss disconnect device %p", condev->device);
+
+       /* if an alert-level is set, emit a signal */
+       if (condev->alert_level != NO_ALERT)
+               link_loss_emit_alert_signal(condev);
+
+       /* we are open for more changes now */
+       link_loss_remove_condev(condev);
+}
+
+static void link_loss_local_disc(struct btd_device *device,
+                                       gboolean removal, void *user_data)
+{
+       struct connected_device *condev = user_data;
+
+       /* no need to alert on this device - we requested disconnection */
+       link_loss_remove_condev(condev);
+
+       DBG("alert level zeroed for locally disconnecting dev %p", device);
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void link_loss_alert_lvl_write(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       const uint8_t *value, size_t len,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct link_loss_adapter *la = user_data;
+       struct connected_device *condev = NULL;
+        uint8_t ecode = 0;
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+       struct btd_device *device = NULL;
+
+       if (!value || len == 0) {
+               ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+               goto done;
+       }
+
+       if (offset != 0) {
+               ecode = BT_ATT_ERROR_INVALID_OFFSET;
+               goto done;
+       }
+
+       if (!get_dest_info(att, &bdaddr, &bdaddr_type)) {
+               ecode = BT_ATT_ERROR_UNLIKELY;
+               goto done;
+       }
+
+       device = btd_adapter_get_device(la->adapter, &bdaddr, bdaddr_type);
+       if (!device) {
+               ecode = BT_ATT_ERROR_UNLIKELY;
+               goto done;
+       }
+
+       /* Write value should be anyone of 0x00, 0x01, 0x02 */
+       if (value[0] > 0x02) {
+               ecode = 0x80;
+               goto done;
+       }
+
+       /* condev might remain NULL here if nothing is found */
+       condev = find_connected_device(la, device);
+
+       /* Register a disconnect cb if the alert level is non-zero */
+       if (value[0] != NO_ALERT && !condev) {
+               condev = g_new0(struct connected_device, 1);
+               condev->device = btd_device_ref(device);
+               condev->adapter = la;
+               condev->callback_id = btd_device_add_attio_callback(device,
+                                       NULL, link_loss_disc_cb, condev);
+               condev->local_disc_id = device_add_disconnect_watch(device,
+                                       link_loss_local_disc, condev, NULL);
+
+               la->connected_devices = g_slist_append(la->connected_devices,
+                                                               condev);
+       }
+
+       if (condev) {
+               if (value[0] != NO_ALERT) {
+                       condev->alert_level = value[0];
+                       link_loss_emit_alert_signal(condev);
+               } else {
+                       link_loss_emit_alert_signal(condev);
+                       link_loss_remove_condev(condev);
+                       condev = NULL;
+               }
+       }
+
+       DBG("alert level set to %d by device %p", value[0], device);
+        gatt_db_attribute_write_result(attrib, id, ecode);
+       return;
+done:
+       DBG("Set link loss alert level for dev %p", device);
+       /* reset alert level on erroneous devices */
+       link_loss_remove_condev(condev);
+        gatt_db_attribute_write_result(attrib, id, ecode);
+}
+#else
+static uint8_t link_loss_alert_lvl_write(struct attribute *a,
+                               struct btd_device *device, gpointer user_data)
+{
+       uint8_t value;
+       struct link_loss_adapter *la = user_data;
+       struct connected_device *condev = NULL;
+
+       if (!device)
+               goto set_error;
+
+       /* condev might remain NULL here if nothing is found */
+       condev = find_connected_device(la, device);
+
+       if (a->len == 0) {
+               DBG("Illegal alert level length");
+               goto set_error;
+       }
+
+       value = a->data[0];
+       if (value != NO_ALERT && value != MILD_ALERT && value != HIGH_ALERT) {
+               DBG("Illegal alert value");
+               goto set_error;
+       }
+
+       /* Register a disconnect cb if the alert level is non-zero */
+       if (value != NO_ALERT && !condev) {
+               condev = g_new0(struct connected_device, 1);
+               condev->device = btd_device_ref(device);
+               condev->adapter = la;
+               condev->callback_id = btd_device_add_attio_callback(device,
+                                       NULL, link_loss_disc_cb, condev);
+               condev->local_disc_id = device_add_disconnect_watch(device,
+                                       link_loss_local_disc, condev, NULL);
+
+               la->connected_devices = g_slist_append(la->connected_devices,
+                                                               condev);
+       } else if (value == NO_ALERT && condev) {
+               link_loss_remove_condev(condev);
+               condev = NULL;
+       }
+
+       DBG("alert level set to %d by device %p", value, device);
+
+       if (condev)
+               condev->alert_level = value;
+
+       return 0;
+
+set_error:
+       error("Set link loss alert level for dev %p", device);
+       /* reset alert level on erroneous devices */
+       link_loss_remove_condev(condev);
+       return ATT_ECODE_IO;
+}
+#endif
+
+void link_loss_register(struct btd_adapter *adapter)
+{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct gatt_db_attribute *service, *charc;
+       struct gatt_db *db;
+#else
+       gboolean svc_added;
+#endif
+       bt_uuid_t uuid;
+       struct link_loss_adapter *lladapter;
+
+       bt_uuid16_create(&uuid, LINK_LOSS_SVC_UUID);
+
+       lladapter = g_new0(struct link_loss_adapter, 1);
+       lladapter->adapter = adapter;
+
+       link_loss_adapters = g_slist_append(link_loss_adapters, lladapter);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(adapter));
+
+       /*
+        * Link Loss Service
+        */
+       service = gatt_db_add_service(db, &uuid, true, 3);
+       if (!service)
+               goto err;
+
+       lladapter->llservice = service;
+
+       /*
+        * Alert Level characteristic.
+        */
+       bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+       charc = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+                                               BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE,
+                                               link_loss_alert_lvl_read,
+                                               link_loss_alert_lvl_write, lladapter);
+
+       if (!charc)
+               goto err;
+
+       gatt_db_service_set_active(service, true);
+#else
+       /* Link Loss Service */
+       svc_added = gatt_service_add(adapter,
+                       GATT_PRIM_SVC_UUID, &uuid,
+                       /* Alert level characteristic */
+                       GATT_OPT_CHR_UUID16, ALERT_LEVEL_CHR_UUID,
+                       GATT_OPT_CHR_PROPS,
+                               GATT_CHR_PROP_READ | GATT_CHR_PROP_WRITE,
+                       GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+                               link_loss_alert_lvl_read, lladapter,
+                       GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+                               link_loss_alert_lvl_write, lladapter,
+                       GATT_OPT_CHR_VALUE_GET_HANDLE,
+                               &lladapter->alert_lvl_value_handle,
+                       GATT_OPT_INVALID);
+
+       if (!svc_added)
+               goto err;
+#endif
+       DBG("Link Loss service added");
+       return;
+
+err:
+       error("Error adding Link Loss service");
+       link_loss_unregister(adapter);
+}
+
+static void remove_condev_list_item(gpointer data, gpointer user_data)
+{
+       struct connected_device *condev = data;
+
+       link_loss_remove_condev(condev);
+}
+
+void link_loss_unregister(struct btd_adapter *adapter)
+{
+       struct link_loss_adapter *lladapter;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct gatt_db *db;
+#endif
+
+       lladapter = find_link_loss_adapter(adapter);
+       if (!lladapter)
+               return;
+
+       g_slist_foreach(lladapter->connected_devices, remove_condev_list_item,
+                       NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* Remove registered service */
+       if (lladapter->llservice) {
+               db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(adapter));
+               gatt_db_remove_service(db, lladapter->llservice);
+       }
+#endif
+
+       link_loss_adapters = g_slist_remove(link_loss_adapters, lladapter);
+       g_free(lladapter);
+}
diff --git a/profiles/proximity/linkloss.h b/profiles/proximity/linkloss.h
new file mode 100755 (executable)
index 0000000..0447def
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Texas Instruments Corporation
+ *
+ *
+ *  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
+ *
+ */
+
+void link_loss_register(struct btd_adapter *adapter);
+void link_loss_unregister(struct btd_adapter *adapter);
+const char *link_loss_get_alert_level(struct btd_device *device);
diff --git a/profiles/proximity/main.c b/profiles/proximity/main.c
new file mode 100755 (executable)
index 0000000..38a51f1
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <errno.h>
+#include <stdint.h>
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/plugin.h"
+#include "manager.h"
+
+static GKeyFile *config = NULL;
+
+static GKeyFile *open_config_file(const char *file)
+{
+       GError *gerr = NULL;
+       GKeyFile *keyfile;
+
+       keyfile = g_key_file_new();
+
+       g_key_file_set_list_separator(keyfile, ',');
+
+       if (!g_key_file_load_from_file(keyfile, file, 0, &gerr)) {
+               if (!g_error_matches(gerr, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+                       error("Parsing %s failed: %s", file, gerr->message);
+               g_error_free(gerr);
+               g_key_file_free(keyfile);
+               return NULL;
+       }
+
+       return keyfile;
+}
+
+static int proximity_init(void)
+{
+       config = open_config_file(CONFIGDIR "/proximity.conf");
+
+       if (proximity_manager_init(config) < 0)
+               return -EIO;
+
+       return 0;
+}
+
+static void proximity_exit(void)
+{
+       if (config)
+               g_key_file_free(config);
+
+       proximity_manager_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(proximity, VERSION,
+                       BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                       proximity_init, proximity_exit)
diff --git a/profiles/proximity/manager.c b/profiles/proximity/manager.c
new file mode 100755 (executable)
index 0000000..dbb3bda
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt.h"
+#include "monitor.h"
+#include "reporter.h"
+#include "manager.h"
+
+static struct enabled enabled  = {
+       .linkloss = TRUE,
+       .pathloss = TRUE,
+       .findme = TRUE,
+};
+
+static int monitor_linkloss_probe(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct gatt_primary *linkloss;
+
+       linkloss = btd_device_get_primary(device, LINK_LOSS_UUID);
+       if (linkloss == NULL)
+               return -1;
+
+       return monitor_register_linkloss(device, &enabled, linkloss);
+}
+
+static int monitor_immediate_probe(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct gatt_primary *immediate;
+
+       immediate = btd_device_get_primary(device, IMMEDIATE_ALERT_UUID);
+       if (immediate == NULL)
+               return -1;
+
+       return monitor_register_immediate(device, &enabled, immediate);
+}
+
+static int monitor_txpower_probe(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct gatt_primary *txpower;
+
+       txpower = btd_device_get_primary(device, TX_POWER_UUID);
+       if (txpower == NULL)
+               return -1;
+
+       return monitor_register_txpower(device, &enabled, txpower);
+}
+
+static void monitor_linkloss_remove(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+
+       monitor_unregister_linkloss(device);
+}
+
+static void monitor_immediate_remove(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+
+       monitor_unregister_immediate(device);
+}
+
+static void monitor_txpower_remove(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+
+       monitor_unregister_txpower(device);
+}
+
+static struct btd_profile pxp_monitor_linkloss_profile = {
+       .name           = "proximity-linkloss",
+       .remote_uuid    = LINK_LOSS_UUID,
+       .device_probe   = monitor_linkloss_probe,
+       .device_remove  = monitor_linkloss_remove,
+};
+
+static struct btd_profile pxp_monitor_immediate_profile = {
+       .name           = "proximity-immediate",
+       .remote_uuid    = IMMEDIATE_ALERT_UUID,
+       .device_probe   = monitor_immediate_probe,
+       .device_remove  = monitor_immediate_remove,
+};
+
+static struct btd_profile pxp_monitor_txpower_profile = {
+       .name           = "proximity-txpower",
+       .remote_uuid    = TX_POWER_UUID,
+       .device_probe   = monitor_txpower_probe,
+       .device_remove  = monitor_txpower_remove,
+};
+
+static struct btd_profile pxp_reporter_profile = {
+       .name           = "Proximity Reporter GATT Driver",
+       .remote_uuid    = GATT_UUID,
+       .device_probe   = reporter_device_probe,
+       .device_remove  = reporter_device_remove,
+
+       .adapter_probe  = reporter_adapter_probe,
+       .adapter_remove = reporter_adapter_remove,
+};
+
+static void load_config_file(GKeyFile *config)
+{
+       char **list;
+       int i;
+
+       if (config == NULL)
+               return;
+
+       list = g_key_file_get_string_list(config, "General", "Disable",
+                                                               NULL, NULL);
+       for (i = 0; list && list[i] != NULL; i++) {
+               if (g_str_equal(list[i], "FindMe"))
+                       enabled.findme = FALSE;
+               else if (g_str_equal(list[i], "LinkLoss"))
+                       enabled.linkloss = FALSE;
+               else if (g_str_equal(list[i], "PathLoss"))
+                       enabled.pathloss = FALSE;
+       }
+
+       g_strfreev(list);
+}
+
+int proximity_manager_init(GKeyFile *config)
+{
+       load_config_file(config);
+
+       if (btd_profile_register(&pxp_monitor_linkloss_profile) < 0)
+               goto fail;
+
+       if (btd_profile_register(&pxp_monitor_immediate_profile) < 0)
+               goto fail;
+
+       if (btd_profile_register(&pxp_monitor_txpower_profile) < 0)
+               goto fail;
+
+       if (btd_profile_register(&pxp_reporter_profile) < 0)
+               goto fail;
+
+       return 0;
+
+fail:
+       proximity_manager_exit();
+
+       return -1;
+}
+
+void proximity_manager_exit(void)
+{
+       btd_profile_unregister(&pxp_reporter_profile);
+       btd_profile_unregister(&pxp_monitor_txpower_profile);
+       btd_profile_unregister(&pxp_monitor_immediate_profile);
+       btd_profile_unregister(&pxp_monitor_linkloss_profile);
+}
diff --git a/profiles/proximity/manager.h b/profiles/proximity/manager.h
new file mode 100755 (executable)
index 0000000..e65c31d
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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
+ *
+ */
+
+int proximity_manager_init(GKeyFile *conf);
+void proximity_manager_exit(void);
diff --git a/profiles/proximity/monitor.c b/profiles/proximity/monitor.c
new file mode 100755 (executable)
index 0000000..a583eb7
--- /dev/null
@@ -0,0 +1,822 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/error.h"
+#include "src/log.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt.h"
+#include "src/attio.h"
+#include "src/textfile.h"
+
+#include "monitor.h"
+
+#define PROXIMITY_INTERFACE "org.bluez.ProximityMonitor1"
+
+#define ALERT_LEVEL_CHR_UUID 0x2A06
+#define POWER_LEVEL_CHR_UUID 0x2A07
+
+#define IMMEDIATE_TIMEOUT      5
+#define TX_POWER_SIZE          1
+
+enum {
+       ALERT_NONE = 0,
+       ALERT_MILD,
+       ALERT_HIGH,
+};
+
+struct monitor {
+       struct btd_device *device;
+       GAttrib *attrib;
+       struct att_range *linkloss;
+       struct att_range *txpower;
+       struct att_range *immediate;
+       struct enabled enabled;
+       char *linklosslevel;            /* Link Loss Alert Level */
+       char *fallbacklevel;            /* Immediate fallback alert level */
+       char *immediatelevel;           /* Immediate Alert Level */
+       char *signallevel;              /* Path Loss RSSI level */
+       uint16_t linklosshandle;        /* Link Loss Characteristic
+                                        * Value Handle */
+       uint16_t txpowerhandle;         /* Tx Characteristic Value Handle */
+       uint16_t immediatehandle;       /* Immediate Alert Value Handle */
+       guint immediateto;              /* Reset Immediate Alert to "none" */
+       guint attioid;
+};
+
+static GSList *monitors = NULL;
+
+static struct monitor *find_monitor(struct btd_device *device)
+{
+       GSList *l;
+
+       for (l = monitors; l; l = l->next) {
+               struct monitor *monitor = l->data;
+
+               if (monitor->device == device)
+                       return monitor;
+       }
+
+       return NULL;
+}
+
+static void write_proximity_config(struct btd_device *device, const char *alert,
+                                       const char *level)
+{
+       char *filename;
+       GKeyFile *key_file;
+       char *data;
+       gsize length = 0;
+
+       filename = btd_device_get_storage_path(device, "proximity");
+       if (!filename) {
+               warn("Unable to get proximity storage path for device");
+               return;
+       }
+
+       key_file = g_key_file_new();
+       g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+       if (level)
+               g_key_file_set_string(key_file, alert, "Level", level);
+       else
+               g_key_file_remove_group(key_file, alert, NULL);
+
+       data = g_key_file_to_data(key_file, &length, NULL);
+       if (length > 0) {
+               create_file(filename, S_IRUSR | S_IWUSR);
+               g_file_set_contents(filename, data, length, NULL);
+       }
+
+       g_free(data);
+       g_free(filename);
+       g_key_file_free(key_file);
+}
+
+static char *read_proximity_config(struct btd_device *device, const char *alert)
+{
+       char *filename;
+       GKeyFile *key_file;
+       char *str;
+
+       filename = btd_device_get_storage_path(device, "proximity");
+       if (!filename) {
+               warn("Unable to get proximity storage path for device");
+               return NULL;
+       }
+
+       key_file = g_key_file_new();
+       g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+       str = g_key_file_get_string(key_file, alert, "Level", NULL);
+
+       g_free(filename);
+       g_key_file_free(key_file);
+
+       return str;
+}
+
+static uint8_t str2level(const char *level)
+{
+       if (g_strcmp0("high", level) == 0)
+               return ALERT_HIGH;
+       else if (g_strcmp0("mild", level) == 0)
+               return ALERT_MILD;
+
+       return ALERT_NONE;
+}
+
+static void linkloss_written(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       struct monitor *monitor = user_data;
+       struct btd_device *device = monitor->device;
+       const char *path = device_get_path(device);
+
+       if (status != 0) {
+               error("Link Loss Write Request failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       if (!dec_write_resp(pdu, plen)) {
+               error("Link Loss Write Request: protocol error");
+               return;
+       }
+
+       DBG("Link Loss Alert Level written");
+
+       g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+                               PROXIMITY_INTERFACE, "LinkLossAlertLevel");
+}
+
+static void char_discovered_cb(uint8_t status, GSList *characteristics,
+                                                               void *user_data)
+{
+       struct monitor *monitor = user_data;
+       struct gatt_char *chr;
+       uint8_t value = str2level(monitor->linklosslevel);
+
+       if (status) {
+               error("Discover Link Loss handle: %s", att_ecode2str(status));
+               return;
+       }
+
+       DBG("Setting alert level \"%s\" on Reporter", monitor->linklosslevel);
+
+       /* Assume there is a single Alert Level characteristic */
+       chr = characteristics->data;
+       monitor->linklosshandle = chr->value_handle;
+
+       gatt_write_char(monitor->attrib, monitor->linklosshandle, &value, 1,
+                                               linkloss_written, monitor);
+}
+
+static int write_alert_level(struct monitor *monitor)
+{
+       struct att_range *linkloss = monitor->linkloss;
+       bt_uuid_t uuid;
+
+       if (monitor->linklosshandle) {
+               uint8_t value = str2level(monitor->linklosslevel);
+
+               gatt_write_char(monitor->attrib, monitor->linklosshandle,
+                                       &value, 1, linkloss_written, monitor);
+               return 0;
+       }
+
+       bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+
+       /* FIXME: use cache (requires service changed support) ? */
+       gatt_discover_char(monitor->attrib, linkloss->start, linkloss->end,
+                                       &uuid, char_discovered_cb, monitor);
+
+       return 0;
+}
+
+static void tx_power_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       uint8_t value[TX_POWER_SIZE];
+       ssize_t vlen;
+
+       if (status != 0) {
+               DBG("Tx Power Level read failed: %s", att_ecode2str(status));
+               return;
+       }
+
+       vlen = dec_read_resp(pdu, plen, value, sizeof(value));
+       if (vlen < 0) {
+               DBG("Protocol error");
+               return;
+       }
+
+       if (vlen != 1) {
+               DBG("Invalid length for TX Power value: %zd", vlen);
+               return;
+       }
+
+       DBG("Tx Power Level: %02x", (int8_t) value[0]);
+}
+
+static void tx_power_handle_cb(uint8_t status, GSList *characteristics,
+                                                               void *user_data)
+{
+       struct monitor *monitor = user_data;
+       struct gatt_char *chr;
+
+       if (status) {
+               error("Discover Tx Power handle: %s", att_ecode2str(status));
+               return;
+       }
+
+       chr = characteristics->data;
+       monitor->txpowerhandle = chr->value_handle;
+
+       DBG("Tx Power handle: 0x%04x", monitor->txpowerhandle);
+
+       gatt_read_char(monitor->attrib, monitor->txpowerhandle,
+                                                       tx_power_read_cb, monitor);
+}
+
+static void read_tx_power(struct monitor *monitor)
+{
+       struct att_range *txpower = monitor->txpower;
+       bt_uuid_t uuid;
+
+       if (monitor->txpowerhandle != 0) {
+               gatt_read_char(monitor->attrib, monitor->txpowerhandle,
+                                               tx_power_read_cb, monitor);
+               return;
+       }
+
+       bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
+
+       gatt_discover_char(monitor->attrib, txpower->start, txpower->end,
+                               &uuid, tx_power_handle_cb, monitor);
+}
+
+static gboolean immediate_timeout(gpointer user_data)
+{
+       struct monitor *monitor = user_data;
+       const char *path = device_get_path(monitor->device);
+
+       monitor->immediateto = 0;
+
+       if (g_strcmp0(monitor->immediatelevel, "none") == 0)
+               return FALSE;
+
+       if (monitor->attrib) {
+               uint8_t value = ALERT_NONE;
+               gatt_write_cmd(monitor->attrib, monitor->immediatehandle,
+                               &value, 1, NULL, NULL);
+       }
+
+       g_free(monitor->immediatelevel);
+       monitor->immediatelevel = g_strdup("none");
+
+
+       g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+                               PROXIMITY_INTERFACE, "ImmediateAlertLevel");
+
+       return FALSE;
+}
+
+static void immediate_written(gpointer user_data)
+{
+       struct monitor *monitor = user_data;
+       const char *path = device_get_path(monitor->device);
+
+       g_free(monitor->fallbacklevel);
+       monitor->fallbacklevel = NULL;
+
+
+       g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+                               PROXIMITY_INTERFACE, "ImmediateAlertLevel");
+
+       monitor->immediateto = g_timeout_add_seconds(IMMEDIATE_TIMEOUT,
+                                               immediate_timeout, monitor);
+}
+
+static void write_immediate_alert(struct monitor *monitor)
+{
+       uint8_t value = str2level(monitor->immediatelevel);
+
+       gatt_write_cmd(monitor->attrib, monitor->immediatehandle, &value, 1,
+                                               immediate_written, monitor);
+}
+
+static void immediate_handle_cb(uint8_t status, GSList *characteristics,
+                                                               void *user_data)
+{
+       struct monitor *monitor = user_data;
+       struct gatt_char *chr;
+
+       if (status) {
+               error("Discover Immediate Alert handle: %s",
+                                               att_ecode2str(status));
+               return;
+       }
+
+       chr = characteristics->data;
+       monitor->immediatehandle = chr->value_handle;
+
+       DBG("Immediate Alert handle: 0x%04x", monitor->immediatehandle);
+
+       if (monitor->fallbacklevel)
+               write_immediate_alert(monitor);
+}
+
+static void discover_immediate_handle(struct monitor *monitor)
+{
+       struct att_range *immediate = monitor->immediate;
+       bt_uuid_t uuid;
+
+       bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+
+       gatt_discover_char(monitor->attrib, immediate->start, immediate->end,
+                                       &uuid, immediate_handle_cb, monitor);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+       struct monitor *monitor = user_data;
+
+       monitor->attrib = g_attrib_ref(attrib);
+
+       if (monitor->enabled.linkloss)
+               write_alert_level(monitor);
+
+       if (monitor->enabled.pathloss)
+               read_tx_power(monitor);
+
+       if (monitor->immediatehandle == 0) {
+               if(monitor->enabled.pathloss || monitor->enabled.findme)
+                       discover_immediate_handle(monitor);
+       } else if (monitor->fallbacklevel)
+               write_immediate_alert(monitor);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+       struct monitor *monitor = user_data;
+       const char *path = device_get_path(monitor->device);
+
+       g_attrib_unref(monitor->attrib);
+       monitor->attrib = NULL;
+
+       if (monitor->immediateto == 0)
+               return;
+
+       g_source_remove(monitor->immediateto);
+       monitor->immediateto = 0;
+
+       if (g_strcmp0(monitor->immediatelevel, "none") == 0)
+               return;
+
+       g_free(monitor->immediatelevel);
+       monitor->immediatelevel = g_strdup("none");
+
+       g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+                               PROXIMITY_INTERFACE, "ImmediateAlertLevel");
+}
+
+static gboolean level_is_valid(const char *level)
+{
+       return (g_str_equal("none", level) ||
+                       g_str_equal("mild", level) ||
+                       g_str_equal("high", level));
+}
+
+static gboolean property_get_link_loss_level(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct monitor *monitor = data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+                                               &monitor->linklosslevel);
+
+       return TRUE;
+}
+
+static void property_set_link_loss_level(const GDBusPropertyTable *property,
+               DBusMessageIter *iter, GDBusPendingPropertySet id, void *data)
+{
+       struct monitor *monitor = data;
+       const char *level;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+               g_dbus_pending_property_error(id,
+                                       ERROR_INTERFACE ".InvalidArguments",
+                                       "Invalid arguments in method call");
+               return;
+       }
+
+       dbus_message_iter_get_basic(iter, &level);
+
+       if (!level_is_valid(level)) {
+               g_dbus_pending_property_error(id,
+                                       ERROR_INTERFACE ".InvalidArguments",
+                                       "Invalid arguments in method call");
+               return;
+       }
+
+       if (g_strcmp0(monitor->linklosslevel, level) == 0)
+               goto done;
+
+       g_free(monitor->linklosslevel);
+       monitor->linklosslevel = g_strdup(level);
+
+       write_proximity_config(monitor->device, "LinkLossAlertLevel", level);
+
+       if (monitor->attrib)
+               write_alert_level(monitor);
+
+done:
+       g_dbus_pending_property_success(id);
+}
+
+static gboolean property_exists_link_loss_level(
+                               const GDBusPropertyTable *property, void *data)
+{
+       struct monitor *monitor = data;
+
+       if (!monitor->enabled.linkloss)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean property_get_immediate_alert_level(
+                                       const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct monitor *monitor = data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+                                               &monitor->immediatelevel);
+
+       return TRUE;
+}
+
+static void property_set_immediate_alert_level(
+               const GDBusPropertyTable *property, DBusMessageIter *iter,
+               GDBusPendingPropertySet id, void *data)
+{
+       struct monitor *monitor = data;
+       struct btd_device *device = monitor->device;
+       const char *level;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+               g_dbus_pending_property_error(id,
+                                       ERROR_INTERFACE ".InvalidArguments",
+                                       "Invalid arguments in method call");
+               return;
+       }
+
+       dbus_message_iter_get_basic(iter, &level);
+
+       if (!level_is_valid(level)) {
+               g_dbus_pending_property_error(id,
+                                       ERROR_INTERFACE ".InvalidArguments",
+                                       "Invalid arguments in method call");
+               return;
+       }
+
+       if (g_strcmp0(monitor->immediatelevel, level) == 0)
+               goto done;
+
+       if (monitor->immediateto) {
+               g_source_remove(monitor->immediateto);
+               monitor->immediateto = 0;
+       }
+
+       /* Previous Immediate Alert level if connection/write fails */
+       g_free(monitor->fallbacklevel);
+       monitor->fallbacklevel = monitor->immediatelevel;
+
+       monitor->immediatelevel = g_strdup(level);
+
+       /*
+        * Means that Link/Path Loss are disabled or there is a pending
+        * writting for Find Me(Immediate Alert characteristic value).
+        * If enabled, Path Loss always registers a connection callback
+        * when the Proximity Monitor starts.
+        */
+       if (monitor->attioid == 0)
+               monitor->attioid = btd_device_add_attio_callback(device,
+                                                       attio_connected_cb,
+                                                       attio_disconnected_cb,
+                                                       monitor);
+       else if (monitor->attrib)
+               write_immediate_alert(monitor);
+
+done:
+       g_dbus_pending_property_success(id);
+}
+
+static gboolean property_exists_immediate_alert_level(
+                               const GDBusPropertyTable *property, void *data)
+{
+       struct monitor *monitor = data;
+
+       if (!(monitor->enabled.findme || monitor->enabled.pathloss))
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean property_get_signal_level(
+                                       const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct monitor *monitor = data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+                                               &monitor->signallevel);
+
+       return TRUE;
+}
+
+static gboolean property_exists_signal_level(const GDBusPropertyTable *property,
+                                                               void *data)
+{
+       struct monitor *monitor = data;
+
+       if (!monitor->enabled.pathloss)
+               return FALSE;
+
+       return TRUE;
+}
+
+static const GDBusPropertyTable monitor_device_properties[] = {
+       { "LinkLossAlertLevel", "s", property_get_link_loss_level,
+                                       property_set_link_loss_level,
+                                       property_exists_link_loss_level },
+       { "ImmediateAlertLevel", "s", property_get_immediate_alert_level,
+                                       property_set_immediate_alert_level,
+                                       property_exists_immediate_alert_level },
+       { "SignalLevel", "s", property_get_signal_level, NULL,
+                                       property_exists_signal_level },
+       { }
+};
+
+static void monitor_destroy(gpointer user_data)
+{
+       struct monitor *monitor = user_data;
+
+       monitors = g_slist_remove(monitors, monitor);
+
+       btd_device_unref(monitor->device);
+       g_free(monitor->linklosslevel);
+       g_free(monitor->immediatelevel);
+       g_free(monitor->signallevel);
+       g_free(monitor);
+}
+
+static struct monitor *register_monitor(struct btd_device *device)
+{
+       const char *path = device_get_path(device);
+       struct monitor *monitor;
+       char *level;
+
+       monitor = find_monitor(device);
+       if (monitor != NULL)
+               return monitor;
+
+       level = read_proximity_config(device, "LinkLossAlertLevel");
+
+       monitor = g_new0(struct monitor, 1);
+       monitor->device = btd_device_ref(device);
+       monitor->linklosslevel = (level ? : g_strdup("high"));
+       monitor->signallevel = g_strdup("unknown");
+       monitor->immediatelevel = g_strdup("none");
+
+       monitors = g_slist_append(monitors, monitor);
+
+       if (g_dbus_register_interface(btd_get_dbus_connection(), path,
+                               PROXIMITY_INTERFACE,
+                               NULL, NULL, monitor_device_properties,
+                               monitor, monitor_destroy) == FALSE) {
+               error("D-Bus failed to register %s interface",
+                                               PROXIMITY_INTERFACE);
+               monitor_destroy(monitor);
+               return NULL;
+       }
+
+       DBG("Registered interface %s on path %s", PROXIMITY_INTERFACE, path);
+
+       return monitor;
+}
+
+static void update_monitor(struct monitor *monitor)
+{
+       if (monitor->txpower != NULL && monitor->immediate != NULL)
+               monitor->enabled.pathloss = TRUE;
+       else
+               monitor->enabled.pathloss = FALSE;
+
+       DBG("Link Loss: %s, Path Loss: %s, FindMe: %s",
+                               monitor->enabled.linkloss ? "TRUE" : "FALSE",
+                               monitor->enabled.pathloss ? "TRUE" : "FALSE",
+                               monitor->enabled.findme ? "TRUE" : "FALSE");
+
+       if (!monitor->enabled.linkloss && !monitor->enabled.pathloss)
+               return;
+
+       if (monitor->attioid != 0)
+               return;
+
+       monitor->attioid = btd_device_add_attio_callback(monitor->device,
+                                                       attio_connected_cb,
+                                                       attio_disconnected_cb,
+                                                       monitor);
+}
+
+int monitor_register_linkloss(struct btd_device *device,
+                                               struct enabled *enabled,
+                                               struct gatt_primary *linkloss)
+{
+       struct monitor *monitor;
+
+       if (!enabled->linkloss)
+               return 0;
+
+       monitor = register_monitor(device);
+       if (monitor == NULL)
+               return -1;
+
+       monitor->linkloss = g_new0(struct att_range, 1);
+       monitor->linkloss->start = linkloss->range.start;
+       monitor->linkloss->end = linkloss->range.end;
+       monitor->enabled.linkloss = TRUE;
+
+       update_monitor(monitor);
+
+       return 0;
+}
+
+int monitor_register_txpower(struct btd_device *device,
+                                               struct enabled *enabled,
+                                               struct gatt_primary *txpower)
+{
+       struct monitor *monitor;
+
+       if (!enabled->pathloss)
+               return 0;
+
+       monitor = register_monitor(device);
+       if (monitor == NULL)
+               return -1;
+
+       monitor->txpower = g_new0(struct att_range, 1);
+       monitor->txpower->start = txpower->range.start;
+       monitor->txpower->end = txpower->range.end;
+
+       update_monitor(monitor);
+
+       return 0;
+}
+
+int monitor_register_immediate(struct btd_device *device,
+                                               struct enabled *enabled,
+                                               struct gatt_primary *immediate)
+{
+       struct monitor *monitor;
+
+       if (!enabled->pathloss && !enabled->findme)
+               return 0;
+
+       monitor = register_monitor(device);
+       if (monitor == NULL)
+               return -1;
+
+       monitor->immediate = g_new0(struct att_range, 1);
+       monitor->immediate->start = immediate->range.start;
+       monitor->immediate->end = immediate->range.end;
+       monitor->enabled.findme = enabled->findme;
+
+       update_monitor(monitor);
+
+       return 0;
+}
+
+static void cleanup_monitor(struct monitor *monitor)
+{
+       struct btd_device *device = monitor->device;
+       const char *path = device_get_path(device);
+
+       if (monitor->immediate != NULL || monitor->txpower != NULL)
+               return;
+
+       if (monitor->immediateto != 0) {
+               g_source_remove(monitor->immediateto);
+               monitor->immediateto = 0;
+       }
+
+       if (monitor->linkloss != NULL)
+               return;
+
+       if (monitor->attioid != 0) {
+               btd_device_remove_attio_callback(device, monitor->attioid);
+               monitor->attioid = 0;
+       }
+
+       if (monitor->attrib != NULL) {
+               g_attrib_unref(monitor->attrib);
+               monitor->attrib = NULL;
+       }
+
+       g_dbus_unregister_interface(btd_get_dbus_connection(), path,
+                                                       PROXIMITY_INTERFACE);
+}
+
+void monitor_unregister_linkloss(struct btd_device *device)
+{
+       struct monitor *monitor;
+
+       monitor = find_monitor(device);
+       if (monitor == NULL)
+               return;
+
+       g_free(monitor->linkloss);
+       monitor->linkloss = NULL;
+       monitor->enabled.linkloss = FALSE;
+
+       cleanup_monitor(monitor);
+}
+
+void monitor_unregister_txpower(struct btd_device *device)
+{
+       struct monitor *monitor;
+
+       monitor = find_monitor(device);
+       if (monitor == NULL)
+               return;
+
+       g_free(monitor->txpower);
+       monitor->txpower = NULL;
+       monitor->enabled.pathloss = FALSE;
+
+       cleanup_monitor(monitor);
+}
+
+void monitor_unregister_immediate(struct btd_device *device)
+{
+       struct monitor *monitor;
+
+       monitor = find_monitor(device);
+       if (monitor == NULL)
+               return;
+
+       g_free(monitor->immediate);
+       monitor->immediate = NULL;
+       monitor->enabled.findme = FALSE;
+       monitor->enabled.pathloss = FALSE;
+
+       cleanup_monitor(monitor);
+}
diff --git a/profiles/proximity/monitor.h b/profiles/proximity/monitor.h
new file mode 100755 (executable)
index 0000000..d9a40c6
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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
+ *
+ */
+
+struct enabled {
+       gboolean linkloss;
+       gboolean pathloss;
+       gboolean findme;
+};
+
+int monitor_register_linkloss(struct btd_device *device,
+                                               struct enabled *enabled,
+                                               struct gatt_primary *linkloss);
+int monitor_register_txpower(struct btd_device *device,
+                                               struct enabled *enabled,
+                                               struct gatt_primary *txpower);
+int monitor_register_immediate(struct btd_device *device,
+                                               struct enabled *enabled,
+                                               struct gatt_primary *immediate);
+
+void monitor_unregister_linkloss(struct btd_device *device);
+void monitor_unregister_txpower(struct btd_device *device);
+void monitor_unregister_immediate(struct btd_device *device);
diff --git a/profiles/proximity/reporter.c b/profiles/proximity/reporter.c
new file mode 100755 (executable)
index 0000000..30fc7c2
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "src/attrib-server.h"
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "src/error.h"
+#endif
+
+#include "reporter.h"
+#include "linkloss.h"
+#include "immalert.h"
+
+struct reporter_adapter {
+       struct btd_adapter *adapter;
+       GSList *devices;
+};
+
+static GSList *reporter_adapters;
+
+static int radapter_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct reporter_adapter *radapter = a;
+       const struct btd_adapter *adapter = b;
+
+       if (radapter->adapter == adapter)
+               return 0;
+
+       return -1;
+}
+
+static struct reporter_adapter *
+find_reporter_adapter(struct btd_adapter *adapter)
+{
+       GSList *l = g_slist_find_custom(reporter_adapters, adapter,
+                                                               radapter_cmp);
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+const char *get_alert_level_string(uint8_t level)
+{
+       switch (level) {
+       case NO_ALERT:
+               return "none";
+       case MILD_ALERT:
+               return "mild";
+       case HIGH_ALERT:
+               return "high";
+       }
+
+       return "unknown";
+}
+
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+static void register_tx_power(struct btd_adapter *adapter)
+{
+       uint16_t start_handle, h;
+       const int svc_size = 4;
+       uint8_t atval[256];
+       bt_uuid_t uuid;
+
+       bt_uuid16_create(&uuid, TX_POWER_SVC_UUID);
+       start_handle = attrib_db_find_avail(adapter, &uuid, svc_size);
+       if (start_handle == 0) {
+               error("Not enough free handles to register service");
+               return;
+       }
+
+       DBG("start_handle=0x%04x", start_handle);
+
+       h = start_handle;
+
+       /* Primary service definition */
+       bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+       put_le16(TX_POWER_SVC_UUID, &atval[0]);
+       attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+       /* Power level characteristic */
+       bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+       atval[0] = GATT_CHR_PROP_READ | GATT_CHR_PROP_NOTIFY;
+       put_le16(h + 1, &atval[1]);
+       put_le16(POWER_LEVEL_CHR_UUID, &atval[3]);
+       attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+       /* Power level value */
+       bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
+       atval[0] = 0x00;
+       attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1);
+
+       /* Client characteristic configuration */
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+       atval[0] = 0x00;
+       atval[1] = 0x00;
+       attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NONE, atval, 2);
+
+       g_assert(h - start_handle == svc_size);
+}
+#endif
+
+static gboolean property_get_link_loss_level(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct btd_device *device = data;
+       const char *level;
+
+       level = link_loss_get_alert_level(device);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &level);
+
+       return TRUE;
+}
+
+static gboolean property_get_immediate_alert_level(
+                                       const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct btd_device *device = data;
+       const char *level;
+
+       level = imm_alert_get_level(device);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &level);
+
+       return TRUE;
+}
+
+static const GDBusPropertyTable reporter_device_properties[] = {
+       { "LinkLossAlertLevel", "s", property_get_link_loss_level },
+       { "ImmediateAlertLevel", "s", property_get_immediate_alert_level },
+       { }
+};
+
+static void unregister_reporter_device(gpointer data, gpointer user_data)
+{
+       struct btd_device *device = data;
+       struct reporter_adapter *radapter = user_data;
+       const char *path = device_get_path(device);
+
+       DBG("unregister on device %s", path);
+
+       g_dbus_unregister_interface(btd_get_dbus_connection(), path,
+                                       PROXIMITY_REPORTER_INTERFACE);
+
+       radapter->devices = g_slist_remove(radapter->devices, device);
+       btd_device_unref(device);
+}
+
+static void register_reporter_device(struct btd_device *device,
+                                       struct reporter_adapter *radapter)
+{
+       const char *path = device_get_path(device);
+
+       DBG("register on device %s", path);
+
+       g_dbus_register_interface(btd_get_dbus_connection(), path,
+                                       PROXIMITY_REPORTER_INTERFACE,
+                                       NULL, NULL, reporter_device_properties,
+                                       device, NULL);
+
+       btd_device_ref(device);
+       radapter->devices = g_slist_prepend(radapter->devices, device);
+}
+
+int reporter_device_probe(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct reporter_adapter *radapter;
+       struct btd_adapter *adapter = device_get_adapter(device);
+
+       radapter = find_reporter_adapter(adapter);
+       if (!radapter)
+               return -1;
+
+       register_reporter_device(device, radapter);
+
+       return 0;
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *register_proximity(DBusConnection *conn, DBusMessage *msg,
+                                                               void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       if(adapter == NULL) {
+               DBG("Adapter is NULL");
+               return btd_error_invalid_args(msg);
+       }
+
+       link_loss_register(adapter);
+       imm_alert_register(adapter);
+
+       /* TODO: TX Power service implementation
+        * is incomplete in BlueZ.
+        */
+       //register_tx_power(adapter);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_proximity(DBusConnection *conn, DBusMessage *msg,
+                                                               void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       if(adapter == NULL) {
+               DBG("Adapter is NULL");
+               return btd_error_invalid_args(msg);
+       }
+
+       link_loss_unregister(adapter);
+       imm_alert_unregister(adapter);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable reporter_adapter_methods[] = {
+       { GDBUS_METHOD("RegisterProximity", NULL, NULL,
+                                               register_proximity) },
+       { GDBUS_METHOD("UnregisterProximity", NULL, NULL,
+                                               unregister_proximity) },
+       { }
+};
+#endif
+
+void reporter_device_remove(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct reporter_adapter *radapter;
+       struct btd_adapter *adapter = device_get_adapter(device);
+
+       radapter = find_reporter_adapter(adapter);
+       if (!radapter)
+               return;
+
+       unregister_reporter_device(device, radapter);
+}
+
+int reporter_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+       struct reporter_adapter *radapter;
+
+       radapter = g_new0(struct reporter_adapter, 1);
+       radapter->adapter = adapter;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       const char *path = adapter_get_path(adapter);
+
+       g_dbus_register_interface(btd_get_dbus_connection(), path,
+                                       PROXIMITY_REPORTER_INTERFACE,
+                                       reporter_adapter_methods,
+                                       NULL, NULL, adapter, NULL);
+#else
+       link_loss_register(adapter);
+       register_tx_power(adapter);
+       imm_alert_register(adapter);
+#endif
+       reporter_adapters = g_slist_prepend(reporter_adapters, radapter);
+       DBG("Proximity Reporter for adapter %p", adapter);
+
+       return 0;
+}
+
+void reporter_adapter_remove(struct btd_profile *p,
+                                               struct btd_adapter *adapter)
+{
+       struct reporter_adapter *radapter = find_reporter_adapter(adapter);
+       if (!radapter)
+               return;
+
+       g_slist_foreach(radapter->devices, unregister_reporter_device,
+                                                               radapter);
+
+       link_loss_unregister(adapter);
+       imm_alert_unregister(adapter);
+
+       reporter_adapters = g_slist_remove(reporter_adapters, radapter);
+       g_free(radapter);
+}
diff --git a/profiles/proximity/reporter.h b/profiles/proximity/reporter.h
new file mode 100755 (executable)
index 0000000..ed2c4dc
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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
+ *
+ */
+
+#define PROXIMITY_REPORTER_INTERFACE "org.bluez.ProximityReporter1"
+
+#define IMMEDIATE_ALERT_SVC_UUID       0x1802
+#define LINK_LOSS_SVC_UUID             0x1803
+#define TX_POWER_SVC_UUID              0x1804
+#define ALERT_LEVEL_CHR_UUID           0x2A06
+#define POWER_LEVEL_CHR_UUID           0x2A07
+
+enum {
+       NO_ALERT = 0x00,
+       MILD_ALERT = 0x01,
+       HIGH_ALERT = 0x02,
+};
+
+void reporter_device_remove(struct btd_service *service);
+int reporter_device_probe(struct btd_service *service);
+
+int reporter_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter);
+void reporter_adapter_remove(struct btd_profile *p,
+                                               struct btd_adapter *adapter);
+
+const char *get_alert_level_string(uint8_t level);
\ No newline at end of file
diff --git a/profiles/tds/manager.c b/profiles/tds/manager.c
new file mode 100755 (executable)
index 0000000..6d13ee3
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2016 Samsung Electronics Co. Ltd.
+ *
+ *  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 "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "src/plugin.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/adapter.h"
+#include "src/profile.h"
+
+#include "tds.h"
+
+int tds_provider_adapter_probe(struct btd_profile *p,
+                                       struct btd_adapter *adapter)
+{
+       tds_register_provider_interface(adapter);
+       return 0;
+}
+
+void tds_provider_adapter_remove(struct btd_profile *p,
+                                                struct btd_adapter *adapter)
+{
+       tds_unregister_provider_interface(adapter);
+}
+
+static struct btd_profile tds_provider = {
+       .name           = "TDS Provider GATT Driver",
+       .remote_uuid    = GATT_UUID,
+       .adapter_probe  = tds_provider_adapter_probe,
+       .adapter_remove = tds_provider_adapter_remove,
+};
+
+static int tds_provider_init(void)
+{
+       return btd_profile_register(&tds_provider);
+}
+
+static void tds_provider_exit(void)
+{
+       btd_profile_unregister(&tds_provider);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(tds, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                        tds_provider_init, tds_provider_exit)
diff --git a/profiles/tds/tds.c b/profiles/tds/tds.c
new file mode 100755 (executable)
index 0000000..786bf3d
--- /dev/null
@@ -0,0 +1,785 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2016 Samsung Electronics Co. Ltd.
+ *
+ *  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 <stdbool.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <time.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+#include "src/plugin.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/error.h"
+#include "src/log.h"
+#include "src/adapter.h"
+
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "attrib/gatt-service.h"
+
+#include "src/shared/gatt-server.h"
+#include "src/attrib-server.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/attio.h"
+#include "src/dbus-common.h"
+
+#include "tds.h"
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/att.h"
+ #include "btio/btio.h"
+#include "src/gatt-database.h"
+#endif
+
+
+#define TDS_USER_CHARACTERITIC_UUID            0x2af6
+#define TDS_USER_CHARACTERITIC_DESCRIPTOR_UUID 0x2a56
+
+/* TDS Block Data */
+struct tds_block_data {
+       uint8_t *val;
+       unsigned int len;
+};
+
+/* pointer to User characteristic data */
+static struct tds_block_data *ptr = NULL;
+
+/* Adapter Instance for the provider */
+struct tds_service_adapter {
+       struct btd_adapter *adapter;
+       struct gatt_db_attribute *service;
+       GSList *connected_devices;
+};
+
+static GSList *tds_service_adapters;
+
+struct connected_device {
+        struct btd_device *device;
+        struct tds_service_adapter *adapter;
+        guint callback_id;
+       uint16_t gatt_chr_handle;
+       unsigned int timeout_id;
+       bool tds_control_point_ccc_enabled;
+};
+
+static int tds_adapter_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct tds_service_adapter *tdsadapter = a;
+       const struct btd_adapter *adapter = b;
+
+       if (tdsadapter->adapter == adapter)
+               return 0;
+
+       return -1;
+}
+
+static struct tds_service_adapter *
+find_tds_service_adapter(struct btd_adapter *adapter)
+{
+       GSList *l = g_slist_find_custom(tds_service_adapters, adapter,
+                       tds_adapter_cmp);
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+static int device_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct connected_device *condev = a;
+       const struct btd_device *device = b;
+
+       if (condev->device == device)
+               return 0;
+
+       return -1;
+}
+
+static struct connected_device *
+find_connected_device(struct tds_service_adapter *adapter, struct btd_device *device)
+{
+       GSList *l = g_slist_find_custom(adapter->connected_devices, device,
+                       device_cmp);
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+static void indication_cfm_cb(void *user_data)
+{
+       struct connected_device *condev = user_data;
+       DBG("Received confirmation of Indication Confirmation");
+       g_dbus_emit_signal(btd_get_dbus_connection(), device_get_path(condev->device),
+                       TDS_SERVICE_PROVIDER_INTERFACE, "TdsActivationIndCnfm",
+                       DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *tds_activation_response(DBusConnection *connection,
+                                DBusMessage *msg, void *user_data)
+{
+       struct connected_device *condev = user_data;
+       uint8_t *value;
+       int32_t len = 0;
+       uint8_t result = 0x04; /* Operation Failed */
+       int k; /* Debug */
+       uint8_t *pdu = NULL;
+
+       DBG("+");
+       if (condev->tds_control_point_ccc_enabled == false) {
+               DBG("CCCD is disabled, can not send indication to remote device");
+               return dbus_message_new_method_return(msg);
+       }
+
+       if (condev->timeout_id == 0) {
+               DBG("Timer is not running: either no request pending or response came late!!");
+                return btd_error_failed(msg, "TDS Activation Request not pending");
+       }
+
+       /* Remove & reset Timer */
+       g_source_remove(condev->timeout_id);
+       condev->timeout_id =  0;
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_BYTE, &result,
+                               DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &value, &len,
+                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       DBG("Result [0x%x] data length [%d]", result, len);
+
+       for(k=0; k < len ; k++)
+               DBG("Data[%d] = [0x%x]", k, value[k]);
+
+       switch(result) {
+       case 0x00:
+               DBG("Success");
+               break;
+       case 0x02:
+               DBG("Invalid Parameter");
+               break;
+       case 0x03:
+               DBG("Unsupported Organization ID");
+               break;
+       case 0x04:
+               DBG("Operation Failed");
+               break;
+       default:
+               return btd_error_invalid_args(msg);
+       }
+
+       pdu = g_malloc0(sizeof(uint8_t)* (2+ len));
+       pdu[0] = 0x01; /* Opcode - TDS Control Point Activation Request */
+       pdu[1] = result;
+
+       if (len > 0) {
+               memcpy(pdu+2, value, len);
+       } else {
+               DBG("TDS Response with no parameters");
+       }
+
+       DBG("Send Indication to device [%s], chr handle [%d]",  device_get_path(condev->device), condev->gatt_chr_handle);
+
+       if (!bt_gatt_server_send_indication(btd_device_get_gatt_server(condev->device),
+                               condev->gatt_chr_handle,
+                               pdu, (2+len), indication_cfm_cb, condev, NULL))
+               DBG("Sending Indication Failed!!");
+       else
+               DBG("Sending Indication Successful, wait for confirmation!!");
+
+       g_free(pdu);
+       DBG("-");
+       return dbus_message_new_method_return(msg);
+}
+
+static void tds_client_remove_condev(struct connected_device *condev)
+{
+       struct tds_service_adapter *a;
+
+       if (!condev)
+               return;
+
+       a = condev->adapter;
+
+       if (condev->callback_id && condev->device)
+               btd_device_remove_attio_callback(condev->device,
+                               condev->callback_id);
+
+       if (condev->device)
+               btd_device_unref(condev->device);
+
+       a->connected_devices = g_slist_remove(a->connected_devices, condev);
+       g_free(condev);
+}
+
+static void tds_client_disc_cb(gpointer user_data)
+{
+       struct connected_device *condev = user_data;
+
+       if (!condev)
+               return;
+
+       /* Unregister Interface */
+       g_dbus_unregister_interface(btd_get_dbus_connection(),
+                       device_get_path(condev->device),
+                       TDS_SERVICE_PROVIDER_INTERFACE);
+
+       DBG("TDS Client remove device %p", condev->device);
+       tds_client_remove_condev(condev);
+}
+
+static const GDBusSignalTable tds_signals[] = {
+        { GDBUS_SIGNAL("TdsActivationRequested",
+                        GDBUS_ARGS({ "org_id", "y"},
+                                        { "TdsDataBlock", "ay"})) },
+       { GDBUS_SIGNAL("TdsActivationIndCnfm", NULL) },
+};
+
+static const GDBusMethodTable tds_methods[] = {
+       { GDBUS_ASYNC_METHOD("TdsActivationResponse",
+                       GDBUS_ARGS({ "result", "y" }, { "response_param", "ay" }), NULL,
+                       tds_activation_response) },
+       { }
+};
+
+static bool indication_wait_cb(gpointer user_data)
+{
+       struct connected_device *condev = (struct connected_device *)user_data;
+       uint16_t len = 2;
+       uint8_t pdu[2];
+       DBG("Indication Timer Expired!!");
+       condev->timeout_id =  0;
+
+       if (!condev->tds_control_point_ccc_enabled) {
+               DBG("CCCD is not Enabled!! No need to send indication");
+               return false;
+       } else {
+               DBG("CCCD is Enabled!!..Send Indication with Operation Failed!");
+       }
+
+       pdu[0] = 0x01; /* Op Code: Activation Request */
+       pdu[1] = 0x04; /* Result: Operation Failed*/
+
+       DBG("Send Indication to device [%s], chr handle [%d]",  device_get_path(condev->device), condev->gatt_chr_handle);
+
+       if (!bt_gatt_server_send_indication(btd_device_get_gatt_server(condev->device),
+                               condev->gatt_chr_handle,
+                               pdu, len, indication_cfm_cb, condev, NULL))
+               DBG("Sending Indication Failed!!");
+       else
+               DBG("Sending Indication Successful, wait for confirmation!!");
+
+       return false;
+}
+
+static void tds_control_point_char_write(struct gatt_db_attribute *attrib,
+                                        unsigned int id, uint16_t offset,
+                                        const uint8_t *value, size_t len,
+                                        uint8_t opcode, struct bt_att *att,
+                                        void *user_data)
+{
+       DBG("len [%d]", len);
+       DBG("Opcode [%d]", opcode);
+       DBG("TRansaction ID [%d]", id);
+       DBG("Offset [%d]", offset);
+
+       uint8_t ecode = 0;
+       struct btd_device *device = NULL;
+       struct tds_service_adapter *tsadapter = user_data;
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+       struct connected_device *condev = NULL;
+       int k;
+       const uint8_t *param = NULL;
+
+       if (!value || len < 2) {
+               ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+               goto done;
+       }
+
+       if (offset != 0) {
+               ecode = BT_ATT_ERROR_INVALID_OFFSET;
+               goto done;
+       }
+
+       if (!bt_att_get_remote_addr(att, &bdaddr, &bdaddr_type)) {
+               ecode = BT_ATT_ERROR_UNLIKELY;
+               goto done;
+       }
+
+       device = btd_adapter_get_device(tsadapter->adapter, &bdaddr, bdaddr_type);
+
+       if (!device) {
+               ecode = BT_ATT_ERROR_UNLIKELY;
+               goto done;
+       }
+       DBG("Device path [%s]", device_get_path(device));
+
+       /* Create Connected device and Register SIgnal Interface */
+       condev = find_connected_device(tsadapter, device);
+
+       if (!condev) {
+               DBG("Device is NULL..create device");
+               condev = g_new0(struct connected_device, 1);
+               condev->device = btd_device_ref(device);
+               condev->adapter = tsadapter;
+               condev->callback_id = btd_device_add_attio_callback(device,
+                               NULL, tds_client_disc_cb, condev);
+
+               tsadapter->connected_devices = g_slist_append(tsadapter->connected_devices,
+                               condev);
+               DBG("added connected dev %p", device);
+               /* Register Signal on Device Interface */
+               if (!g_dbus_register_interface(btd_get_dbus_connection(), device_get_path(device),
+                                       TDS_SERVICE_PROVIDER_INTERFACE,
+                                       tds_methods, tds_signals,
+                                       NULL,
+                                       condev, NULL)) {
+                       error("Unable to register TDS Activation Signal");
+                       tds_client_remove_condev(condev);
+                       goto done;
+               }
+       }
+
+       if (condev->timeout_id) {
+               DBG("Already one activation request is under progress from device [%s]", device_get_path(device));
+               ecode = BT_ERROR_ALREADY_IN_PROGRESS;
+               goto done;
+       }
+
+       condev->gatt_chr_handle = gatt_db_attribute_get_handle(attrib);
+       DBG("Characteristic Attribute handle [0x%x]", condev->gatt_chr_handle);
+
+       /* Write value should be anyone of 0x00, 0x01, 0x02 */
+       switch(value[0]) {
+       case 0x00: {
+               DBG("Opcode reserved for future use");
+               ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+               goto done;
+       }
+       case 0x01: {
+               DBG("TDS Control Point Activation Request");
+               break;
+       }
+       default: {
+               DBG("Invalid Opcode [0x%x]", value[0]);
+               ecode = 0x80;
+               goto done;
+       }
+       }
+
+       for(k=0; k < len; k++)
+               DBG("@@TDS Control Point [%d] 0x%x", k, value[k]);
+
+       /* Success case*/
+       if (gatt_db_attribute_write_result(attrib, id, ecode)) {
+               DBG("TDS Control Point Activation write resp sent successfully!!");
+               /* Emit Signal */
+               len = len -2;
+
+               if (len > 0) {
+                       param = &value[2];
+               }
+               g_dbus_emit_signal(btd_get_dbus_connection(), device_get_path(device),
+                               TDS_SERVICE_PROVIDER_INTERFACE, "TdsActivationRequested",
+                               DBUS_TYPE_BYTE, &value[1],
+                               DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &param, len,
+                               DBUS_TYPE_INVALID);
+
+               /* Start timer for max 10 seconds to wait for Indication from app */
+               if (condev->tds_control_point_ccc_enabled) {
+                       DBG("Control point is enabled for device [%s] start the Indication Timer", device_get_path(device));
+                       if (condev->timeout_id)
+                               g_source_remove(condev->timeout_id);
+                       condev->timeout_id = g_timeout_add(10000, (GSourceFunc)indication_wait_cb, condev);
+               } else {
+                       DBG("Control point is Not enabled for device [%s] Dont start the Indication Timer",device_get_path(device));
+               }
+       } else {
+               DBG("TDS Control Point Activation write resp sending failed!!!");
+       }
+
+       return;
+done:
+       gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static void tds_user_data_descriptor_read_cb(struct gatt_db_attribute *attrib,
+                                        unsigned int id, uint16_t offset,
+                                        uint8_t opcode, struct bt_att *att,
+                                        void *user_data)
+{
+       DBG("TDS User Characteritsic descriptor Read requested..");
+
+       if (!ptr) {
+               DBG("TDS Block data still not set");
+               gatt_db_attribute_read_result(attrib, id, 0, NULL, 0);
+       } else {
+               gatt_db_attribute_read_result(attrib, id, 0, ptr->val, ptr->len);
+       }
+}
+
+static void tds_control_point_ccc_read_cb(struct gatt_db_attribute *attrib,
+                                        unsigned int id, uint16_t offset,
+                                        uint8_t opcode, struct bt_att *att,
+                                        void *user_data)
+{
+       struct tds_service_adapter *adapter = user_data;
+       struct btd_device *device = NULL;
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+       struct connected_device *condev = NULL;
+       uint8_t ecode = 0;
+       uint8_t value[2];
+       DBG("TDS Control Point CCC Read requested..");
+
+       if (!bt_att_get_remote_addr(att, &bdaddr, &bdaddr_type)) {
+               ecode = BT_ATT_ERROR_UNLIKELY;
+               goto done;
+       }
+
+       device = btd_adapter_get_device(adapter->adapter, &bdaddr, bdaddr_type);
+
+       if (!device) {
+               ecode = BT_ATT_ERROR_UNLIKELY;
+               goto done;
+       }
+       DBG("Device path [%s]", device_get_path(device));
+
+       /* Create Connected device and Register Signal Interface */
+       condev = find_connected_device(adapter, device);
+       if (!condev) {
+               DBG("Device is not created yet, default CCCD value is Disabled");
+               value[0] = 0x00;
+       } else {
+               DBG("CCCD is [%s] for device [%s]", condev->tds_control_point_ccc_enabled ? "Enabled" : "Disabled", device_get_path(device));
+               value[0] = condev->tds_control_point_ccc_enabled;
+       }
+
+       value[1] = 0x00;
+
+done:
+       gatt_db_attribute_read_result(attrib, id, ecode, value, 2);
+}
+
+
+static void tds_user_char_read_cb(struct gatt_db_attribute *attrib,
+                                        unsigned int id, uint16_t offset,
+                                        uint8_t opcode, struct bt_att *att,
+                                        void *user_data)
+{
+       uint8_t value[1];
+       DBG("TDS user char Read requested..");
+       value[0] = 0x01;
+       gatt_db_attribute_read_result(attrib, id, 0, value, 1);
+}
+
+static void tds_control_point_ccc_write_cb(struct gatt_db_attribute *attrib,
+                                        unsigned int id, uint16_t offset,
+                                        const uint8_t *value, size_t len,
+                                        uint8_t opcode, struct bt_att *att,
+                                        void *user_data)
+{
+       struct tds_service_adapter *adapter = user_data;
+       struct btd_device *device = NULL;
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+       struct connected_device *condev = NULL;
+       uint8_t ecode = 0;
+       DBG("TDS Control Point CCC Write requested..len [%d] val [0x%x] val [0x%x]", len, value[0], value[1]);
+
+       if (!value || len != 2) {
+               ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+               goto done;
+       }
+
+       if (offset) {
+               ecode = BT_ATT_ERROR_INVALID_OFFSET;
+               goto done;
+       }
+
+       if (!bt_att_get_remote_addr(att, &bdaddr, &bdaddr_type)) {
+               ecode = BT_ATT_ERROR_UNLIKELY;
+               goto done;
+       }
+
+       device = btd_adapter_get_device(adapter->adapter, &bdaddr, bdaddr_type);
+
+       if (!device) {
+               ecode = BT_ATT_ERROR_UNLIKELY;
+               goto done;
+       }
+       DBG("Device path [%s]", device_get_path(device));
+
+       /* Create Connected device and Register Signal Interface */
+       condev = find_connected_device(adapter, device);
+
+       if (!condev) {
+               DBG("Device is NULL..create device");
+               condev = g_new0(struct connected_device, 1);
+               condev->device = btd_device_ref(device);
+               condev->adapter = adapter;
+               condev->callback_id = btd_device_add_attio_callback(device,
+                               NULL, tds_client_disc_cb, condev);
+
+               adapter->connected_devices = g_slist_append(adapter->connected_devices,
+                               condev);
+               DBG("added connected dev %p", device);
+
+               /* Register Signal on Device Interface */
+               if (!g_dbus_register_interface(btd_get_dbus_connection(), device_get_path(device),
+                                       TDS_SERVICE_PROVIDER_INTERFACE,
+                                       tds_methods, tds_signals,
+                                       NULL,
+                                       condev, NULL)) {
+                       error("Unable to register TDS Activation Signal");
+                       tds_client_remove_condev(condev);
+                       goto done;
+               }
+       }
+
+       if (value[0] == 0x00) {
+               DBG("CCCD is Disabled by Client [%s]", device_get_path(device));
+               condev->tds_control_point_ccc_enabled = false;
+       } else if (value[0] == 0x02) { /* Indication */
+               if (condev->tds_control_point_ccc_enabled) {
+                       DBG("TDS Control point CCCD Already Enabled\n");
+                       goto done;
+               }
+
+               DBG("CCCD is Enabled by Client [%s]", device_get_path(device));
+               condev->tds_control_point_ccc_enabled = true;
+       } else
+               ecode = 0x80;
+
+       DBG("TDS Server: Control Point Enabled: [%s]\n",
+                       condev->tds_control_point_ccc_enabled ? "true" : "false");
+
+done:
+       gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+void tds_service_unregister(struct tds_service_adapter *tsadapter)
+{
+       DBG("TDS Service UnRegister..");
+       struct gatt_db *db;
+
+       /* Remove registered service */
+       if (tsadapter->service) {
+               db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(tsadapter->adapter));
+               gatt_db_remove_service(db, tsadapter->service);
+       }
+
+       if (ptr) {
+               g_free(ptr->val);
+               g_free(ptr);
+               ptr = NULL;
+       }
+}
+
+void tds_service_register(struct tds_service_adapter *tsadapter)
+{
+       DBG("TDS Service Register..");
+       struct gatt_db_attribute *service, *char_tds_control, *char_user_char, *desc_tds_ccc, *desc_user;
+       struct gatt_db *db;
+
+       bt_uuid_t uuid;
+       bt_uuid16_create(&uuid, TRANSPORT_DISCOVERY_SERVICE_UUID);
+
+       db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(tsadapter->adapter));
+
+       /*
+        * TDS Primary Service
+        */
+       service = gatt_db_add_service(db, &uuid, true, 7);
+       if (!service)
+               goto err;
+
+       tsadapter->service = service;
+       DBG("TDS Primary Service added");
+
+       /*
+        * TDS Control Point characteristic.
+        */
+       bt_uuid16_create(&uuid, TDS_CONTROL_POINT_CHARACTERISTIC_UUID);
+       char_tds_control = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_WRITE,
+                       BT_GATT_CHRC_PROP_WRITE | BT_GATT_CHRC_PROP_INDICATE,
+                       NULL, /* Non Readable */
+                       tds_control_point_char_write, tsadapter);
+
+       if (!char_tds_control)
+               goto err;
+       DBG("TDS Control Point char added");
+
+
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+       desc_tds_ccc =  gatt_db_service_add_descriptor(char_tds_control, &uuid,
+                       BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+                       tds_control_point_ccc_read_cb,
+                       tds_control_point_ccc_write_cb, tsadapter);
+
+       if (!desc_tds_ccc)
+               goto err;
+       DBG("TDS Control Point CCCD added");
+       /*
+        * TDS User characteristic.
+        */
+       bt_uuid16_create(&uuid, TDS_USER_CHARACTERITIC_UUID);
+       char_user_char = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_NONE,
+                       BT_ATT_PERM_READ,
+                       tds_user_char_read_cb,
+                       NULL, /* Non Writable */
+                       NULL);
+
+       if (!char_user_char)
+               goto err;
+
+       DBG("TDS User Characteristic added");
+       bt_uuid16_create(&uuid, TDS_USER_CHARACTERITIC_DESCRIPTOR_UUID);
+       desc_user = gatt_db_service_add_descriptor(char_user_char, &uuid,
+                       BT_ATT_PERM_READ,
+                       tds_user_data_descriptor_read_cb,
+                       NULL, /* Non Writable */
+                       tsadapter);
+       if (!desc_user)
+               goto err;
+
+       DBG("TDS User Char Descriptor added...");
+       gatt_db_service_set_active(service, true);
+
+       DBG("TDS Service activated");
+       return;
+
+err:
+       error("Error adding TDS service");
+       tds_service_unregister(tsadapter);
+}
+
+static DBusMessage *register_tds_proider(DBusConnection *conn, DBusMessage *msg,
+                                                                void *user_data)
+{
+       DBG("TDS Provider Register");
+       struct tds_service_adapter *tsadapter = user_data;
+
+       if (tsadapter->adapter == NULL) {
+               DBG("Adapter is NULL");
+               return btd_error_invalid_args(msg);
+       }
+
+       tds_service_register(tsadapter);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_tds_block_data(DBusConnection *conn,
+                                                DBusMessage *msg, void *data)
+{
+       uint8_t *value;
+       int32_t len = 0;
+
+       DBG("Set TDS Block data");
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &value, &len,
+                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       /*TODO Max length to be checked*/
+       if (len < 1)
+               return btd_error_invalid_args(msg);
+
+       if (ptr) {
+               g_free(ptr->val);
+               g_free(ptr);
+       }
+       ptr = g_malloc0(sizeof(struct tds_block_data));
+       ptr->val = g_memdup(value, len);
+       ptr->len = len;
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_tds_provider(DBusConnection *conn, DBusMessage *msg,
+                                                                void *user_data)
+{
+       struct tds_service_adapter *tsadapter = user_data;
+
+       if (tsadapter->adapter == NULL) {
+               DBG("Adapter is NULL");
+               return btd_error_invalid_args(msg);
+       }
+
+       tds_service_unregister(tsadapter);
+       return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable tds_provider_adapter_methods[] = {
+       { GDBUS_METHOD("RegisterTdsProvider", NULL, NULL,
+                       register_tds_proider) },
+       { GDBUS_METHOD("UnregisterTdsProvider", NULL, NULL,
+                       unregister_tds_provider) },
+       { GDBUS_METHOD("SetTdsBlockData",
+                       GDBUS_ARGS({ "value", "ay" }), NULL,
+                       set_tds_block_data) },
+       { }
+};
+
+void tds_unregister_provider_interface(struct btd_adapter *adapter)
+{
+       struct tds_service_adapter *tsadapter = find_tds_service_adapter(adapter);
+       if (!tsadapter)
+               return;
+       tds_service_unregister(tsadapter);
+
+       tds_service_adapters = g_slist_remove(tds_service_adapters, tsadapter);
+       g_free(tsadapter);
+}
+
+void tds_register_provider_interface(struct btd_adapter *adapter)
+{
+       struct tds_service_adapter *tsadapter;
+       const char *path = adapter_get_path(adapter);
+
+       tsadapter = g_new0(struct tds_service_adapter, 1);
+       tsadapter->adapter = adapter;
+
+       g_dbus_register_interface(btd_get_dbus_connection(), path,
+                       TDS_SERVICE_PROVIDER_INTERFACE,
+                       tds_provider_adapter_methods,
+                       NULL, NULL, tsadapter, NULL);
+       tds_service_adapters = g_slist_append(tds_service_adapters, tsadapter);
+}
diff --git a/profiles/tds/tds.h b/profiles/tds/tds.h
new file mode 100755 (executable)
index 0000000..85b9b10
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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
+ *
+ */
+
+#define TRANSPORT_DISCOVERY_SERVICE_UUID       0x1824
+#define TDS_CONTROL_POINT_CHARACTERISTIC_UUID  0x2abc
+
+#define TDS_USER_CHARACTERITIC_UUID            0x2af6
+#define TDS_USER_CHARACTERITIC_DESCRIPTOR_UUID 0x2a56
+
+#define TDS_SERVICE_PROVIDER_INTERFACE "org.bluez.TdsServiceProvider1"
+
+void tds_register_provider_interface(struct btd_adapter *adapter);
+
+void tds_unregister_provider_interface(struct btd_adapter *adapter);
diff --git a/profiles/thermometer/thermometer.c b/profiles/thermometer/thermometer.c
new file mode 100755 (executable)
index 0000000..b0fc3e0
--- /dev/null
@@ -0,0 +1,1321 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  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 <stdbool.h>
+#include <errno.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
+#include "src/error.h"
+#include "src/log.h"
+#include "attrib/gattrib.h"
+#include "src/attio.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+
+#define THERMOMETER_INTERFACE          "org.bluez.Thermometer1"
+#define THERMOMETER_MANAGER_INTERFACE  "org.bluez.ThermometerManager1"
+#define THERMOMETER_WATCHER_INTERFACE  "org.bluez.ThermometerWatcher1"
+
+/* Temperature measurement flag fields */
+#define TEMP_UNITS             0x01
+#define TEMP_TIME_STAMP                0x02
+#define TEMP_TYPE              0x04
+
+#define FLOAT_MAX_MANTISSA     16777216 /* 2^24 */
+
+#define VALID_RANGE_DESC_SIZE  4
+#define TEMPERATURE_TYPE_SIZE  1
+#define MEASUREMENT_INTERVAL_SIZE      2
+
+struct thermometer_adapter {
+       struct btd_adapter      *adapter;
+       GSList                  *devices;
+       GSList                  *fwatchers;     /* Final measurements */
+       GSList                  *iwatchers;     /* Intermediate measurements */
+};
+
+struct thermometer {
+       struct btd_device               *dev;           /* Device reference */
+       struct thermometer_adapter      *tadapter;
+       GAttrib                         *attrib;        /* GATT connection */
+       struct att_range                *svc_range;     /* Thermometer range */
+       guint                           attioid;        /* Att watcher id */
+       /* attio id for Temperature Measurement value indications */
+       guint                           attio_measurement_id;
+       /* attio id for Intermediate Temperature value notifications */
+       guint                           attio_intermediate_id;
+       /* attio id for Measurement Interval value indications */
+       guint                           attio_interval_id;
+       gboolean                        intermediate;
+       uint8_t                         type;
+       uint16_t                        interval;
+       uint16_t                        max;
+       uint16_t                        min;
+       gboolean                        has_type;
+       gboolean                        has_interval;
+
+       uint16_t                        measurement_ccc_handle;
+       uint16_t                        intermediate_ccc_handle;
+       uint16_t                        interval_val_handle;
+};
+
+struct characteristic {
+       struct thermometer      *t;     /* Thermometer where the char belongs */
+       char                    uuid[MAX_LEN_UUID_STR + 1];
+};
+
+struct watcher {
+       struct thermometer_adapter      *tadapter;
+       guint                           id;
+       char                            *srv;
+       char                            *path;
+};
+
+struct measurement {
+       struct thermometer      *t;
+       int16_t                 exp;
+       int32_t                 mant;
+       uint64_t                time;
+       gboolean                suptime;
+       char                    *unit;
+       char                    *type;
+       char                    *value;
+};
+
+struct tmp_interval_data {
+       struct thermometer      *thermometer;
+       uint16_t                interval;
+};
+
+static GSList *thermometer_adapters = NULL;
+
+static const char * const temp_type[] = {
+       "<reserved>",
+       "armpit",
+       "body",
+       "ear",
+       "finger",
+       "intestines",
+       "mouth",
+       "rectum",
+       "toe",
+       "tympanum"
+};
+
+static const char *temptype2str(uint8_t value)
+{
+        if (value > 0 && value < G_N_ELEMENTS(temp_type))
+               return temp_type[value];
+
+       error("Temperature type %d reserved for future use", value);
+       return NULL;
+}
+
+static void destroy_watcher(gpointer user_data)
+{
+       struct watcher *watcher = user_data;
+
+       g_free(watcher->path);
+       g_free(watcher->srv);
+       g_free(watcher);
+}
+
+static void remove_watcher(gpointer user_data)
+{
+       struct watcher *watcher = user_data;
+
+       g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+}
+
+static void destroy_thermometer(gpointer user_data)
+{
+       struct thermometer *t = user_data;
+
+       if (t->attioid > 0)
+               btd_device_remove_attio_callback(t->dev, t->attioid);
+
+       if (t->attrib != NULL) {
+               g_attrib_unregister(t->attrib, t->attio_measurement_id);
+               g_attrib_unregister(t->attrib, t->attio_intermediate_id);
+               g_attrib_unregister(t->attrib, t->attio_interval_id);
+               g_attrib_unref(t->attrib);
+       }
+
+       btd_device_unref(t->dev);
+       g_free(t->svc_range);
+       g_free(t);
+}
+
+static void destroy_thermometer_adapter(gpointer user_data)
+{
+       struct thermometer_adapter *tadapter = user_data;
+
+       if (tadapter->devices != NULL)
+               g_slist_free_full(tadapter->devices, destroy_thermometer);
+
+       if (tadapter->fwatchers != NULL)
+               g_slist_free_full(tadapter->fwatchers, remove_watcher);
+
+       g_free(tadapter);
+}
+
+static int cmp_adapter(gconstpointer a, gconstpointer b)
+{
+       const struct thermometer_adapter *tadapter = a;
+       const struct btd_adapter *adapter = b;
+
+       if (adapter == tadapter->adapter)
+               return 0;
+
+       return -1;
+}
+
+static int cmp_device(gconstpointer a, gconstpointer b)
+{
+       const struct thermometer *t = a;
+       const struct btd_device *dev = b;
+
+       if (dev == t->dev)
+               return 0;
+
+       return -1;
+}
+
+static int cmp_watcher(gconstpointer a, gconstpointer b)
+{
+       const struct watcher *watcher = a;
+       const struct watcher *match = b;
+       int ret;
+
+       ret = g_strcmp0(watcher->srv, match->srv);
+       if (ret != 0)
+               return ret;
+
+       return g_strcmp0(watcher->path, match->path);
+}
+
+static struct thermometer_adapter *
+find_thermometer_adapter(struct btd_adapter *adapter)
+{
+       GSList *l = g_slist_find_custom(thermometer_adapters, adapter,
+                                                               cmp_adapter);
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+static void change_property(struct thermometer *t, const char *name,
+                                                       gpointer value) {
+       if (g_strcmp0(name, "Intermediate") == 0) {
+               gboolean *intermediate = value;
+               if (t->intermediate == *intermediate)
+                       return;
+
+               t->intermediate = *intermediate;
+       } else if (g_strcmp0(name, "Interval") == 0) {
+               uint16_t *interval = value;
+               if (t->has_interval && t->interval == *interval)
+                       return;
+
+               t->has_interval = TRUE;
+               t->interval = *interval;
+       } else if (g_strcmp0(name, "Maximum") == 0) {
+               uint16_t *max = value;
+               if (t->max == *max)
+                       return;
+
+               t->max = *max;
+       } else if (g_strcmp0(name, "Minimum") == 0) {
+               uint16_t *min = value;
+               if (t->min == *min)
+                       return;
+
+               t->min = *min;
+       } else {
+               DBG("%s is not a thermometer property", name);
+               return;
+       }
+
+       g_dbus_emit_property_changed(btd_get_dbus_connection(),
+                                               device_get_path(t->dev),
+                                               THERMOMETER_INTERFACE, name);
+}
+
+static void update_watcher(gpointer data, gpointer user_data)
+{
+       struct watcher *w = data;
+       struct measurement *m = user_data;
+       const char *path = device_get_path(m->t->dev);
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(w->srv, w->path,
+                               THERMOMETER_WATCHER_INTERFACE,
+                               "MeasurementReceived");
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       dict_append_entry(&dict, "Exponent", DBUS_TYPE_INT16, &m->exp);
+       dict_append_entry(&dict, "Mantissa", DBUS_TYPE_INT32, &m->mant);
+       dict_append_entry(&dict, "Unit", DBUS_TYPE_STRING, &m->unit);
+
+       if (m->suptime)
+               dict_append_entry(&dict, "Time", DBUS_TYPE_UINT64, &m->time);
+
+       dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &m->type);
+       dict_append_entry(&dict, "Measurement", DBUS_TYPE_STRING, &m->value);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       dbus_message_set_no_reply(msg, TRUE);
+       g_dbus_send_message(btd_get_dbus_connection(), msg);
+}
+
+static void recv_measurement(struct thermometer *t, struct measurement *m)
+{
+       GSList *wlist;
+
+       m->t = t;
+
+       if (g_strcmp0(m->value, "intermediate") == 0)
+               wlist = t->tadapter->iwatchers;
+       else
+               wlist = t->tadapter->fwatchers;
+
+       g_slist_foreach(wlist, update_watcher, m);
+}
+
+static void proc_measurement(struct thermometer *t, const uint8_t *pdu,
+                                               uint16_t len, gboolean final)
+{
+       struct measurement m;
+       const char *type = NULL;
+       uint8_t flags;
+       uint32_t raw;
+
+       /* skip opcode and handle */
+       pdu += 3;
+       len -= 3;
+
+       if (len < 1) {
+               DBG("Mandatory flags are not provided");
+               return;
+       }
+
+       memset(&m, 0, sizeof(m));
+
+       flags = *pdu;
+
+       if (flags & TEMP_UNITS)
+               m.unit = "fahrenheit";
+       else
+               m.unit = "celsius";
+
+       pdu++;
+       len--;
+
+       if (len < 4) {
+               DBG("Mandatory temperature measurement value is not provided");
+               return;
+       }
+
+       raw = get_le32(pdu);
+       m.mant = raw & 0x00FFFFFF;
+       m.exp = ((int32_t) raw) >> 24;
+
+       if (m.mant & 0x00800000) {
+               /* convert to C2 negative value */
+               m.mant = m.mant - FLOAT_MAX_MANTISSA;
+       }
+
+       pdu += 4;
+       len -= 4;
+
+       if (flags & TEMP_TIME_STAMP) {
+               struct tm ts;
+               time_t time;
+
+               if (len < 7) {
+                       DBG("Time stamp is not provided");
+                       return;
+               }
+
+               ts.tm_year = get_le16(pdu) - 1900;
+               ts.tm_mon = *(pdu + 2) - 1;
+               ts.tm_mday = *(pdu + 3);
+               ts.tm_hour = *(pdu + 4);
+               ts.tm_min = *(pdu + 5);
+               ts.tm_sec = *(pdu + 6);
+               ts.tm_isdst = -1;
+
+               time = mktime(&ts);
+               m.time = (uint64_t) time;
+               m.suptime = TRUE;
+
+               pdu += 7;
+               len -= 7;
+       }
+
+       if (flags & TEMP_TYPE) {
+               if (len < 1) {
+                       DBG("Temperature type is not provided");
+                       return;
+               }
+
+               type = temptype2str(*pdu);
+       } else if (t->has_type) {
+               type = temptype2str(t->type);
+       }
+
+       m.type = g_strdup(type);
+       m.value = final ? "final" : "intermediate";
+
+       recv_measurement(t, &m);
+       g_free(m.type);
+}
+
+
+static void measurement_ind_handler(const uint8_t *pdu, uint16_t len,
+                                                       gpointer user_data)
+{
+       struct thermometer *t = user_data;
+       uint8_t *opdu;
+       uint16_t olen;
+       size_t plen;
+
+       if (len < 3) {
+               DBG("Bad pdu received");
+               return;
+       }
+
+       proc_measurement(t, pdu, len, TRUE);
+
+       opdu = g_attrib_get_buffer(t->attrib, &plen);
+       olen = enc_confirmation(opdu, plen);
+
+       if (olen > 0)
+               g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL);
+}
+
+static void intermediate_notify_handler(const uint8_t *pdu, uint16_t len,
+                                                       gpointer user_data)
+{
+       struct thermometer *t = user_data;
+
+       if (len < 3) {
+               DBG("Bad pdu received");
+               return;
+       }
+
+       proc_measurement(t, pdu, len, FALSE);
+}
+
+static void interval_ind_handler(const uint8_t *pdu, uint16_t len,
+                                                       gpointer user_data)
+{
+       struct thermometer *t = user_data;
+       uint16_t interval;
+       uint8_t *opdu;
+       uint16_t olen;
+       size_t plen;
+
+       if (len < 5) {
+               DBG("Bad pdu received");
+               return;
+       }
+
+       interval = get_le16(pdu + 3);
+       change_property(t, "Interval", &interval);
+
+       opdu = g_attrib_get_buffer(t->attrib, &plen);
+       olen = enc_confirmation(opdu, plen);
+
+       if (olen > 0)
+               g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL);
+}
+
+static void valid_range_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct thermometer *t = user_data;
+       uint8_t value[VALID_RANGE_DESC_SIZE];
+       uint16_t max, min;
+       ssize_t vlen;
+
+       if (status != 0) {
+               DBG("Valid Range descriptor read failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       vlen = dec_read_resp(pdu, len, value, sizeof(value));
+       if (vlen < 0) {
+               DBG("Protocol error\n");
+               return;
+       }
+
+       if (vlen < 4) {
+               DBG("Invalid range received");
+               return;
+       }
+
+       min = get_le16(&value[0]);
+       max = get_le16(&value[2]);
+
+       if (min == 0 || min > max) {
+               DBG("Invalid range");
+               return;
+       }
+
+       change_property(t, "Maximum", &max);
+       change_property(t, "Minimum", &min);
+}
+
+static void write_ccc_cb(guint8 status, const guint8 *pdu,
+                                               guint16 len, gpointer user_data)
+{
+       char *msg = user_data;
+
+       if (status != 0)
+               error("%s failed", msg);
+
+       g_free(msg);
+}
+
+static void process_thermometer_desc(struct characteristic *ch, uint16_t uuid,
+                                                               uint16_t handle)
+{
+       uint8_t atval[2];
+       uint16_t val;
+       char *msg;
+
+       if (uuid == GATT_CHARAC_VALID_RANGE_UUID) {
+               if (g_strcmp0(ch->uuid, MEASUREMENT_INTERVAL_UUID) == 0)
+                       gatt_read_char(ch->t->attrib, handle,
+                                               valid_range_desc_cb, ch->t);
+               return;
+       }
+
+       if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
+               return;
+
+       if (g_strcmp0(ch->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) {
+               ch->t->measurement_ccc_handle = handle;
+
+               if (g_slist_length(ch->t->tadapter->fwatchers) == 0) {
+                       val = 0x0000;
+                       msg = g_strdup("Disable Temperature Measurement ind");
+               } else {
+                       val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
+                       msg = g_strdup("Enable Temperature Measurement ind");
+               }
+       } else if (g_strcmp0(ch->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
+               ch->t->intermediate_ccc_handle = handle;
+
+               if (g_slist_length(ch->t->tadapter->iwatchers) == 0) {
+                       val = 0x0000;
+                       msg = g_strdup("Disable Intermediate Temperature noti");
+               } else {
+                       val = GATT_CLIENT_CHARAC_CFG_NOTIF_BIT;
+                       msg = g_strdup("Enable Intermediate Temperature noti");
+               }
+       } else if (g_strcmp0(ch->uuid, MEASUREMENT_INTERVAL_UUID) == 0) {
+               val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
+               msg = g_strdup("Enable Measurement Interval indication");
+       } else {
+               return;
+       }
+
+       put_le16(val, atval);
+       gatt_write_char(ch->t->attrib, handle, atval, sizeof(atval),
+                                                       write_ccc_cb, msg);
+}
+
+static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data)
+{
+       struct characteristic *ch = user_data;
+
+       if (status != 0) {
+               error("Discover all characteristic descriptors failed [%s]: %s",
+                                       ch->uuid, att_ecode2str(status));
+               goto done;
+       }
+
+       for ( ; descs; descs = descs->next) {
+               struct gatt_desc *desc = descs->data;
+
+               process_thermometer_desc(ch, desc->uuid16, desc->handle);
+       }
+
+done:
+       g_free(ch);
+}
+
+static void discover_desc(struct thermometer *t, struct gatt_char *c,
+                                               struct gatt_char *c_next)
+{
+       struct characteristic *ch;
+       uint16_t start, end;
+
+       start = c->value_handle + 1;
+
+       if (c_next != NULL) {
+               if (start == c_next->handle)
+                       return;
+               end = c_next->handle - 1;
+       } else if (c->value_handle != t->svc_range->end) {
+               end = t->svc_range->end;
+       } else {
+               return;
+       }
+
+       ch = g_new0(struct characteristic, 1);
+       ch->t = t;
+       memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
+
+       gatt_discover_desc(t->attrib, start, end, NULL, discover_desc_cb, ch);
+}
+
+static void read_temp_type_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct thermometer *t = user_data;
+       uint8_t value[TEMPERATURE_TYPE_SIZE];
+       ssize_t vlen;
+
+       if (status != 0) {
+               DBG("Temperature Type value read failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       vlen = dec_read_resp(pdu, len, value, sizeof(value));
+       if (vlen < 0) {
+               DBG("Protocol error.");
+               return;
+       }
+
+       if (vlen != 1) {
+               DBG("Invalid length for Temperature type");
+               return;
+       }
+
+       t->has_type = TRUE;
+       t->type = value[0];
+}
+
+static void read_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct thermometer *t = user_data;
+       uint8_t value[MEASUREMENT_INTERVAL_SIZE];
+       uint16_t interval;
+       ssize_t vlen;
+
+       if (status != 0) {
+               DBG("Measurement Interval value read failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       vlen = dec_read_resp(pdu, len, value, sizeof(value));
+       if (vlen < 0) {
+               DBG("Protocol error\n");
+               return;
+       }
+
+       if (vlen < 2) {
+               DBG("Invalid Interval received");
+               return;
+       }
+
+       interval = get_le16(&value[0]);
+       change_property(t, "Interval", &interval);
+}
+
+static void process_thermometer_char(struct thermometer *t,
+                               struct gatt_char *c, struct gatt_char *c_next)
+{
+       if (g_strcmp0(c->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
+               gboolean intermediate = TRUE;
+               change_property(t, "Intermediate", &intermediate);
+
+               t->attio_intermediate_id = g_attrib_register(t->attrib,
+                                       ATT_OP_HANDLE_NOTIFY, c->value_handle,
+                                       intermediate_notify_handler, t, NULL);
+
+               discover_desc(t, c, c_next);
+       } else if (g_strcmp0(c->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) {
+
+               t->attio_measurement_id = g_attrib_register(t->attrib,
+                                       ATT_OP_HANDLE_IND, c->value_handle,
+                                       measurement_ind_handler, t, NULL);
+
+               discover_desc(t, c, c_next);
+       } else if (g_strcmp0(c->uuid, TEMPERATURE_TYPE_UUID) == 0) {
+               gatt_read_char(t->attrib, c->value_handle,
+                                                       read_temp_type_cb, t);
+       } else if (g_strcmp0(c->uuid, MEASUREMENT_INTERVAL_UUID) == 0) {
+               bool need_desc = false;
+
+               gatt_read_char(t->attrib, c->value_handle, read_interval_cb, t);
+
+               if (c->properties & GATT_CHR_PROP_WRITE) {
+                       t->interval_val_handle = c->value_handle;
+                       need_desc = true;
+               }
+
+               if (c->properties & GATT_CHR_PROP_INDICATE) {
+                       t->attio_interval_id = g_attrib_register(t->attrib,
+                                       ATT_OP_HANDLE_IND, c->value_handle,
+                                       interval_ind_handler, t, NULL);
+                       need_desc = true;
+               }
+
+               if (need_desc)
+                       discover_desc(t, c, c_next);
+       }
+}
+
+static void configure_thermometer_cb(uint8_t status, GSList *characteristics,
+                                                               void *user_data)
+{
+       struct thermometer *t = user_data;
+       GSList *l;
+
+       if (status != 0) {
+               error("Discover thermometer characteristics: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       for (l = characteristics; l; l = l->next) {
+               struct gatt_char *c = l->data;
+               struct gatt_char *c_next = (l->next ? l->next->data : NULL);
+
+               process_thermometer_char(t, c, c_next);
+       }
+}
+
+static void write_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct tmp_interval_data *data = user_data;
+
+       if (status != 0) {
+               error("Interval Write Request failed %s",
+                                                       att_ecode2str(status));
+               goto done;
+       }
+
+       if (!dec_write_resp(pdu, len)) {
+               error("Interval Write Request: protocol error");
+               goto done;
+       }
+
+       change_property(data->thermometer, "Interval", &data->interval);
+
+done:
+       g_free(user_data);
+}
+
+static void enable_final_measurement(gpointer data, gpointer user_data)
+{
+       struct thermometer *t = data;
+       uint16_t handle = t->measurement_ccc_handle;
+       uint8_t value[2];
+       char *msg;
+
+       if (t->attrib == NULL || !handle)
+               return;
+
+       put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value);
+       msg = g_strdup("Enable Temperature Measurement indications");
+
+       gatt_write_char(t->attrib, handle, value, sizeof(value),
+                                                       write_ccc_cb, msg);
+}
+
+static void enable_intermediate_measurement(gpointer data, gpointer user_data)
+{
+       struct thermometer *t = data;
+       uint16_t handle = t->intermediate_ccc_handle;
+       uint8_t value[2];
+       char *msg;
+
+       if (t->attrib == NULL || !handle)
+               return;
+
+       put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+       msg = g_strdup("Enable Intermediate Temperature notifications");
+
+       gatt_write_char(t->attrib, handle, value, sizeof(value),
+                                                       write_ccc_cb, msg);
+}
+
+static void disable_final_measurement(gpointer data, gpointer user_data)
+{
+       struct thermometer *t = data;
+       uint16_t handle = t->measurement_ccc_handle;
+       uint8_t value[2];
+       char *msg;
+
+       if (t->attrib == NULL || !handle)
+               return;
+
+       put_le16(0x0000, value);
+       msg = g_strdup("Disable Temperature Measurement indications");
+
+       gatt_write_char(t->attrib, handle, value, sizeof(value),
+                                                       write_ccc_cb, msg);
+}
+
+static void disable_intermediate_measurement(gpointer data, gpointer user_data)
+{
+       struct thermometer *t = data;
+       uint16_t handle = t->intermediate_ccc_handle;
+       uint8_t value[2];
+       char *msg;
+
+       if (t->attrib == NULL || !handle)
+               return;
+
+       put_le16(0x0000, value);
+       msg = g_strdup("Disable Intermediate Temperature notifications");
+
+       gatt_write_char(t->attrib, handle, value, sizeof(value),
+                                                       write_ccc_cb, msg);
+}
+
+static void remove_int_watcher(struct thermometer_adapter *tadapter,
+                                                       struct watcher *w)
+{
+       if (!g_slist_find(tadapter->iwatchers, w))
+               return;
+
+       tadapter->iwatchers = g_slist_remove(tadapter->iwatchers, w);
+
+       if (g_slist_length(tadapter->iwatchers) == 0)
+               g_slist_foreach(tadapter->devices,
+                                       disable_intermediate_measurement, 0);
+}
+
+static void watcher_exit(DBusConnection *conn, void *user_data)
+{
+       struct watcher *watcher = user_data;
+       struct thermometer_adapter *tadapter = watcher->tadapter;
+
+       DBG("Thermometer watcher %s disconnected", watcher->path);
+
+       remove_int_watcher(tadapter, watcher);
+
+       tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher);
+       g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+
+       if (g_slist_length(tadapter->fwatchers) == 0)
+               g_slist_foreach(tadapter->devices,
+                                       disable_final_measurement, 0);
+}
+
+static struct watcher *find_watcher(GSList *list, const char *sender,
+                                                       const char *path)
+{
+       struct watcher *match;
+       GSList *l;
+
+       match = g_new0(struct watcher, 1);
+       match->srv = g_strdup(sender);
+       match->path = g_strdup(path);
+
+       l = g_slist_find_custom(list, match, cmp_watcher);
+       destroy_watcher(match);
+
+       if (l != NULL)
+               return l->data;
+
+       return NULL;
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       const char *sender = dbus_message_get_sender(msg);
+       struct thermometer_adapter *tadapter = data;
+       struct watcher *watcher;
+       char *path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       watcher = find_watcher(tadapter->fwatchers, sender, path);
+       if (watcher != NULL)
+               return btd_error_already_exists(msg);
+
+       DBG("Thermometer watcher %s registered", path);
+
+       watcher = g_new0(struct watcher, 1);
+       watcher->srv = g_strdup(sender);
+       watcher->path = g_strdup(path);
+       watcher->tadapter = tadapter;
+       watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
+                                               watcher, destroy_watcher);
+
+       if (g_slist_length(tadapter->fwatchers) == 0)
+               g_slist_foreach(tadapter->devices, enable_final_measurement, 0);
+
+       tadapter->fwatchers = g_slist_prepend(tadapter->fwatchers, watcher);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       const char *sender = dbus_message_get_sender(msg);
+       struct thermometer_adapter *tadapter = data;
+       struct watcher *watcher;
+       char *path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       watcher = find_watcher(tadapter->fwatchers, sender, path);
+       if (watcher == NULL)
+               return btd_error_does_not_exist(msg);
+
+       DBG("Thermometer watcher %s unregistered", path);
+
+       remove_int_watcher(tadapter, watcher);
+
+       tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher);
+       g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+
+       if (g_slist_length(tadapter->fwatchers) == 0)
+               g_slist_foreach(tadapter->devices,
+                                       disable_final_measurement, 0);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       const char *sender = dbus_message_get_sender(msg);
+       struct thermometer_adapter *ta = data;
+       struct watcher *watcher;
+       char *path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       watcher = find_watcher(ta->fwatchers, sender, path);
+       if (watcher == NULL)
+               return btd_error_does_not_exist(msg);
+
+       if (find_watcher(ta->iwatchers, sender, path))
+               return btd_error_already_exists(msg);
+
+       DBG("Intermediate measurement watcher %s registered", path);
+
+       if (g_slist_length(ta->iwatchers) == 0)
+               g_slist_foreach(ta->devices,
+                                       enable_intermediate_measurement, 0);
+
+       ta->iwatchers = g_slist_prepend(ta->iwatchers, watcher);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *disable_intermediate(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       const char *sender = dbus_message_get_sender(msg);
+       struct thermometer_adapter *ta = data;
+       struct watcher *watcher;
+       char *path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       watcher = find_watcher(ta->iwatchers, sender, path);
+       if (watcher == NULL)
+               return btd_error_does_not_exist(msg);
+
+       DBG("Intermediate measurement %s unregistered", path);
+
+       remove_int_watcher(ta, watcher);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static gboolean property_get_intermediate(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct thermometer *t = data;
+       dbus_bool_t val;
+
+       val = !!t->intermediate;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+       return TRUE;
+}
+
+static gboolean property_get_interval(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct thermometer *t = data;
+
+       if (!t->has_interval)
+               return FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->interval);
+
+       return TRUE;
+}
+
+static void property_set_interval(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter,
+                                       GDBusPendingPropertySet id, void *data)
+{
+       struct thermometer *t = data;
+       struct tmp_interval_data *interval_data;
+       uint16_t val;
+       uint8_t atval[2];
+
+       if (t->interval_val_handle == 0) {
+               g_dbus_pending_property_error(id,
+                                       ERROR_INTERFACE ".NotSupported",
+                                       "Operation is not supported");
+               return;
+       }
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) {
+               g_dbus_pending_property_error(id,
+                                       ERROR_INTERFACE ".InvalidArguments",
+                                       "Invalid arguments in method call");
+               return;
+       }
+
+       dbus_message_iter_get_basic(iter, &val);
+
+       if (val < t->min || val > t->max) {
+               g_dbus_pending_property_error(id,
+                                       ERROR_INTERFACE ".InvalidArguments",
+                                       "Invalid arguments in method call");
+               return;
+       }
+
+       put_le16(val, &atval[0]);
+
+       interval_data = g_new0(struct tmp_interval_data, 1);
+       interval_data->thermometer = t;
+       interval_data->interval = val;
+       gatt_write_char(t->attrib, t->interval_val_handle, atval, sizeof(atval),
+                                       write_interval_cb, interval_data);
+
+       g_dbus_pending_property_success(id);
+}
+
+static gboolean property_exists_interval(const GDBusPropertyTable *property,
+                                                               void *data)
+{
+       struct thermometer *t = data;
+
+       return t->has_interval;
+}
+
+static gboolean property_get_maximum(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct thermometer *t = data;
+
+       if (!t->has_interval)
+               return FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->max);
+
+       return TRUE;
+}
+
+static gboolean property_get_minimum(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct thermometer *t = data;
+
+       if (!t->has_interval)
+               return FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->min);
+
+       return TRUE;
+}
+
+static const GDBusPropertyTable thermometer_properties[] = {
+       { "Intermediate", "b", property_get_intermediate },
+       { "Interval", "q", property_get_interval, property_set_interval,
+                                               property_exists_interval },
+       { "Maximum", "q", property_get_maximum, NULL,
+                                               property_exists_interval },
+       { "Minimum", "q", property_get_minimum, NULL,
+                                               property_exists_interval },
+       { }
+};
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+       struct thermometer *t = user_data;
+
+       t->attrib = g_attrib_ref(attrib);
+
+       gatt_discover_char(t->attrib, t->svc_range->start, t->svc_range->end,
+                                       NULL, configure_thermometer_cb, t);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+       struct thermometer *t = user_data;
+
+       DBG("GATT Disconnected");
+
+       if (t->attio_measurement_id > 0) {
+               g_attrib_unregister(t->attrib, t->attio_measurement_id);
+               t->attio_measurement_id = 0;
+       }
+
+       if (t->attio_intermediate_id > 0) {
+               g_attrib_unregister(t->attrib, t->attio_intermediate_id);
+               t->attio_intermediate_id = 0;
+       }
+
+       if (t->attio_interval_id > 0) {
+               g_attrib_unregister(t->attrib, t->attio_interval_id);
+               t->attio_interval_id = 0;
+       }
+
+       g_attrib_unref(t->attrib);
+       t->attrib = NULL;
+}
+
+static int thermometer_register(struct btd_device *device,
+                                               struct gatt_primary *tattr)
+{
+       const char *path = device_get_path(device);
+       struct thermometer *t;
+       struct btd_adapter *adapter;
+       struct thermometer_adapter *tadapter;
+
+       adapter = device_get_adapter(device);
+
+       tadapter = find_thermometer_adapter(adapter);
+
+       if (tadapter == NULL)
+               return -1;
+
+       t = g_new0(struct thermometer, 1);
+       t->dev = btd_device_ref(device);
+       t->tadapter = tadapter;
+       t->svc_range = g_new0(struct att_range, 1);
+       t->svc_range->start = tattr->range.start;
+       t->svc_range->end = tattr->range.end;
+
+       tadapter->devices = g_slist_prepend(tadapter->devices, t);
+
+       if (!g_dbus_register_interface(btd_get_dbus_connection(),
+                               path, THERMOMETER_INTERFACE,
+                               NULL, NULL, thermometer_properties,
+                               t, destroy_thermometer)) {
+               error("D-Bus failed to register %s interface",
+                                                       THERMOMETER_INTERFACE);
+               destroy_thermometer(t);
+               return -EIO;
+       }
+
+       t->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+                                               attio_disconnected_cb, t);
+       return 0;
+}
+
+static void thermometer_unregister(struct btd_device *device)
+{
+       struct thermometer *t;
+       struct btd_adapter *adapter;
+       struct thermometer_adapter *tadapter;
+       GSList *l;
+
+       adapter = device_get_adapter(device);
+
+       tadapter = find_thermometer_adapter(adapter);
+
+       if (tadapter == NULL)
+               return;
+
+       l = g_slist_find_custom(tadapter->devices, device, cmp_device);
+       if (l == NULL)
+               return;
+
+       t = l->data;
+
+       tadapter->devices = g_slist_remove(tadapter->devices, t);
+
+       g_dbus_unregister_interface(btd_get_dbus_connection(),
+                               device_get_path(t->dev), THERMOMETER_INTERFACE);
+}
+
+static const GDBusMethodTable thermometer_manager_methods[] = {
+       { GDBUS_METHOD("RegisterWatcher",
+                       GDBUS_ARGS({ "agent", "o" }), NULL,
+                       register_watcher) },
+       { GDBUS_METHOD("UnregisterWatcher",
+                       GDBUS_ARGS({ "agent", "o" }), NULL,
+                       unregister_watcher) },
+       { GDBUS_METHOD("EnableIntermediateMeasurement",
+                       GDBUS_ARGS({ "agent", "o" }), NULL,
+                       enable_intermediate) },
+       { GDBUS_METHOD("DisableIntermediateMeasurement",
+                       GDBUS_ARGS({ "agent", "o" }), NULL,
+                       disable_intermediate) },
+       { }
+};
+
+static int thermometer_adapter_register(struct btd_adapter *adapter)
+{
+       struct thermometer_adapter *tadapter;
+
+       tadapter = g_new0(struct thermometer_adapter, 1);
+       tadapter->adapter = adapter;
+
+       if (!g_dbus_register_interface(btd_get_dbus_connection(),
+                                               adapter_get_path(adapter),
+                                               THERMOMETER_MANAGER_INTERFACE,
+                                               thermometer_manager_methods,
+                                               NULL, NULL, tadapter,
+                                               destroy_thermometer_adapter)) {
+               error("D-Bus failed to register %s interface",
+                                               THERMOMETER_MANAGER_INTERFACE);
+               destroy_thermometer_adapter(tadapter);
+               return -EIO;
+       }
+
+       thermometer_adapters = g_slist_prepend(thermometer_adapters, tadapter);
+
+       return 0;
+}
+
+static void thermometer_adapter_unregister(struct btd_adapter *adapter)
+{
+       struct thermometer_adapter *tadapter;
+
+       tadapter = find_thermometer_adapter(adapter);
+       if (tadapter == NULL)
+               return;
+
+       thermometer_adapters = g_slist_remove(thermometer_adapters, tadapter);
+
+       g_dbus_unregister_interface(btd_get_dbus_connection(),
+                                       adapter_get_path(tadapter->adapter),
+                                       THERMOMETER_MANAGER_INTERFACE);
+}
+
+static int thermometer_device_probe(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct gatt_primary *tattr;
+
+       tattr = btd_device_get_primary(device, HEALTH_THERMOMETER_UUID);
+       if (tattr == NULL)
+               return -EINVAL;
+
+       return thermometer_register(device, tattr);
+}
+
+static void thermometer_device_remove(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+
+       thermometer_unregister(device);
+}
+
+static int thermometer_adapter_probe(struct btd_profile *p,
+                                               struct btd_adapter *adapter)
+{
+       return thermometer_adapter_register(adapter);
+}
+
+static void thermometer_adapter_remove(struct btd_profile *p,
+                                               struct btd_adapter *adapter)
+{
+       thermometer_adapter_unregister(adapter);
+}
+
+static struct btd_profile thermometer_profile = {
+       .name           = "Health Thermometer GATT driver",
+       .remote_uuid    = HEALTH_THERMOMETER_UUID,
+       .device_probe   = thermometer_device_probe,
+       .device_remove  = thermometer_device_remove,
+       .adapter_probe  = thermometer_adapter_probe,
+       .adapter_remove = thermometer_adapter_remove
+};
+
+static int thermometer_init(void)
+{
+       return btd_profile_register(&thermometer_profile);
+}
+
+static void thermometer_exit(void)
+{
+       btd_profile_unregister(&thermometer_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(thermometer, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                                       thermometer_init, thermometer_exit)
diff --git a/profiles/time/server.c b/profiles/time/server.c
new file mode 100755 (executable)
index 0000000..2289c6a
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <time.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/plugin.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "src/shared/util.h"
+#include "src/attrib-server.h"
+#include "attrib/gatt-service.h"
+#include "src/log.h"
+
+#define CURRENT_TIME_SVC_UUID          0x1805
+#define REF_TIME_UPDATE_SVC_UUID       0x1806
+
+#define LOCAL_TIME_INFO_CHR_UUID       0x2A0F
+#define TIME_UPDATE_CTRL_CHR_UUID      0x2A16
+#define TIME_UPDATE_STAT_CHR_UUID      0x2A17
+#define CT_TIME_CHR_UUID               0x2A2B
+
+enum {
+       UPDATE_RESULT_SUCCESSFUL = 0,
+       UPDATE_RESULT_CANCELED = 1,
+       UPDATE_RESULT_NO_CONN = 2,
+       UPDATE_RESULT_ERROR = 3,
+       UPDATE_RESULT_TIMEOUT = 4,
+       UPDATE_RESULT_NOT_ATTEMPTED = 5,
+};
+
+enum {
+       UPDATE_STATE_IDLE = 0,
+       UPDATE_STATE_PENDING = 1,
+};
+
+enum {
+       GET_REFERENCE_UPDATE = 1,
+       CANCEL_REFERENCE_UPDATE = 2,
+};
+
+static int encode_current_time(uint8_t value[10])
+{
+       struct timespec tp;
+       struct tm tm;
+
+       if (clock_gettime(CLOCK_REALTIME, &tp) == -1) {
+               int err = -errno;
+
+               error("clock_gettime: %s", strerror(-err));
+               return err;
+       }
+
+       if (localtime_r(&tp.tv_sec, &tm) == NULL) {
+               error("localtime_r() failed");
+               /* localtime_r() does not set errno */
+               return -EINVAL;
+       }
+
+       put_le16(1900 + tm.tm_year, &value[0]); /* Year */
+       value[2] = tm.tm_mon + 1; /* Month */
+       value[3] = tm.tm_mday; /* Day */
+       value[4] = tm.tm_hour; /* Hours */
+       value[5] = tm.tm_min; /* Minutes */
+       value[6] = tm.tm_sec; /* Seconds */
+       value[7] = tm.tm_wday == 0 ? 7 : tm.tm_wday; /* Day of Week */
+       /* From Time Profile spec: "The number of 1/256 fractions of a second."
+        * In 1s there are 256 fractions, in 1ns there are 256/10^9 fractions.
+        * To avoid integer overflow, we use the equivalent 1/3906250 ratio. */
+       value[8] = tp.tv_nsec / 3906250; /* Fractions256 */
+       value[9] = 0x00; /* Adjust Reason */
+
+       return 0;
+}
+
+static uint8_t current_time_read(struct attribute *a,
+                                struct btd_device *device, gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       uint8_t value[10];
+
+       if (encode_current_time(value) < 0)
+               return ATT_ECODE_IO;
+
+       attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);
+
+       return 0;
+}
+
+static uint8_t local_time_info_read(struct attribute *a,
+                               struct btd_device *device, gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       uint8_t value[2];
+
+       DBG("a=%p", a);
+
+       tzset();
+
+       /* Convert POSIX "timezone" (seconds West of GMT) to Time Profile
+        * format (offset from UTC in number of 15 minutes increments). */
+       value[0] = (uint8_t) (-1 * timezone / (60 * 15));
+
+       /* FIXME: POSIX "daylight" variable only indicates whether there
+        * is DST for the local time or not. The offset is unknown. */
+       value[1] = daylight ? 0xff : 0x00;
+
+       attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);
+
+       return 0;
+}
+
+static gboolean register_current_time_service(struct btd_adapter *adapter)
+{
+       bt_uuid_t uuid;
+
+       bt_uuid16_create(&uuid, CURRENT_TIME_SVC_UUID);
+
+       /* Current Time service */
+       return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
+                               /* CT Time characteristic */
+                               GATT_OPT_CHR_UUID16, CT_TIME_CHR_UUID,
+                               GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
+                                                       GATT_CHR_PROP_NOTIFY,
+                               GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+                                               current_time_read, adapter,
+
+                               /* Local Time Information characteristic */
+                               GATT_OPT_CHR_UUID16, LOCAL_TIME_INFO_CHR_UUID,
+                               GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
+                               GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+                                               local_time_info_read, adapter,
+
+                               GATT_OPT_INVALID);
+}
+
+static uint8_t time_update_control(struct attribute *a,
+                                               struct btd_device *device,
+                                               gpointer user_data)
+{
+       DBG("handle 0x%04x", a->handle);
+
+       if (a->len != 1)
+               DBG("Invalid control point value size: %zu", a->len);
+
+       switch (a->data[0]) {
+       case GET_REFERENCE_UPDATE:
+               DBG("Get Reference Update");
+               break;
+       case CANCEL_REFERENCE_UPDATE:
+               DBG("Cancel Reference Update");
+               break;
+       default:
+               DBG("Unknown command: 0x%02x", a->data[0]);
+       }
+
+       return 0;
+}
+
+static uint8_t time_update_status(struct attribute *a,
+                                               struct btd_device *device,
+                                               gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       uint8_t value[2];
+
+       DBG("handle 0x%04x", a->handle);
+
+       value[0] = UPDATE_STATE_IDLE;
+       value[1] = UPDATE_RESULT_SUCCESSFUL;
+       attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);
+
+       return 0;
+}
+
+static gboolean register_ref_time_update_service(struct btd_adapter *adapter)
+{
+       bt_uuid_t uuid;
+
+       bt_uuid16_create(&uuid, REF_TIME_UPDATE_SVC_UUID);
+
+       /* Reference Time Update service */
+       return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
+                               /* Time Update control point */
+                               GATT_OPT_CHR_UUID16, TIME_UPDATE_CTRL_CHR_UUID,
+                               GATT_OPT_CHR_PROPS,
+                                       GATT_CHR_PROP_WRITE_WITHOUT_RESP,
+                               GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+                                               time_update_control, adapter,
+
+                               /* Time Update status */
+                               GATT_OPT_CHR_UUID16, TIME_UPDATE_STAT_CHR_UUID,
+                               GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
+                               GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+                                               time_update_status, adapter,
+
+                               GATT_OPT_INVALID);
+}
+
+static int time_server_init(struct btd_profile *p, struct btd_adapter *adapter)
+{
+       const char *path = adapter_get_path(adapter);
+
+       DBG("path %s", path);
+
+       if (!register_current_time_service(adapter)) {
+               error("Current Time Service could not be registered");
+               return -EIO;
+       }
+
+       if (!register_ref_time_update_service(adapter)) {
+               error("Reference Time Update Service could not be registered");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void time_server_exit(struct btd_profile *p,
+                                               struct btd_adapter *adapter)
+{
+       const char *path = adapter_get_path(adapter);
+
+       DBG("path %s", path);
+}
+
+struct btd_profile time_profile = {
+       .name           = "gatt-time-server",
+       .adapter_probe  = time_server_init,
+       .adapter_remove = time_server_exit,
+};
+
+static int time_init(void)
+{
+       return btd_profile_register(&time_profile);
+}
+
+static void time_exit(void)
+{
+       btd_profile_unregister(&time_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(time, VERSION,
+                       BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                       time_init, time_exit)
index 5d7a9a9..2a3e40b 100755 (executable)
 #include "advertising.h"
 #include "eir.h"
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "adapter_le_vsc_features.h"
+#endif
+
 #define ADAPTER_INTERFACE      "org.bluez.Adapter1"
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+#define DEVICED_DEST                   "org.tizen.system.deviced"
+#define DEVICED_BATT_INTERFACE         "org.tizen.system.deviced.Battery"
+#define DEVICED_BATT_OBJECT_PATH       "/Org/Tizen/System/DeviceD/Battery"
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
+
 #define MODE_OFF               0x00
 #define MODE_CONNECTABLE       0x01
 #define MODE_DISCOVERABLE      0x02
 #define DISTANCE_VAL_INVALID   0x7FFF
 #define PATHLOSS_MAX           137
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define ADV_DATA_MAX_LENGTH 31
+#define SCAN_RESPONSE_DATA_LENGTH_MAX 31
+#define EIR_MANUFACTURER_DATA_LENGTH_MAX 100
+
+#define LE_BEARER_POSTFIX      " LE"
+#define LE_BEARER_POSTFIX_LEN  3
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
+
 static DBusConnection *dbus_conn = NULL;
 
 static bool kernel_conn_control = false;
@@ -181,6 +203,20 @@ struct btd_adapter_pin_cb_iter {
        /* When the iterator reaches the end, it is NULL and attempt is 0 */
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct adv_info {
+       int slot_id;    /* Reservied slot id is 0 (Single adv) */
+       bool status;            /* Advertising status */
+};
+
+static GSList *read_requests = NULL;
+
+struct le_data_length_read_request {
+       struct btd_adapter *adapter;
+       DBusMessage *msg;
+};
+#endif
+
 struct btd_adapter {
        int ref_count;
 
@@ -188,6 +224,9 @@ struct btd_adapter {
        struct mgmt *mgmt;
 
        bdaddr_t bdaddr;                /* controller Bluetooth address */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       bdaddr_t le_static_addr;
+#endif
        uint32_t dev_class;             /* controller class of device */
        char *name;                     /* controller device name */
        char *short_name;               /* controller short name */
@@ -205,6 +244,18 @@ struct btd_adapter {
 
        char *current_alias;            /* current adapter name alias */
        char *stored_alias;             /* stored adapter name alias */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint8_t *local_irk;     /* adapter local IRK */
+       uint8_t disc_type;
+       bool ipsp_intialized;           /* Ipsp Initialization state */
+       struct le_data_length_read_handler *read_handler;
+       struct le_data_length_read_default_data_length_handler *def_read_handler;
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+       guint charging_watch;
+       guint charging_timeout;
+       charging_state_e charging;
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
 
        bool discovering;               /* discovering property state */
        bool filtered_discovery;        /* we are doing filtered discovery */
@@ -222,6 +273,9 @@ struct btd_adapter {
 
        GSList *discovery_found;        /* list of found devices */
        guint discovery_idle_timeout;   /* timeout between discovery runs */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       guint le_discovery_idle_timeout;        /* timeout between le discovery runs */
+#endif
        guint passive_scan_timeout;     /* timeout between passive scans */
        guint temp_devices_timeout;     /* timeout for temporary devices */
 
@@ -239,6 +293,14 @@ struct btd_adapter {
        struct btd_advertising *adv_manager;
 
        gboolean initialized;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       GSList *adv_list;       /* List of advertising instance */
+       bool advertising;               /* Advertising active */
+       gchar *version;                 /* Bluetooth Version */
+       uint8_t adv_tx_power;
+       bool le_discovering;                    /* LE Discovery active */
+       GSList *le_discovery_list;              /* list of LE discovery clients */
+#endif
 
        GSList *pin_callbacks;
        GSList *msd_callbacks;
@@ -258,10 +320,32 @@ struct btd_adapter {
        guint pair_device_timeout;
 
        unsigned int db_id;             /* Service event handler for GATT db */
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint8_t central_rpa_res_support;
+       bluetooth_a2dp_role_t a2dp_role;
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       bool scan_filter_support;               /* platform's scan filtering support */
+       uint8_t scan_type;              /* scan type */
+       GSList *scan_params;    /* scan filter parameters */
+       GSList *addr_filters;   /* adress scan filters list */
+       GSList *service_data_changed_filters;   /* service data changed scan filters list */
+       GSList *service_uuid_filters;   /* service uuid scan filters list */
+       GSList *solicit_data_filters;   /* solicitation data scan filters list */
+       GSList *local_name_filters;     /* local name scan filters list */
+       GSList *manufaturer_data_filters;       /* manufacturer data scan filters list */
+       GSList *service_data_filters;   /* service data scan filters list */
+#endif
+#endif
        bool is_default;                /* true if adapter is default one */
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+enum {
+       DEINIT_6LOWPAN,
+       INIT_6LOWPAN
+};
+#endif
+
 static struct btd_adapter *btd_adapter_lookup(uint16_t index)
 {
        GList *list;
@@ -454,7 +538,13 @@ static void store_adapter_info(struct btd_adapter *adapter)
        if (adapter->stored_alias)
                g_key_file_set_string(key_file, "General", "Alias",
                                                        adapter->stored_alias);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* Store A2DP Role */
+       if (adapter->a2dp_role == BLUETOOTH_A2DP_SINK_ROLE)
+               g_key_file_set_string(key_file, "General", "DefaultA2DPRole", "sink");
+       else
+               g_key_file_set_string(key_file, "General", "DefaultA2DPRole", "source");
+#endif
        ba2str(&adapter->bdaddr, address);
        snprintf(filename, PATH_MAX, STORAGEDIR "/%s/settings", address);
 
@@ -467,12 +557,252 @@ static void store_adapter_info(struct btd_adapter *adapter)
        g_key_file_free(key_file);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+bluetooth_a2dp_role_t btd_adapter_get_a2dp_role(struct btd_adapter *adapter)
+{
+       if (!adapter)
+               return BLUETOOTH_A2DP_SOURCE_ROLE;
+
+       return adapter->a2dp_role;
+}
+
+void btd_adapter_set_a2dp_role(struct btd_adapter *adapter, bluetooth_a2dp_role_t role)
+{
+       if (!adapter) {
+               DBG("Could not set a2dp role");
+               return;
+       }
+
+       if (role == BLUETOOTH_A2DP_SOURCE_ROLE) {
+               DBG("Set audio source role");
+               adapter->a2dp_role = BLUETOOTH_A2DP_SOURCE_ROLE;
+       } else if (role == BLUETOOTH_A2DP_SINK_ROLE) {
+               DBG("Set audio sink role");
+               adapter->a2dp_role = BLUETOOTH_A2DP_SINK_ROLE;
+       }
+
+       store_adapter_info(adapter);
+}
+#endif
+
 static void trigger_pairable_timeout(struct btd_adapter *adapter);
 static void adapter_start(struct btd_adapter *adapter);
 static void adapter_stop(struct btd_adapter *adapter);
 static void trigger_passive_scanning(struct btd_adapter *adapter);
 static bool set_mode(struct btd_adapter *adapter, uint16_t opcode,
                                                        uint8_t mode);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static bool load_local_irk(struct btd_adapter *adapter);
+static bool set_local_irk(struct btd_adapter *adapter);
+static bool set_privacy(struct btd_adapter *adapter, bool privacy);
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+static gboolean charging_state_timeout_cb(gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       int bredr_pkt_type = ACL_PTYPE_MASK;
+
+       adapter->charging_timeout = 0;
+
+       DBG("Set all connections to BR/EDR type");
+       g_slist_foreach(adapter->devices, device_change_pkt_type,
+                       (gpointer)bredr_pkt_type);
+
+       return FALSE;
+}
+
+static void set_charging_state(struct btd_adapter *adapter,
+               charging_state_e state)
+{
+       int br_pkt_type = ACL_PTYPE_MASK |
+               HCI_2DH1 | HCI_2DH3 | HCI_2DH5 |
+               HCI_3DH1 | HCI_3DH3 | HCI_3DH5;
+
+       if (adapter->charging == state)
+               return;
+
+       DBG("old charging state : %d, new charging_state : %d",
+                       adapter->charging, state);
+
+       /*
+        * Only none / wire charging <-> wireless charging state change should
+        * be handled.
+        */
+       if ((adapter->charging == NONE_CHARGING && state == WIRE_CHARGING) ||
+           (adapter->charging == WIRE_CHARGING && state == NONE_CHARGING)) {
+               DBG("Just update charging state");
+               adapter->charging = state;
+               return;
+       }
+
+       if (adapter->charging_timeout) {
+               g_source_remove(adapter->charging_timeout);
+               adapter->charging_timeout = 0;
+       }
+
+       adapter->charging = state;
+       if (adapter->charging == NONE_CHARGING ||
+           adapter->charging == WIRE_CHARGING) {
+               DBG("Trigger timeout to set connection to BR/EDR type");
+               adapter->charging_timeout = g_timeout_add(2000,
+                               charging_state_timeout_cb, adapter);
+       } else if (adapter->charging == WIRELESS_CHARGING) {
+               DBG("Set all connections to BR type");
+               g_slist_foreach(adapter->devices, device_change_pkt_type,
+                               (gpointer)br_pkt_type);
+       }
+
+       return;
+}
+
+static gboolean charging_state_changed(DBusConnection *connection,
+               DBusMessage *msg, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       int state = 0;
+
+       DBG("charging_state_changed");
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &state,
+                               DBUS_TYPE_INVALID))
+               return TRUE;
+
+       set_charging_state(adapter, state);
+
+       return TRUE;
+}
+
+charging_state_e get_charging_state(struct btd_adapter *adapter)
+{
+       DBG("charging_state: %d", adapter->charging);
+       return adapter->charging;
+}
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+
+static int compare_slot(gconstpointer a, gconstpointer b)
+{
+       const struct adv_info *adv = a;
+       const int id = *(int*)b;
+
+       return (adv->slot_id == id ? 0 : -1);
+}
+
+static struct adv_info *find_advertiser(struct btd_adapter *adapter,
+                               int slot_id)
+{
+       GSList *list;
+
+       list = g_slist_find_custom(adapter->adv_list, &slot_id,
+                                                       compare_slot);
+       if (list)
+               return list->data;
+
+       return NULL;
+}
+
+static void create_advertiser(struct btd_adapter *adapter,
+                                       int slot_id)
+{
+       struct adv_info *adv;
+
+       if (!adapter)
+               return;
+
+       if (find_advertiser(adapter, slot_id) != NULL) {
+               error("Aleady existed [%d]", slot_id);
+               return;
+       }
+
+       DBG("Create adv slot id : %d", slot_id);
+
+       adv = g_new0(struct adv_info, 1);
+       if (adv == NULL)
+               return;
+
+       adv->slot_id = slot_id;
+
+       adapter->adv_list= g_slist_append(adapter->adv_list, adv);
+       return;
+}
+
+
+static void advertising_state_changed(struct btd_adapter *adapter,
+                                       int slot_id, bool enabled)
+{
+       struct adv_info *adv;
+       int id = slot_id;
+       int state = enabled;
+
+       if (!adapter)
+               return;
+
+       adv = find_advertiser(adapter, slot_id);
+       if (!adv) {
+               DBG("Unable to find advertiser [%d]", slot_id);
+               return;
+       }
+
+       adv->status = enabled;
+       DBG("slot_id %d, status %d", adv->slot_id, adv->status);
+
+       g_dbus_emit_signal(dbus_conn, adapter->path,
+                       ADAPTER_INTERFACE, "AdvertisingEnabled",
+                       DBUS_TYPE_INT32, &id,
+                       DBUS_TYPE_BOOLEAN, &state,
+                       DBUS_TYPE_INVALID);
+}
+
+static void clear_advertiser_cb(gpointer data, gpointer user_data)
+{
+       struct adv_info *adv = data;
+       struct btd_adapter *adapter = user_data;
+
+       if (adv->status)
+               advertising_state_changed(adapter, adv->slot_id, 0);
+}
+
+static void advertiser_cleanup(struct btd_adapter *adapter)
+{
+       if (!adapter->adv_list)
+               return;
+
+       g_slist_foreach(adapter->adv_list, clear_advertiser_cb, adapter);
+       g_slist_free(adapter->adv_list);
+       adapter->adv_list = NULL;
+}
+#endif
+
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined TIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN
+#define OCF_PAGE_SCAN_TIMEOUT                  0x0018
+#define OGF_PAGE_SCAN_TIMEOUT          0x03
+
+typedef struct {
+       uint16_t        timeout;        /* Value */
+} __attribute__ ((packed)) hci_page_scan_timeout;
+#define HCI_PAGE_SCAN_TIMEOUT_CP_SIZE 2
+
+static gboolean send_sprd_page_scan_timeout(gint value)
+{
+       int dd;
+       hci_page_scan_timeout cp;
+       DBG("+");
+       dd = hci_open_dev(0);
+       cp.timeout = value;
+       if (hci_send_cmd(dd, OGF_PAGE_SCAN_TIMEOUT, OCF_PAGE_SCAN_TIMEOUT,
+                               HCI_PAGE_SCAN_TIMEOUT_CP_SIZE, &cp) < 0) {
+               DBG("Error: While setting Page Timeout value");
+               hci_close_dev(dd);
+               return FALSE;
+       }
+       DBG("Page Scan Timeout Value Patch %d", value);
+
+       hci_close_dev(dd);
+
+       return TRUE;
+}
+#endif
 
 static void settings_changed(struct btd_adapter *adapter, uint32_t settings)
 {
@@ -490,6 +820,15 @@ static void settings_changed(struct btd_adapter *adapter, uint32_t settings)
 
                if (adapter->current_settings & MGMT_SETTING_POWERED) {
                        adapter_start(adapter);
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined TIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN
+                       /* Approx 6.4 Seconds of timeout */
+                       /* This Added because Z3 device was not able to connect with
+                         * some device as it was getting Page Timeout
+                         * (LG HBS800, sony carkit) etc. So, Increasing Page timeout value
+                         * from 5.12 Sec (which is default) to ~6.4sec*/
+                       DBG("Setting value");
+                       send_sprd_page_scan_timeout(10240);
+#endif /* TIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN */
                } else {
                        adapter_stop(adapter);
 
@@ -524,6 +863,31 @@ static void settings_changed(struct btd_adapter *adapter, uint32_t settings)
 
                trigger_pairable_timeout(adapter);
        }
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (changed_mask & MGMT_SETTING_ADVERTISING) {
+               if ((adapter->current_settings & MGMT_SETTING_ADVERTISING) &&
+                       (adapter->advertising)) {
+                       return;
+               }
+
+               adapter->advertising = adapter->current_settings & MGMT_SETTING_ADVERTISING;
+               advertising_state_changed(adapter, 0, adapter->advertising);
+       }
+
+       if ((changed_mask & MGMT_SETTING_PRIVACY) &&
+           !(adapter->current_settings & MGMT_SETTING_PRIVACY)) {
+               DBG("LE Privacy feature is disabled");
+
+               /*
+                * Some Android devices don't consider the device as LE one,
+                * if the device doesn't distribute IRK when pairing.
+                * Because of this compatibility issue, set IRK
+                * even though privacy feature is disabled.
+                */
+               set_local_irk(adapter);
+       }
+#endif
 }
 
 static void new_settings_callback(uint16_t index, uint16_t length,
@@ -783,6 +1147,10 @@ struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter,
        struct device_addr_type addr;
        struct btd_device *device;
        GSList *list;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       bool exact_match = false;
+#endif
+       char addr_str[18];
 
        if (!adapter)
                return NULL;
@@ -790,13 +1158,33 @@ struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter,
        bacpy(&addr.bdaddr, dst);
        addr.bdaddr_type = bdaddr_type;
 
+       ba2str(dst, addr_str);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        list = g_slist_find_custom(adapter->devices, &addr,
+                                               device_addr_type_strict_cmp);
+       if (list) {
+               device = list->data;
+               exact_match = true;
+       } else {
+#endif
+               list = g_slist_find_custom(adapter->devices, &addr,
                                                        device_addr_type_cmp);
+               if (list) {
+                       device = list->data;
+               }
+       }
+
        if (!list)
                return NULL;
 
        device = list->data;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (exact_match)
+               return device;
+#endif
+
        /*
         * If we're looking up based on public address and the address
         * was not previously used over this bearer we may need to
@@ -1045,6 +1433,13 @@ static void adapter_service_insert(struct btd_adapter *adapter, sdp_record_t *re
 
        DBG("%s", adapter->path);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (rec == NULL) {
+               DBG("record is NULL return");
+               return;
+       }
+#endif
+
        /* skip record without a browse group */
        if (sdp_get_browse_groups(rec, &browse_list) < 0) {
                DBG("skipping record without browse group");
@@ -1106,6 +1501,18 @@ void adapter_service_remove(struct btd_adapter *adapter, uint32_t handle)
        remove_record_from_server(rec->handle);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void adapter_print_devices(struct btd_adapter *adapter)
+{
+       GSList *dev;
+
+       dev = adapter->devices;
+       for (; dev; dev = dev->next) {
+               device_print_addr(dev->data);
+       }
+}
+#endif
+
 static struct btd_device *adapter_create_device(struct btd_adapter *adapter,
                                                const bdaddr_t *bdaddr,
                                                uint8_t bdaddr_type)
@@ -1118,6 +1525,10 @@ static struct btd_device *adapter_create_device(struct btd_adapter *adapter,
 
        adapter->devices = g_slist_append(adapter->devices, device);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       adapter_print_devices(adapter);
+#endif
+
        return device;
 }
 
@@ -1144,6 +1555,46 @@ static void service_auth_cancel(struct service_auth *auth)
        g_free(auth);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void btd_adapter_unpair_device(struct btd_adapter *adapter,
+                               struct btd_device *dev)
+{
+       DBG("+");
+       GList *l;
+
+       adapter->connect_list = g_slist_remove(adapter->connect_list, dev);
+
+//     adapter->devices = g_slist_remove(adapter->devices, dev);
+//
+//     adapter->discovery_found = g_slist_remove(adapter->discovery_found,
+//                                                                     dev);
+
+       adapter->connections = g_slist_remove(adapter->connections, dev);
+
+       if (adapter->connect_le == dev)
+               adapter->connect_le = NULL;
+
+       l = adapter->auths->head;
+       while (l != NULL) {
+               struct service_auth *auth = l->data;
+               GList *next = g_list_next(l);
+
+               if (auth->device != dev) {
+                       l = next;
+                       continue;
+               }
+
+               g_queue_delete_link(adapter->auths, l);
+               l = next;
+
+               service_auth_cancel(auth);
+       }
+
+       device_unpair(dev, TRUE);
+       DBG("-");
+}
+#endif
+
 void btd_adapter_remove_device(struct btd_adapter *adapter,
                                struct btd_device *dev)
 {
@@ -1189,6 +1640,11 @@ struct btd_device *btd_adapter_get_device(struct btd_adapter *adapter,
        if (!adapter)
                return NULL;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (!bacmp(addr, BDADDR_ANY))
+               return NULL;
+#endif
+
        device = btd_adapter_find_device(adapter, addr, addr_type);
        if (device)
                return device;
@@ -1207,6 +1663,11 @@ static void passive_scanning_complete(uint8_t status, uint16_t length,
        struct btd_adapter *adapter = user_data;
        const struct mgmt_cp_start_discovery *rp = param;
 
+       if (!rp) {
+               error("Error ocurred in Scanning, rp is NULL");
+               return;
+       }
+
        DBG("status 0x%02x", status);
 
        if (length < sizeof(*rp)) {
@@ -1229,11 +1690,15 @@ static gboolean passive_scanning_timeout(gpointer user_data)
        adapter->passive_scan_timeout = 0;
 
        cp.type = SCAN_TYPE_LE;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       mgmt_send(adapter->mgmt, MGMT_OP_START_LE_DISCOVERY,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               passive_scanning_complete, adapter, NULL);
+#else
        mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY,
                                adapter->dev_id, sizeof(cp), &cp,
                                passive_scanning_complete, adapter, NULL);
-
+#endif
        return FALSE;
 }
 
@@ -1264,8 +1729,13 @@ static void trigger_passive_scanning(struct btd_adapter *adapter)
         * The discovery procedure is using interleaved scanning and
         * thus will discover Low Energy devices as well.
         */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (adapter->discovery_list || adapter->le_discovery_list)
+               return;
+#else
        if (adapter->discovery_list)
                return;
+#endif
 
        if (adapter->discovery_enable == 0x01)
                return;
@@ -1339,22 +1809,45 @@ static void stop_passive_scanning_complete(uint8_t status, uint16_t length,
 static void stop_passive_scanning(struct btd_adapter *adapter)
 {
        struct mgmt_cp_stop_discovery cp;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct mgmt_cp_stop_discovery le_cp;
+#endif
 
        DBG("");
 
        /* If there are any normal discovery clients passive scanning
         * wont be running */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (adapter->discovery_list || adapter->le_discovery_list)
+               return;
+#else
        if (adapter->discovery_list)
                return;
+#endif
 
        if (adapter->discovery_enable == 0x00)
                return;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if ((adapter->discovery_type & 0x01) > 0) {
+               cp.type = 0x01;
+               mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               stop_passive_scanning_complete, adapter, NULL);
+       }
+       if ((adapter->discovery_type & 0x06) > 0) {
+               le_cp.type = 0x06;
+               mgmt_send(adapter->mgmt, MGMT_OP_STOP_LE_DISCOVERY,
+                               adapter->dev_id, sizeof(le_cp), &le_cp,
+                               stop_passive_scanning_complete, adapter, NULL);
+       }
+#else
        cp.type = adapter->discovery_type;
 
        mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
                        adapter->dev_id, sizeof(cp), &cp,
                        stop_passive_scanning_complete, adapter, NULL);
+#endif
 }
 
 static void cancel_passive_scanning(struct btd_adapter *adapter)
@@ -1403,6 +1896,9 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
        const struct mgmt_cp_start_discovery *rp = param;
 
        DBG("status 0x%02x", status);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+       DBG("Discovery Type 0x%02x", rp->type);
+#endif
 
        if (length < sizeof(*rp)) {
                btd_error(adapter->dev_id,
@@ -1411,7 +1907,12 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
        }
 
        if (status == MGMT_STATUS_SUCCESS) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               DBG("Return param discovery type 0x%02x", rp->type);
+               adapter->discovery_type |= rp->type;
+#else
                adapter->discovery_type = rp->type;
+#endif
                adapter->discovery_enable = 0x01;
 
                if (adapter->current_discovery_filter)
@@ -1426,30 +1927,150 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
                g_dbus_emit_property_changed(dbus_conn, adapter->path,
                                        ADAPTER_INTERFACE, "Discovering");
                return;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       } else {
+               adapter->discovering = false;
+               g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                                       ADAPTER_INTERFACE, "Discovering");
+#endif
+
        }
 
        /*
         * In case the restart of the discovery failed, then just trigger
         * it for the next idle timeout again.
         */
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT * 2);
+#endif
 }
 
-static gboolean start_discovery_timeout(gpointer user_data)
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void start_le_discovery_complete(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
 {
        struct btd_adapter *adapter = user_data;
-       struct mgmt_cp_start_service_discovery *sd_cp;
-       uint8_t new_type;
+       const struct mgmt_cp_start_discovery *rp = param;
 
-       DBG("");
+       if (!rp) {
+               error("Error ocurred in LEDiscovering, rp is NULL");
+               return;
+       }
 
-       adapter->discovery_idle_timeout = 0;
+       DBG("status 0x%02x", status);
+       if (length < sizeof(*rp)) {
+               error("Wrong size of start discovery return parameters");
+               return;
+       }
 
-       /* If we're doing filtered discovery, it must be quickly restarted */
-       adapter->no_scan_restart_delay = !!adapter->current_discovery_filter;
+       DBG("Discovery Type 0x%02x", rp->type);
+       if (status == MGMT_STATUS_SUCCESS) {
+               adapter->discovery_type |= rp->type;
+               adapter->discovery_enable = 0x01;
 
-       DBG("adapter->current_discovery_filter == %d",
-           !!adapter->current_discovery_filter);
+               if (adapter->le_discovering)
+                       return;
+
+               adapter->le_discovering = true;
+               g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                                       ADAPTER_INTERFACE, "LEDiscovering");
+
+               return;
+       } else {
+               adapter->le_discovering = false;
+               g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                                       ADAPTER_INTERFACE, "LEDiscovering");
+
+       }
+}
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean start_le_discovery_timeout(gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       uint8_t new_type;
+
+       DBG("");
+
+       adapter->le_discovery_idle_timeout = 0;
+
+       new_type = SCAN_TYPE_LE;
+
+       if (adapter->discovery_enable == 0x01) {
+               /*
+                * If there is an already running discovery and it has the
+                * same type, then just keep it.
+                */
+
+               if ((adapter->discovery_type & new_type) == SCAN_TYPE_LE) {
+                       if (adapter->le_discovering)
+                               return FALSE;
+
+                       adapter->le_discovering = true;
+                       g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                                       ADAPTER_INTERFACE, "LEDiscovering");
+
+                       return FALSE;
+               }
+       }
+
+       struct mgmt_cp_start_discovery cp;
+
+       cp.type = new_type;
+       mgmt_send(adapter->mgmt, MGMT_OP_START_LE_DISCOVERY,
+                       adapter->dev_id, sizeof(cp), &cp,
+                       start_le_discovery_complete, adapter, NULL);
+
+       return FALSE;
+}
+#endif
+
+static gboolean start_discovery_timeout(gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct mgmt_cp_start_service_discovery *sd_cp;
+#endif
+       uint8_t new_type;
+
+       DBG("");
+
+       adapter->discovery_idle_timeout = 0;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       new_type = SCAN_TYPE_BREDR;
+
+       if (adapter->discovery_enable == 0x01) {
+               /*
+                * If there is an already running discovery and it has the
+                * same type, then just keep it.
+                */
+               if ((adapter->discovery_type & new_type) == SCAN_TYPE_BREDR) {
+                       if (adapter->discovering)
+                               return FALSE;
+
+                       adapter->discovering = true;
+                       g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                                       ADAPTER_INTERFACE, "Discovering");
+
+                       return FALSE;
+               }
+       }
+
+       struct mgmt_cp_start_discovery cp;
+       cp.type = new_type;
+       mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               start_discovery_complete, adapter, NULL);
+
+#else
+       /* If we're doing filtered discovery, it must be quickly restarted */
+       adapter->no_scan_restart_delay = !!adapter->current_discovery_filter;
+
+       DBG("adapter->current_discovery_filter == %d",
+           !!adapter->current_discovery_filter);
 
        new_type = get_scan_type(adapter);
 
@@ -1516,9 +2137,38 @@ static gboolean start_discovery_timeout(gpointer user_data)
                  adapter->dev_id, sizeof(*sd_cp) + sd_cp->uuid_count * 16,
                  sd_cp, start_discovery_complete, adapter, NULL);
 
+#endif
+
        return FALSE;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void trigger_start_le_discovery(struct btd_adapter *adapter, guint delay)
+{
+
+       DBG("");
+
+       cancel_passive_scanning(adapter);
+
+       if (adapter->le_discovery_idle_timeout > 0) {
+               g_source_remove(adapter->le_discovery_idle_timeout);
+               adapter->le_discovery_idle_timeout = 0;
+       }
+
+       /*
+        * If the controller got powered down in between, then ensure
+        * that we do not keep trying to restart discovery.
+        *
+        * This is safe-guard and should actually never trigger.
+        */
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return;
+
+       adapter->le_discovery_idle_timeout = g_timeout_add_seconds(delay,
+                                       start_le_discovery_timeout, adapter);
+}
+#endif
+
 static void trigger_start_discovery(struct btd_adapter *adapter, guint delay)
 {
 
@@ -1544,6 +2194,7 @@ static void trigger_start_discovery(struct btd_adapter *adapter, guint delay)
                                        start_discovery_timeout, adapter);
 }
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
 static void suspend_discovery_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
@@ -1614,6 +2265,7 @@ static void resume_discovery(struct btd_adapter *adapter)
         */
        trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT);
 }
+#endif
 
 static void discovering_callback(uint16_t index, uint16_t length,
                                        const void *param, void *user_data)
@@ -1629,11 +2281,40 @@ static void discovering_callback(uint16_t index, uint16_t length,
        DBG("hci%u type %u discovering %u method %d", adapter->dev_id, ev->type,
                                ev->discovering, adapter->filtered_discovery);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       DBG("info discov_type %d", adapter->discovery_type);
+       if (ev->type == SCAN_TYPE_BREDR) {
+               if (ev->discovering == FALSE) {
+                       hci_clear_bit(BDADDR_BREDR, &adapter->discovery_type);
+                       adapter->discovering = false;
+               } else {
+                       hci_set_bit(BDADDR_BREDR, &adapter->discovery_type);
+                       adapter->discovering = true;
+               }
+               g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                                       ADAPTER_INTERFACE, "Discovering");
+
+       } else if (ev->type == SCAN_TYPE_LE) {
+               if (ev->discovering == FALSE) {
+                       hci_clear_bit(BDADDR_LE_PUBLIC, &adapter->discovery_type);
+                       hci_clear_bit(BDADDR_LE_RANDOM, &adapter->discovery_type);
+                       adapter->le_discovering = false;
+               } else {
+                       hci_set_bit(BDADDR_LE_PUBLIC, &adapter->discovery_type);
+                       hci_set_bit(BDADDR_LE_RANDOM, &adapter->discovery_type);
+                       adapter->le_discovering = true;
+               }
+
+               g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                                       ADAPTER_INTERFACE, "LEDiscovering");
+       }
+#else
        if (adapter->discovery_enable == ev->discovering)
                return;
 
        adapter->discovery_type = ev->type;
        adapter->discovery_enable = ev->discovering;
+#endif
 
        /*
         * Check for existing discoveries triggered by client applications
@@ -1642,11 +2323,19 @@ static void discovering_callback(uint16_t index, uint16_t length,
         * If there are no clients, then it is good idea to trigger a
         * passive scanning attempt.
         */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (adapter->discovery_list == NULL && adapter->le_discovery_list == NULL) {
+               if (!adapter->connect_le)
+                       trigger_passive_scanning(adapter);
+               return;
+       }
+#else
        if (!adapter->discovery_list) {
                if (!adapter->connect_le)
                        trigger_passive_scanning(adapter);
                return;
        }
+#endif
 
        if (adapter->discovery_suspended)
                return;
@@ -1669,6 +2358,57 @@ static void discovering_callback(uint16_t index, uint16_t length,
        }
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void stop_discovery_complete(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       DBG("status 0x%02x", status);
+
+       if (status == MGMT_STATUS_SUCCESS) {
+               adapter->discovery_type &= (~0x01);
+               DBG("Discovery Type 0x%02x", adapter->discovery_type);
+
+               adapter->filtered_discovery = false;
+               adapter->no_scan_restart_delay = false;
+               adapter->discovering = false;
+               g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                                       ADAPTER_INTERFACE, "Discovering");
+
+               if (adapter->discovery_list == NULL && adapter->le_discovery_list == NULL) {
+                       adapter->discovery_enable = 0x00;
+                       trigger_passive_scanning(adapter);
+               }
+       }
+}
+
+static void stop_le_discovery_complete(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       DBG("status 0x%02x", status);
+
+       if (status == MGMT_STATUS_SUCCESS) {
+               adapter->discovery_type &= (~0x06);
+               DBG("Discovery Type 0x%02x", adapter->discovery_type);
+
+               adapter->filtered_discovery = false;
+               adapter->no_scan_restart_delay = false;
+               adapter->le_discovering = false;
+               g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                                       ADAPTER_INTERFACE, "LEDiscovering");
+
+               if (adapter->discovery_list == NULL && adapter->le_discovery_list == NULL) {
+                       adapter->discovery_enable = 0x00;
+                       trigger_passive_scanning(adapter);
+               }
+       }
+}
+
+#else
+
 static void stop_discovery_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
@@ -1688,6 +2428,7 @@ static void stop_discovery_complete(uint8_t status, uint16_t length,
                trigger_passive_scanning(adapter);
        }
 }
+#endif
 
 static int compare_sender(gconstpointer a, gconstpointer b)
 {
@@ -1969,7 +2710,11 @@ static void discovery_destroy(void *user_data)
        if (adapter->discovery_list)
                return;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       hci_clear_bit(BDADDR_BREDR, &adapter->discovery_type);
+#else
        adapter->discovery_type = 0x00;
+#endif
 
        if (adapter->discovery_idle_timeout > 0) {
                g_source_remove(adapter->discovery_idle_timeout);
@@ -2059,80 +2804,2876 @@ static bool get_discovery_client(struct btd_adapter *adapter,
        return false;
 }
 
-static DBusMessage *start_discovery(DBusConnection *conn,
-                                       DBusMessage *msg, void *user_data)
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void le_discovery_destroy(void *user_data)
 {
-       struct btd_adapter *adapter = user_data;
-       const char *sender = dbus_message_get_sender(msg);
-       struct watch_client *client;
-       bool is_discovering;
+       struct watch_client *client = user_data;
+       struct btd_adapter *adapter = client->adapter;
 
-       DBG("sender %s", sender);
+       DBG("owner %s", client->owner);
 
-       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
-               return btd_error_not_ready(msg);
+       adapter->le_discovery_list = g_slist_remove(adapter->le_discovery_list,
+                                                               client);
 
-       is_discovering = get_discovery_client(adapter, sender, &client);
+       g_free(client->owner);
+       g_free(client);
 
        /*
-        * Every client can only start one discovery, if the client
-        * already started a discovery then return an error.
+        * If there are other client discoveries in progress, then leave
+        * it active. If not, then make sure to stop the restart timeout.
         */
-       if (is_discovering)
-               return btd_error_busy(msg);
+       DBG("adapter->discovery_list[%p] adapter->le_discovery_list[%p]",
+                       adapter->discovery_list, adapter->le_discovery_list);
+       if (adapter->discovery_list || adapter->le_discovery_list)
+               return;
 
-       /*
-        * If there was pre-set filter, just reconnect it to discovery_list,
-        * and trigger scan.
-        */
-       if (client) {
-               adapter->set_filter_list = g_slist_remove(
-                                            adapter->set_filter_list, client);
-               adapter->discovery_list = g_slist_prepend(
-                                             adapter->discovery_list, client);
-               update_discovery_filter(adapter);
-               return dbus_message_new_method_return(msg);
+       hci_clear_bit(BDADDR_LE_PUBLIC, &adapter->discovery_type);
+       hci_clear_bit(BDADDR_LE_RANDOM, &adapter->discovery_type);
+
+       DBG("Restart Timer... adapter->discovery_type[%d]", adapter->discovery_type);
+       if (adapter->discovery_idle_timeout > 0) {
+               g_source_remove(adapter->discovery_idle_timeout);
+               adapter->discovery_idle_timeout = 0;
        }
 
-       client = g_new0(struct watch_client, 1);
+       if (adapter->temp_devices_timeout > 0) {
+               g_source_remove(adapter->temp_devices_timeout);
+               adapter->temp_devices_timeout = 0;
+       }
 
-       client->adapter = adapter;
-       client->owner = g_strdup(sender);
-       client->discovery_filter = NULL;
-       client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
-                                               discovery_disconnect, client,
-                                               discovery_destroy);
-       adapter->discovery_list = g_slist_prepend(adapter->discovery_list,
+       discovery_cleanup(adapter);
+}
+
+static void le_discovery_disconnect(DBusConnection *conn, void *user_data)
+{
+       struct watch_client *client = user_data;
+       struct btd_adapter *adapter = client->adapter;
+       struct mgmt_cp_stop_le_discovery cp;
+
+       DBG("owner %s", client->owner);
+
+       adapter->le_discovery_list = g_slist_remove(adapter->le_discovery_list,
                                                                client);
 
        /*
-        * Just trigger the discovery here. In case an already running
-        * discovery in idle phase exists, it will be restarted right
-        * away.
+        * There is no need for extra cleanup of the client since that
+        * will be done by the destroy callback.
+        *
+        * However in case this is the last client, the discovery in
+        * the kernel needs to be disabled.
         */
-       update_discovery_filter(adapter);
+       if (adapter->le_discovery_list)
+               return;
 
-       return dbus_message_new_method_return(msg);
+       /*
+        * In the idle phase of a discovery, there is no need to stop it
+        * and so it is enough to send out the signal and just return.
+        */
+       if (adapter->discovery_enable == 0x00) {
+               adapter->le_discovering = false;
+               g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                                       ADAPTER_INTERFACE, "LEDiscovering");
+
+               if (adapter->discovering == false  && adapter->le_discovering == false) {
+                       trigger_passive_scanning(adapter);
+                       return;
+               }
+       }
+
+       cp.type = 0x06;
+
+       mgmt_send(adapter->mgmt, MGMT_OP_STOP_LE_DISCOVERY,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               stop_discovery_complete, adapter, NULL);
 }
 
-static bool parse_uuids(DBusMessageIter *value, GSList **uuids)
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+static void addr_filter_params_free(gpointer data, gpointer user_data)
 {
-       DBusMessageIter arriter;
+       adapter_le_address_filter_params_t *params = data;
 
-       if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_ARRAY)
-               return false;
+       g_free(params);
+}
 
-       dbus_message_iter_recurse(value, &arriter);
-       while (dbus_message_iter_get_arg_type(&arriter) != DBUS_TYPE_INVALID) {
-               bt_uuid_t uuid, u128;
-               char uuidstr[MAX_LEN_UUID_STR + 1];
-               char *uuid_param;
+static void uuid_filter_params_free(gpointer data, gpointer user_data)
+{
+       adapter_le_uuid_params_t *params = data;
 
-               if (dbus_message_iter_get_arg_type(&arriter) !=
-                                               DBUS_TYPE_STRING)
-                       return false;
+       g_free((char *)params->uuid);
+       g_free((char *)params->uuid_mask);
+       g_free(params);
+}
 
-               dbus_message_iter_get_basic(&arriter, &uuid_param);
+static void manufacturer_filter_params_free(gpointer data, gpointer user_data)
+{
+       adapter_le_manf_data_params_t *params = data;
+
+       g_free((char *)params->man_data);
+       g_free((char *)params->man_data_mask);
+       g_free(params);
+}
+
+static void local_name_filter_params_free(gpointer data, gpointer user_data)
+{
+       adapter_le_local_name_params_t *params = data;
+
+       g_free((char *)params->local_name);
+       g_free(params);
+}
+
+static void service_data_filter_params_free(gpointer data, gpointer user_data)
+{
+       adapter_le_service_data_params_t *params = data;
+
+       g_free((char *)params->service_data);
+       g_free((char *)params->service_data_mask);
+       g_free(params);
+}
+
+static void scan_filter_params_free(gpointer data, gpointer user_data)
+{
+       adapter_le_scan_filter_param_t *params = data;
+       g_free(params);
+}
+
+int adapter_le_address_cmp(gconstpointer a, gconstpointer b)
+{
+       const adapter_le_address_filter_params_t *params = a;
+       const char *address = b;
+       char addr[18];
+
+       ba2str(&params->broadcaster_addr, addr);
+       return strcasecmp(addr, address);
+}
+
+int adapter_le_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+       const adapter_le_uuid_params_t *params = a;
+       const char *uuid = b;
+
+       return strcasecmp((const char *)params->uuid, uuid);
+}
+
+int adapter_le_manufacturer_data_cmp(gconstpointer a, gconstpointer b)
+{
+       const adapter_le_manf_data_params_t *params = a;
+       const struct eir_msd *msd = b;
+
+       if (msd->company == params->company_id) {
+               /* if the advertisiement packet is an iBeacon */
+               if (msd->company == COMPANY_ID_APPLE)
+                       return 0;
+               return strncasecmp((const char *)params->man_data, msd->data, params->man_data_len);
+       } else {
+               return -1;
+       }
+}
+
+int adapter_le_local_name_cmp(gconstpointer a, gconstpointer b)
+{
+       const adapter_le_local_name_params_t *params = a;
+       const char *name = b;
+
+       return strcasecmp(params->local_name, name);
+}
+
+int adapter_le_service_data_cmp(gconstpointer a, gconstpointer b)
+{
+       const adapter_le_service_data_params_t *params = a;
+       const struct eir_sd *sd = b;
+       /* Todo, the service data format for 16 bit, 32bit and
+        * 128 bit uuids needs to addressed */
+       return strncasecmp((const char *)(params->service_data), sd->data, sd->data_len);
+}
+
+int adapter_le_address_filter_index_cmp(gconstpointer a, gconstpointer b)
+{
+       const adapter_le_address_filter_params_t *params = a;
+       uint16_t filter_inex = GPOINTER_TO_UINT(b);
+
+       return params->filter_index - filter_inex;
+}
+
+int adapter_le_uuid_filter_index_cmp(gconstpointer a, gconstpointer b)
+{
+       const adapter_le_uuid_params_t *params = a;
+       uint16_t filter_inex = GPOINTER_TO_UINT(b);
+
+       return params->filter_index - filter_inex;
+}
+
+int adapter_le_manufacturer_data_filter_index_cmp(gconstpointer a, gconstpointer b)
+{
+       const adapter_le_manf_data_params_t *params = a;
+       uint16_t filter_inex = GPOINTER_TO_UINT(b);
+
+       return params->filter_index - filter_inex;
+}
+
+int adapter_le_local_name_filter_index_cmp(gconstpointer a, gconstpointer b)
+{
+       const adapter_le_local_name_params_t *params = a;
+       uint16_t filter_inex = GPOINTER_TO_UINT(b);
+
+       return params->filter_index - filter_inex;
+}
+
+int adapter_le_service_data_filter_index_cmp(gconstpointer a, gconstpointer b)
+{
+       const adapter_le_service_data_params_t *params = a;
+       uint16_t filter_inex = GPOINTER_TO_UINT(b);
+
+       return params->filter_index - filter_inex;
+}
+
+int adapter_le_scan_params_filter_index_cmp(gconstpointer a, gconstpointer b)
+{
+       const adapter_le_scan_filter_param_t *params = a;
+       uint16_t filter_inex = GPOINTER_TO_UINT(b);
+
+       return params->index - filter_inex;
+}
+
+static gboolean adapter_le_clear_platform_scan_filter_data(
+                       struct btd_adapter *adapter, int filter_index)
+{
+       DBG("");
+       GSList *list;
+       if (!adapter)
+               return FALSE;
+
+       list = g_slist_find_custom(adapter->addr_filters,
+               GINT_TO_POINTER(filter_index), adapter_le_address_filter_index_cmp);
+       if (list && list->data) {
+               /* Delete info from the struct to list */
+               adapter->addr_filters = g_slist_delete_link(adapter->addr_filters, list);
+       }
+       list = g_slist_find_custom(adapter->service_data_changed_filters,
+               GINT_TO_POINTER(filter_index), adapter_le_service_data_filter_index_cmp);
+       if (list && list->data) {
+               /* Delete info from the struct to list */
+               adapter->service_data_changed_filters = g_slist_delete_link(adapter->service_data_changed_filters, list);
+       }
+
+       list = g_slist_find_custom(adapter->service_uuid_filters,
+               GINT_TO_POINTER(filter_index), adapter_le_uuid_filter_index_cmp);
+       if (list && list->data) {
+               /* Delete info from the struct to list */
+               adapter->service_uuid_filters = g_slist_delete_link(adapter->service_uuid_filters, list);
+       }
+
+       list = g_slist_find_custom(adapter->solicit_data_filters,
+               GINT_TO_POINTER(filter_index), adapter_le_uuid_filter_index_cmp);
+       if (list && list->data) {
+               /* Delete info from the struct to list */
+               adapter->solicit_data_filters = g_slist_delete_link(adapter->solicit_data_filters, list);
+       }
+
+       list = g_slist_find_custom(adapter->local_name_filters,
+               GINT_TO_POINTER(filter_index), adapter_le_local_name_filter_index_cmp);
+       if (list && list->data) {
+               /* Delete info from the struct to list */
+               adapter->local_name_filters = g_slist_delete_link(adapter->local_name_filters, list);
+       }
+
+       list = g_slist_find_custom(adapter->manufaturer_data_filters,
+               GINT_TO_POINTER(filter_index), adapter_le_manufacturer_data_filter_index_cmp);
+       if (list && list->data) {
+               /* Delete info from the struct to list */
+               adapter->manufaturer_data_filters = g_slist_delete_link(adapter->manufaturer_data_filters, list);
+       }
+
+       list = g_slist_find_custom(adapter->service_data_filters,
+               GINT_TO_POINTER(filter_index), adapter_le_service_data_filter_index_cmp);
+       if (list && list->data) {
+               /* Delete info from the struct to list */
+               adapter->service_data_filters = g_slist_delete_link(adapter->service_data_filters, list);
+       }
+
+       list = g_slist_find_custom(adapter->scan_params,
+               GINT_TO_POINTER(filter_index), adapter_le_scan_params_filter_index_cmp);
+       if (list && list->data) {
+               /* Delete info from the struct to list */
+               adapter->scan_params = g_slist_delete_link(adapter->scan_params, list);
+       }
+
+       return TRUE;
+}
+
+static gboolean adapter_le_enable_platform_scan_filtering(
+                       struct btd_adapter *adapter, gboolean enable)
+{
+       if (!adapter)
+               return FALSE;
+
+       DBG("Platform scan filtering enable[%d]", enable);
+
+       adapter->scan_filter_support = enable;
+
+       return TRUE;
+}
+
+
+static gboolean adapter_le_service_add_addr_scan_filter_data(struct btd_adapter *adapter,
+                                               int filter_index, gchar *string, int addr_type)
+{
+       /* TYPE_DEVICE_ADDRESS */
+       adapter_le_address_filter_params_t *params;
+
+       DBG("");
+
+       params = g_new0(adapter_le_address_filter_params_t, 1);
+       if (!params)
+               return FALSE;
+
+       params->filter_index = filter_index;
+       str2ba(string, &params->broadcaster_addr);
+       params->bdaddr_type = addr_type;
+
+       /* Store the struct to list */
+       adapter->addr_filters = g_slist_append(adapter->addr_filters, params);
+       return TRUE;
+}
+
+static const char *adapter_le_service_find_addr_scan_filter_data(
+                                               struct btd_adapter *adapter, gchar *string)
+{
+       GSList *list;
+       DBG("");
+
+       list = g_slist_find_custom(adapter->addr_filters, string, adapter_le_address_cmp);
+       if (!list)
+               return NULL;
+       else
+               return list->data;
+
+       return NULL;
+}
+
+static gboolean adapter_le_service_delete_addr_scan_filter_data(struct btd_adapter *adapter,
+                                               int filter_index, gchar *string, int addr_type)
+{
+       GSList *list;
+       DBG("");
+
+       list = g_slist_find_custom(adapter->addr_filters, string, adapter_le_address_cmp);
+       if (!list)
+               return FALSE;
+       else
+               /* Delete info from the struct to list */
+               adapter->addr_filters = g_slist_delete_link(adapter->addr_filters, list);
+
+       return TRUE;
+}
+
+static gboolean adapter_le_service_clear_addr_scan_filter_data(struct btd_adapter *adapter)
+{
+       DBG("");
+
+       g_slist_free_full(adapter->addr_filters, addr_filter_params_free);
+       adapter->addr_filters = NULL;
+
+       return TRUE;
+}
+
+static gboolean adapter_le_service_add_uuid_scan_filter_data(struct btd_adapter *adapter,
+                                                       int filter_index, gboolean is_solicited, uint8_t *p_uuid,
+                                                       uint8_t *p_uuid_mask, int uuid_mask_len)
+{
+
+       adapter_le_uuid_params_t *params;
+       bt_uuid_t uuid;
+
+       DBG("");
+
+       params = g_new0(adapter_le_uuid_params_t, 1);
+       if (!params)
+               return FALSE;
+
+       if (uuid_mask_len == UUID_16_LEN) {
+               uint16_t *uuid16 = (void *)p_uuid;
+               sdp_uuid16_create(&uuid, get_be16(uuid16));
+       } else if (uuid_mask_len == UUID_32_LEN) {
+               uint32_t *uuid32 = (void *)p_uuid;
+               sdp_uuid32_create(&uuid, get_be32(uuid32));
+       } else {
+               sdp_uuid128_create(&uuid, p_uuid);
+       }
+       params->filter_index = filter_index;
+       params->uuid = bt_uuid2string(&uuid);
+       params->uuid_mask = g_new0(uint8_t, uuid_mask_len);
+        memcpy(params->uuid_mask, p_uuid_mask, uuid_mask_len);
+       params->uuid_len = uuid_mask_len;
+
+       /* Store the struct to list */
+       adapter->solicit_data_filters = g_slist_append(adapter->solicit_data_filters, params);
+
+       return TRUE;
+}
+
+static adapter_le_uuid_params_t *adapter_le_service_find_uuid_scan_filter_data(struct btd_adapter *adapter,
+                                                       uint8_t *p_uuid)
+{
+       GSList *list;
+       DBG("");
+
+       list = g_slist_find_custom(adapter->solicit_data_filters, p_uuid, adapter_le_uuid_cmp);
+       if (!list)
+               return NULL;
+       else
+               /* Delete info from the struct to list */
+               return list->data;
+
+       return NULL;
+}
+
+static gboolean adapter_le_service_delete_uuid_scan_filter_data(struct btd_adapter *adapter,
+                                                       int filter_index, gboolean is_solicited, uint8_t *p_uuid,
+                                                       uint8_t *p_uuid_mask, int uuid_mask_len)
+{
+       GSList *list;
+       DBG("");
+
+       list = g_slist_find_custom(adapter->solicit_data_filters, GINT_TO_POINTER(filter_index), adapter_le_uuid_filter_index_cmp);
+       if (!list)
+               return FALSE;
+       else {
+               adapter_le_uuid_params_t *params = list->data;
+               /* Delete info from the struct to list */
+               if (params && strcasecmp((const char *)params->uuid, (const char *)p_uuid)) {
+                       adapter->solicit_data_filters = g_slist_delete_link(adapter->solicit_data_filters, list);
+               }
+       }
+
+       return TRUE;
+}
+
+static gboolean adapter_le_service_clear_uuid_scan_filter_data(struct btd_adapter *adapter)
+{
+       DBG("");
+
+       g_slist_free_full(adapter->solicit_data_filters, uuid_filter_params_free);
+       adapter->solicit_data_filters = NULL;
+
+       return TRUE;
+}
+
+static gboolean adapter_le_service_add_manufacturer_scan_filter_data(struct btd_adapter *adapter,
+                                                       int filter_index, int company_id, int company_id_mask,
+                                                       uint8_t *p_data, uint8_t *p_mask, int data_len)
+{
+
+       adapter_le_manf_data_params_t *params;
+
+       DBG("");
+
+       params = g_new0(adapter_le_manf_data_params_t, 1);
+       if (!params)
+               return FALSE;
+
+       params->filter_index = filter_index;
+       params->company_id = company_id;
+       params->company_id_mask = company_id_mask;
+       params->man_data = g_new0(uint8_t, data_len);
+       memcpy(params->man_data, p_data, data_len);
+       params->man_data_mask = g_new0(uint8_t, data_len);
+        memcpy(params->man_data_mask, p_mask, data_len);
+       params->man_data_len = data_len;
+
+       /* Store the struct to list */
+       adapter->manufaturer_data_filters = g_slist_append(adapter->manufaturer_data_filters, params);
+
+       return TRUE;
+}
+
+static adapter_le_manf_data_params_t *adapter_le_service_find_manufacturer_scan_filter_data(struct btd_adapter *adapter,
+                                                       struct eir_msd *msd)
+{
+       GSList *list;
+       DBG("");
+       list = g_slist_find_custom(adapter->manufaturer_data_filters, msd, adapter_le_manufacturer_data_cmp);
+       if (!list)
+               return NULL;
+       else
+               return list->data;
+
+       return NULL;
+}
+
+static gboolean adapter_le_service_delete_manufacturer_scan_filter_data(struct btd_adapter *adapter,
+                                                       int filter_index, int company_id, int company_id_mask,
+                                                       uint8_t *p_data, uint8_t *p_mask, int data_len)
+{
+       GSList *list;
+       DBG("");
+       list = g_slist_find_custom(adapter->manufaturer_data_filters, GINT_TO_POINTER(filter_index), adapter_le_manufacturer_data_filter_index_cmp);
+       if (!list)
+               return FALSE;
+       else {
+               adapter_le_manf_data_params_t *params = list->data;
+               /* Delete info from the struct to list */
+               if (params && strcasecmp((const char *)params->man_data, (const char *)p_data)) {
+                       adapter->manufaturer_data_filters = g_slist_delete_link(adapter->manufaturer_data_filters, list);
+               }
+       }
+
+       return TRUE;
+}
+
+static gboolean adapter_le_service_clear_manufacturer_scan_filter_data(struct btd_adapter *adapter)
+{
+       DBG("");
+
+       g_slist_free_full(adapter->manufaturer_data_filters, manufacturer_filter_params_free);
+       adapter->manufaturer_data_filters = NULL;
+
+       return TRUE;
+}
+
+static gboolean adapter_le_service_add_local_name_scan_filter_data(struct btd_adapter *adapter,
+                                                                                       int filter_index, gchar *name)
+{
+
+       adapter_le_local_name_params_t *params;
+
+       DBG("");
+
+       params = g_new0(adapter_le_local_name_params_t, 1);
+       if (!params)
+               return FALSE;
+
+       params->filter_index = filter_index;
+       params->local_name = g_strdup(name);
+       params->name_len = strlen(name);
+
+       /* Store the struct to list */
+       adapter->local_name_filters = g_slist_append(adapter->local_name_filters, params);
+
+       return TRUE;
+}
+
+static adapter_le_local_name_params_t *adapter_le_service_find_local_name_scan_filter_data(
+                                                                                               struct btd_adapter *adapter,
+                                                                                               gchar *name)
+{
+       GSList *list;
+       DBG("");
+       list = g_slist_find_custom(adapter->local_name_filters, name, adapter_le_local_name_cmp);
+       if (!list)
+               return NULL;
+       else
+               return list->data;
+
+       return NULL;
+}
+
+static gboolean adapter_le_service_delete_local_name_scan_filter_data(struct btd_adapter *adapter,
+                                                                                               int filter_index, gchar *name)
+{
+       GSList *list;
+       DBG("");
+       list = g_slist_find_custom(adapter->local_name_filters, GINT_TO_POINTER(filter_index), adapter_le_local_name_filter_index_cmp);
+       if (!list)
+               return FALSE;
+       else {
+               adapter_le_local_name_params_t *params = list->data;
+               /* Delete info from the struct to list */
+               if (params && strcasecmp((const char *)params->local_name, (const char *)name)) {
+                       adapter->local_name_filters = g_slist_delete_link(adapter->local_name_filters, list);
+               }
+       }
+
+       return TRUE;
+}
+
+static gboolean adapter_le_service_clear_local_name_scan_filter_data(struct btd_adapter *adapter)
+{
+       DBG("");
+
+       g_slist_free_full(adapter->local_name_filters, local_name_filter_params_free);
+       adapter->local_name_filters = NULL;
+
+       return TRUE;
+}
+
+static gboolean adapter_le_service_add_service_scan_filter_data(struct btd_adapter *adapter,
+                                                       int filter_index, uint8_t *p_data, uint8_t *p_mask, int data_len)
+{
+       adapter_le_service_data_params_t *params;
+
+       DBG("");
+
+       params = g_new0(adapter_le_service_data_params_t, 1);
+       if (!params)
+               return FALSE;
+
+       params->filter_index = filter_index;
+       params->service_data = g_new0(uint8_t, data_len);
+       memcpy(params->service_data, p_data, data_len);
+       params->service_data_mask = g_new0(uint8_t, data_len);
+       memcpy(params->service_data_mask, p_mask, data_len);
+       params->service_data_len = data_len;
+
+       /* Store the struct to list */
+       adapter->service_data_filters = g_slist_append(adapter->service_data_filters, params);
+
+       return TRUE;
+}
+
+static adapter_le_service_data_params_t *adapter_le_service_find_service_scan_filter_data(
+                                                       struct btd_adapter *adapter, struct eir_sd *sd)
+{
+       GSList *list;
+       DBG("");
+
+       list = g_slist_find_custom(adapter->service_data_filters, sd, adapter_le_service_data_cmp);
+       if (!list)
+               return NULL;
+       else
+               return list->data;
+
+       return NULL;
+}
+
+static gboolean adapter_le_service_delete_service_scan_filter_data(struct btd_adapter *adapter,
+                                                       int filter_index, uint8_t *p_data, uint8_t *p_mask, int data_len)
+{
+       GSList *list;
+       DBG("");
+
+       list = g_slist_find_custom(adapter->service_data_filters, GINT_TO_POINTER(filter_index), adapter_le_service_data_filter_index_cmp);
+       if (!list)
+               return FALSE;
+       else {
+               adapter_le_service_data_params_t *params = list->data;
+               /* Delete info from the struct to list */
+               if (params && strcasecmp((const char *)params->service_data, (const char *)p_data)) {
+                       adapter->service_data_filters = g_slist_delete_link(adapter->service_data_filters, list);
+               }
+       }
+       return TRUE;
+}
+
+static gboolean adapter_le_service_clear_service_scan_filter_data(struct btd_adapter *adapter)
+{
+       DBG("");
+
+       g_slist_free_full(adapter->service_data_filters, service_data_filter_params_free);
+       adapter->service_data_filters = NULL;
+
+       return TRUE;
+}
+
+static gboolean adapter_le_service_add_scan_filter_params(struct btd_adapter *adapter,
+                                                       adapter_le_scan_filter_param_t *params)
+{
+       adapter_le_scan_filter_param_t *l_params;
+
+       DBG("");
+
+       l_params = g_new0(adapter_le_scan_filter_param_t, 1);
+       if (!l_params)
+               return FALSE;
+
+       l_params->action = params->action;
+       l_params->delivery_mode = params->delivery_mode;
+       l_params->feature = params->feature;
+       l_params->filter_logic_type = params->filter_logic_type;
+       l_params->index = params->index;
+       l_params->list_logic_type = params->list_logic_type;
+       l_params->onfound_timeout = params->onfound_timeout;
+       l_params->onfound_timeout_cnt = params->onfound_timeout_cnt;
+       l_params->rssi_high_threshold = params->rssi_high_threshold;
+       l_params->rssi_low_threshold = params->rssi_low_threshold;
+
+       /* Store the struct to list */
+       adapter->scan_params = g_slist_append(adapter->scan_params, l_params);
+
+       return TRUE;
+}
+
+static adapter_le_service_data_params_t *adapter_le_service_find_scan_filter_params(
+                                                       struct btd_adapter *adapter, int filter_index)
+{
+       GSList *list;
+       DBG("");
+
+       list = g_slist_find_custom(adapter->scan_params, GINT_TO_POINTER(filter_index), adapter_le_scan_params_filter_index_cmp);
+       if (!list)
+               return NULL;
+       else
+               return list->data;
+
+       return NULL;
+}
+
+static gboolean adapter_le_service_delete_scan_filter_params(struct btd_adapter *adapter,
+                                                       adapter_le_scan_filter_param_t *params)
+{
+       GSList *list;
+       DBG("");
+
+       list = g_slist_find_custom(adapter->scan_params, GINT_TO_POINTER(params->index), adapter_le_scan_params_filter_index_cmp);
+       if (!list)
+               return FALSE;
+       else
+               adapter->scan_params = g_slist_remove(adapter->scan_params, list);
+
+       return TRUE;
+}
+
+static gboolean adapter_le_service_clear_scan_filter_params(struct btd_adapter *adapter)
+{
+       DBG("");
+
+       g_slist_free_full(adapter->scan_params, scan_filter_params_free);
+       adapter->scan_params = NULL;
+
+       return TRUE;
+}
+
+int adapter_byte_arr_cmp_with_mask(const char *data1, const char *data2,
+        const char *mask, int data_len)
+{
+       int i;
+       char a, b;
+       if (data1 == NULL || data2 == NULL || mask == NULL)
+               return -1;
+       for (i = 0; i < data_len; i++) {
+               a = data1[i] & mask[i];
+               b = data2[i] & mask[i];
+               if (a != b)
+                       return (int)(a - b);
+       }
+       return 0;
+}
+
+static uint8_t validate_for_filter_policy(struct btd_adapter *adapter,
+                                                               const struct eir_data *eir, gchar *addr)
+{
+       uint8_t allow_report = NONE_REPORT;
+       DBG("");
+
+       if (adapter->scan_filter_support == FALSE)
+               allow_report = SCAN_REPORT;
+       else {
+               if (adapter_le_service_find_addr_scan_filter_data(adapter, addr))
+                       allow_report = SCAN_REPORT;
+               if (eir->name) {
+                       if(adapter_le_service_find_local_name_scan_filter_data(adapter, eir->name))
+                               allow_report = SCAN_REPORT;
+                       }
+               if (eir->sd_list) {
+                       GSList *list = NULL;
+                       for (list = eir->sd_list; list != NULL; list = g_slist_next(list)) {
+                               struct eir_sd *sd = list->data;
+                               if (sd != NULL) {
+                                       static adapter_le_uuid_params_t *uuid_data = NULL;
+                                       static adapter_le_service_data_params_t *service_data = NULL;
+                                       static adapter_le_scan_filter_param_t *scan_param_data = NULL;
+                                       uuid_data = adapter_le_service_find_uuid_scan_filter_data(adapter, (uint8_t *)sd->uuid);
+                                       service_data = adapter_le_service_find_service_scan_filter_data(adapter, sd);
+                                       if (service_data != NULL) {
+                                               if (!adapter_byte_arr_cmp_with_mask((const char *)service_data->service_data,
+                                               (const char *)sd->data, (const char *)service_data->service_data_mask,
+                                               service_data->service_data_len)) {
+                                                       scan_param_data = adapter_le_service_find_scan_filter_params(adapter,
+                                                                               service_data->filter_index);
+                                                       if (scan_param_data && scan_param_data->rssi_high_threshold > eir->tx_power &&
+                                                               scan_param_data->rssi_low_threshold < eir->tx_power)
+                                                               allow_report = SCAN_REPORT;
+                                               }
+                                       }
+                                       if (uuid_data != NULL) {
+                                               if (!adapter_byte_arr_cmp_with_mask((const char *)uuid_data->uuid,
+                                               (const char *)sd->uuid, (const char *)uuid_data->uuid_mask,
+                                               uuid_data->uuid_len)) {
+                                                       scan_param_data = adapter_le_service_find_scan_filter_params(adapter,
+                                                                                               uuid_data->filter_index);
+                                                       if (scan_param_data && scan_param_data->rssi_high_threshold > eir->tx_power &&
+                                                               scan_param_data->rssi_low_threshold < eir->tx_power)
+                                                               allow_report = SCAN_REPORT;
+                                               }
+                                       }
+                                       if (allow_report)
+                                               break;
+                               }
+                       }
+               }
+               if (eir->msd_list) {
+                       GSList *list = NULL;
+                       for (list = eir->msd_list; list != NULL; list = g_slist_next(list)) {
+                               struct eir_msd *msd = list->data;
+                               if (msd != NULL) {
+                                       static adapter_le_manf_data_params_t *manuf_data;
+                                       static adapter_le_scan_filter_param_t *scan_param_data = NULL;
+                                       manuf_data = adapter_le_service_find_manufacturer_scan_filter_data(adapter,
+                                                       msd);
+                                       if (manuf_data != NULL) {
+                                               if (!adapter_byte_arr_cmp_with_mask((const char *)msd->data,
+                                                       (const char *)manuf_data->man_data, (const char *)manuf_data->man_data_mask,
+                                                       manuf_data->man_data_len)) {
+                                                       scan_param_data = adapter_le_service_find_scan_filter_params(adapter,
+                                                                                               manuf_data->filter_index);
+                                                       if (scan_param_data && scan_param_data->rssi_high_threshold > eir->tx_power &&
+                                                               scan_param_data->rssi_low_threshold < eir->tx_power)
+                                                               allow_report = SCAN_REPORT;
+                                               }
+                                               if (msd->company == COMPANY_ID_APPLE)
+                                                       allow_report = IBEACON_REPORT;
+                                       }
+                               }
+                       }
+               }
+       }
+       return allow_report;
+}
+
+gboolean adapter_le_set_platform_scan_filter_params(struct btd_adapter *adapter,
+                                       adapter_le_scan_filter_param_t *params)
+{
+       gboolean ret = TRUE;
+       DBG("adapter_le_scan_filter_param_t [%d]", params->index);
+       adapter_le_scan_filter_action_type action_type = params->action;
+
+       if (action_type == ADD) {
+               ret = adapter_le_service_add_scan_filter_params(adapter, params);
+       } else if (action_type == DELETE) {
+               ret = adapter_le_service_delete_scan_filter_params(adapter, params);
+       } else if (action_type == CLEAR) {
+               ret = adapter_le_service_clear_scan_filter_params(adapter);
+       } else {
+               DBG("filter_action error");
+               ret = FALSE;
+       }
+
+       DBG("Scan Filter VSC :: Action [%x]",
+                                       params->action);
+       return ret;
+}
+
+gboolean adapter_le_set_platform_scan_filter_data(struct btd_adapter *adapter,
+                                               int client_if, int action,
+                                               int filt_type, int filter_index,
+                                               int company_id,
+                                               int company_id_mask,
+                                               int uuid_len, uint8_t *p_uuid,
+                                               int uuid_mask_len, uint8_t *p_uuid_mask,
+                                               gchar *string, int addr_type,
+                                               int data_len, uint8_t *p_data,
+                                               int mask_len, uint8_t *p_mask)
+{
+       gboolean ret = TRUE;
+
+       DBG("");
+
+       switch (filt_type) {
+       case TYPE_DEVICE_ADDRESS: {
+               /* TYPE_DEVICE_ADDRESS */
+               adapter_le_scan_filter_action_type action_type = action;
+
+               if (action_type == ADD) {
+                       ret = adapter_le_service_add_addr_scan_filter_data(adapter,
+                                               filter_index, string, addr_type);
+               } else if (action_type == DELETE) {
+                       ret = adapter_le_service_delete_addr_scan_filter_data(adapter,
+                                               filter_index, string, addr_type);
+               } else if (action_type == CLEAR) {
+                       ret = adapter_le_service_clear_addr_scan_filter_data(adapter);
+               } else {
+                       DBG("filter_action error");
+                       ret = FALSE;
+               }
+
+               break;
+       }
+
+       case TYPE_SERVICE_UUID:
+       case TYPE_SOLICIT_UUID: {
+               adapter_le_scan_filter_action_type action_type = action;
+
+               gboolean is_solicited = (filt_type == TYPE_SOLICIT_UUID) ? TRUE : FALSE;
+
+               if (uuid_len != UUID_16_LEN && uuid_len != UUID_32_LEN
+                       && uuid_len != UUID_128_LEN) {
+                       DBG("UUID length error");
+                       return FALSE;
+               }
+
+               if (uuid_len != uuid_mask_len) {
+                       DBG("Both UUID and UUID_MASK length shoule be samed");
+                       return FALSE;
+               }
+
+               if (action_type == ADD) {
+                       ret = adapter_le_service_add_uuid_scan_filter_data(adapter,
+                                               filter_index, is_solicited, p_uuid,
+                                               p_uuid_mask, uuid_len);
+               } else if (action_type == DELETE) {
+                       ret = adapter_le_service_delete_uuid_scan_filter_data(adapter,
+                                               filter_index, is_solicited, p_uuid,
+                                               p_uuid_mask, uuid_len);
+               } else if (action_type == CLEAR) {
+                       ret = adapter_le_service_clear_uuid_scan_filter_data(adapter);
+               } else {
+                       DBG("filter_action error");
+                       ret = FALSE;
+               }
+
+               break;
+       }
+
+       case TYPE_LOCAL_NAME: {
+               adapter_le_scan_filter_action_type action_type = action;
+
+               if (action_type == ADD) {
+                       ret = adapter_le_service_add_local_name_scan_filter_data(adapter,
+                                               filter_index, (gchar*)string);
+               } else if (action_type == DELETE) {
+                       ret = adapter_le_service_delete_local_name_scan_filter_data(adapter,
+                                               filter_index, (gchar*)string);
+               } else if (action_type == CLEAR) {
+                       ret = adapter_le_service_clear_local_name_scan_filter_data(adapter);
+               } else {
+                       DBG("filter_action error");
+                       ret = FALSE;
+               }
+
+               break;
+       }
+
+       case TYPE_MANUFACTURER_DATA: {
+               adapter_le_scan_filter_action_type action_type = action;
+
+               if (data_len == 0 || (data_len != mask_len)) {
+                       DBG("parameter length error");
+                       return FALSE;
+               }
+
+               if (action_type == ADD) {
+                       ret = adapter_le_service_add_manufacturer_scan_filter_data(adapter,
+                                               filter_index,company_id, company_id_mask, p_data, p_mask, data_len);
+               } else if (action_type == DELETE) {
+                       ret = adapter_le_service_delete_manufacturer_scan_filter_data(adapter,
+                                               filter_index, company_id, company_id_mask, p_data, p_mask, data_len);
+               } else if (action_type == CLEAR) {
+                       ret = adapter_le_service_clear_manufacturer_scan_filter_data(adapter);
+               } else {
+                       DBG("filter_action error");
+                       ret = FALSE;
+               }
+
+               break;
+       }
+
+       case TYPE_SERVICE_DATA: {
+               adapter_le_scan_filter_action_type action_type = action;
+
+               if (data_len == 0 || (data_len != mask_len)) {
+                       DBG("parameter length error");
+                       return FALSE;
+               }
+
+               if (action_type == ADD) {
+                       ret = adapter_le_service_add_service_scan_filter_data(adapter,
+                                               filter_index, p_data, p_mask, data_len);
+               } else if (action_type == DELETE) {
+                       ret = adapter_le_service_delete_service_scan_filter_data(adapter,
+                                               filter_index, p_data, p_mask, data_len);
+               } else if (action_type == CLEAR) {
+                       ret = adapter_le_service_clear_service_scan_filter_data(adapter);
+               } else {
+                       DBG("filter_action error");
+                       ret = FALSE;
+               }
+
+               break;
+       }
+
+       default:
+               DBG("filter_type error");
+               ret = FALSE;
+       }
+
+       return ret;
+}
+#endif
+
+static int set_adv_data_flag(uint8_t *adv_data, uint8_t *data, int data_len, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       adv_data[0] = 2;
+       adv_data[1] = EIR_FLAGS;
+
+       if (adapter->le_static_addr.b[5] != 0)
+               adv_data[2] = EIR_GEN_DISC | EIR_CONTROLLER |
+                               EIR_SIM_HOST | EIR_BREDR_UNSUP;
+       else
+               adv_data[2] = EIR_GEN_DISC | EIR_CONTROLLER | EIR_SIM_HOST;
+
+       memcpy(adv_data + 3, data, data_len);
+       return data_len + 3;
+}
+
+static int set_adv_data_device_name(uint8_t *adv_data, int adv_len, char *name)
+{
+       int ad_type;
+       int ad_len;
+       int i, j;
+       int name_len;
+       uint8_t *data = NULL;
+
+       if (!name)
+               return adv_len;
+
+       data = g_memdup(adv_data, adv_len);
+       if (!data)
+               return adv_len;
+
+       name_len = strlen(name);
+
+       for (i = 0; i <adv_len ; i++) {
+               ad_len = data[i];
+               ad_type = data[i + 1];
+
+               if (ad_type == EIR_NAME_COMPLETE) {
+                       /* Move to last position and update local name */
+                       for (j = i; j < adv_len - 2; j++)
+                               adv_data[j] = data[j + 2];
+
+                       adv_data[j] = name_len + 1;
+                       if (name_len > ADV_DATA_MAX_LENGTH - adv_len) {
+                               adv_data[j] = ADV_DATA_MAX_LENGTH - adv_len + 1;
+                               adv_data[j + 1] = EIR_NAME_SHORT;
+                               memcpy(adv_data + j + 2, name, ADV_DATA_MAX_LENGTH - adv_len);
+                               g_free(data);
+                               return ADV_DATA_MAX_LENGTH;
+                       } else {
+                               adv_data[j + 1] = EIR_NAME_COMPLETE;
+                               memcpy(adv_data + j + 2, name, name_len);
+                               g_free(data);
+                               return adv_len + name_len;
+                       }
+
+               } else {
+                       memcpy(adv_data + i, &data[i], ad_len + 1);
+                       i = i + data[i];
+               }
+       }
+
+       g_free(data);
+       return adv_len;
+}
+
+static int set_adv_data_tx_power(uint8_t *adv_data, int adv_len, int8_t tx_power)
+{
+       int ad_type;
+       int ad_len;
+       int i, j;
+       uint8_t *data = NULL;
+
+       data = g_memdup(adv_data, adv_len);
+       if (!data)
+               return adv_len;
+
+       for (i = 0; i <adv_len ; i++) {
+               ad_len = data[i];
+               ad_type = data[i + 1];
+
+               if (ad_type == EIR_TX_POWER) {
+                       adv_data[i] = 2;
+                       adv_data[i + 1] = EIR_TX_POWER;
+                       adv_data[i + 2] = tx_power;
+
+                       for(j = i + 2; j < adv_len; j++)
+                               adv_data[j + 1] = data[j];
+
+                       g_free(data);
+                       return adv_len + 1;
+               } else {
+                       memcpy(adv_data + i, &data[i], ad_len + 1);
+                       i = i + data[i];
+               }
+       }
+
+       g_free(data);
+       return adv_len;
+}
+
+
+static int adapter_le_set_missed_adv_data(uint8_t *p_data, uint8_t data_len,
+               gboolean is_scan_rsp, char *adapter_name, int8_t tx_power, uint8_t **adv_data, int *adv_len,
+               void *user_data)
+{
+       uint8_t *data;
+       int len;
+
+       data = g_malloc0(ADV_DATA_MAX_LENGTH);
+       memcpy(data, p_data, data_len);
+       len = data_len;
+
+       /* In case multi advertising, need to update the below AD type
+               since it handled into kernel */
+       if (!is_scan_rsp) {
+               len = set_adv_data_flag(data, p_data, data_len, user_data);
+       }
+
+       len = set_adv_data_tx_power(data, len, tx_power);
+
+       len = set_adv_data_device_name(data, len, adapter_name);
+
+       *adv_data = data;
+       *adv_len = len;
+       return 0;
+}
+
+static DBusMessage *adapter_start_custom_discovery(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       const char *sender = dbus_message_get_sender(msg);
+       struct watch_client *client;
+       GSList *list;
+       const gchar *disc_type;
+
+       DBG("sender %s", sender);
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &disc_type,
+                                                       DBUS_TYPE_INVALID)) {
+               return btd_error_invalid_args(msg);
+       }
+
+       DBG("discovery type = %s", disc_type);
+
+       /*Valid strings: "BREDR", "LE", "LE_BREDR" */
+       if (g_strcmp0(disc_type, "BREDR") == 0)
+               adapter->disc_type = BT_DISC_TYPE_BREDR_ONLY;
+       else if (g_strcmp0(disc_type, "LE") == 0)
+               adapter->disc_type =  BT_DISC_TYPE_LE_ONLY;
+       else if (g_strcmp0(disc_type, "LE_BREDR") == 0)
+               adapter->disc_type =  BT_DISC_TYPE_LE_BREDR;
+       else
+               return btd_error_invalid_args(msg);
+
+       /*
+        * Every client can only start one discovery, if the client
+        * already started a discovery then return an error.
+        */
+       list = g_slist_find_custom(adapter->discovery_list, sender,
+                                               compare_sender);
+       if (list)
+               return btd_error_busy(msg);
+
+       client = g_new0(struct watch_client, 1);
+
+       client->adapter = adapter;
+       client->owner = g_strdup(sender);
+       client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
+                                               discovery_disconnect, client,
+                                               discovery_destroy);
+
+       adapter->discovery_list = g_slist_prepend(adapter->discovery_list,
+                                                               client);
+
+       /*
+        * Just trigger the discovery here. In case an already running
+        * discovery in idle phase exists, it will be restarted right
+        * away.
+        */
+       trigger_start_discovery(adapter, 0);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_start_le_discovery(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       const char *sender = dbus_message_get_sender(msg);
+       struct watch_client *client;
+       GSList *list;
+
+       DBG("sender %s", sender);
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       /*
+        * Every client can only start one discovery, if the client
+        * already started a discovery then return an error.
+        */
+
+       adapter->disc_type = BT_DISC_TYPE_LE_ONLY;
+       DBG("adapter->disc_type[%d]", adapter->disc_type);
+       DBG("adapter->discovery_type [%d]", adapter->discovery_type);
+
+       list = g_slist_find_custom(adapter->le_discovery_list, sender,
+                                               compare_sender);
+       if (list)
+               return btd_error_busy(msg);
+
+       client = g_new0(struct watch_client, 1);
+
+       client->adapter = adapter;
+       client->owner = g_strdup(sender);
+       client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
+                                               le_discovery_disconnect, client,
+                                               le_discovery_destroy);
+
+       adapter->le_discovery_list = g_slist_prepend(adapter->le_discovery_list,
+                                                               client);
+
+       /*
+        * Just trigger the discovery here. In case an already running
+        * discovery in idle phase exists, it will be restarted right
+        * away.
+        */
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+       trigger_start_discovery(adapter, 0);
+#else
+       trigger_start_le_discovery(adapter, 0);
+#endif
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_stop_le_discovery(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       const char *sender = dbus_message_get_sender(msg);
+       struct mgmt_cp_stop_le_discovery cp;
+       struct watch_client *client;
+       GSList *list;
+
+       DBG("sender %s", sender);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (adapter->le_discovery_idle_timeout > 0) {
+               DBG("Remove LE scan trigger");
+               g_source_remove(adapter->le_discovery_idle_timeout);
+               adapter->le_discovery_idle_timeout = 0;
+       }
+#endif
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       list = g_slist_find_custom(adapter->le_discovery_list, sender,
+                                               compare_sender);
+       if (!list)
+               return btd_error_failed(msg, "No discovery started");
+
+       client = list->data;
+
+       adapter->disc_type = BT_DISC_TYPE_LE_ONLY;
+       DBG("adapter->disc_type[%d]", adapter->disc_type);
+       DBG("adapter->discovery_type [%d]", adapter->discovery_type);
+
+       cp.type = adapter->discovery_type;
+       DBG("cp.type %d", cp.type);
+
+       /*
+        * The destroy function will cleanup the client information and
+        * also remove it from the list of discovery clients.
+        */
+       g_dbus_remove_watch(dbus_conn, client->watch);
+
+       /*
+        * As long as other discovery clients are still active, just
+        * return success.
+        */
+       DBG("cp.type %d", cp.type);
+       DBG("adapter->le_discovery_list %d", adapter->discovery_type);
+       if (adapter->le_discovery_list)
+               return dbus_message_new_method_return(msg);
+
+       /*
+        * In the idle phase of a discovery, there is no need to stop it
+        * and so it is enough to send out the signal and just return.
+        */
+       DBG("cp.type %d", cp.type);
+       DBG("adapter->discovery_enable %d", adapter->discovery_enable);
+       if (adapter->discovery_enable == 0x00) {
+               adapter->le_discovering = false;
+               g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                                       ADAPTER_INTERFACE, "LEDiscovering");
+
+               trigger_passive_scanning(adapter);
+
+               return dbus_message_new_method_return(msg);
+       }
+       DBG("adapter->discovery_type %d", adapter->discovery_type);
+       cp.type = 0x06;
+       DBG("cp.type %d", cp.type);
+       mgmt_send(adapter->mgmt, MGMT_OP_STOP_LE_DISCOVERY,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               stop_le_discovery_complete, adapter, NULL);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_set_advertising(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       dbus_bool_t err;
+       dbus_bool_t enable = FALSE;
+       dbus_int32_t slot_id;
+
+       DBG("adapter_set_advertising");
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &enable,
+                                               DBUS_TYPE_INT32, &slot_id,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       if (adapter_le_is_supported_multi_advertising() && slot_id > 0)
+               err = adapter_le_enable_multi_adv(adapter, enable, slot_id);
+       else
+               err = set_mode(adapter, MGMT_OP_SET_ADVERTISING, enable);
+
+       if (!err)
+               return btd_error_failed(msg, "Set Advertising failed");
+
+       if (enable)
+               create_advertiser(adapter, slot_id);
+
+       if (err && slot_id > 0)
+               advertising_state_changed(adapter, slot_id, enable);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_set_advertising_params(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct mgmt_cp_set_advertising_params cp;
+       dbus_uint32_t interval_min;
+       dbus_uint32_t interval_max;
+       dbus_uint32_t filter_policy;
+       dbus_uint32_t type;
+       dbus_int32_t slot_id;
+       gboolean ret;
+
+       DBG("Set customised advertising parameters");
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_UINT32, &interval_min,
+                               DBUS_TYPE_UINT32, &interval_max,
+                               DBUS_TYPE_UINT32, &filter_policy,
+                               DBUS_TYPE_UINT32, &type,
+                               DBUS_TYPE_INT32, &slot_id,
+                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       memset(&cp, 0, sizeof(cp));
+
+       DBG("advertising interval min %x, max %x, filter %x type %x",
+                               interval_min, interval_max, filter_policy, type);
+
+       if (filter_policy > 0x03)
+               return btd_error_invalid_args(msg);
+
+       if (type > 0x04)
+               return btd_error_invalid_args(msg);
+
+       if (adapter_le_is_supported_multi_advertising() && slot_id > 0) {
+               adapter_le_adv_inst_info_t *p_inst;
+               adapter_le_adv_param_t *p_params;
+
+               p_inst = malloc(sizeof(adapter_le_adv_inst_info_t));
+               p_params = malloc(sizeof(adapter_le_adv_param_t));
+               memset(p_inst, 0, sizeof(adapter_le_adv_inst_info_t));
+               memset(p_params, 0, sizeof(adapter_le_adv_param_t));
+               p_inst->inst_id = slot_id;
+               p_params->adv_int_min = interval_min;
+               p_params->adv_int_max = interval_max;
+               p_params->adv_type = type;
+               p_params->channel_map = 0x07;   /* fixed channel :: will be used all */
+               p_params->adv_filter_policy = filter_policy;
+               p_params->tx_power = BLE_ADV_TX_POWER_MID;      /* TODO:need to optimize */
+               if (adapter->le_static_addr.b[5] != 0) {
+                       p_inst->bdaddr_type = 0x01;
+                       bacpy(&p_inst->bdaddr, &adapter->le_static_addr);
+               } else {
+                       p_inst->bdaddr_type = 0x00;
+                       bacpy(&p_inst->bdaddr, &adapter->bdaddr);
+               }
+
+               ret = adapter_le_set_multi_adv_params(p_inst, p_params);
+
+               free(p_inst);
+               free(p_params);
+
+               if (ret)
+                       return dbus_message_new_method_return(msg);
+               else
+                       return btd_error_failed(msg, "set advertising param failed");
+       } else {
+               cp.interval_max = interval_max;
+               cp.interval_min = interval_min;
+               cp.filter_policy = filter_policy;
+               cp.type = type;
+
+               if (mgmt_send(adapter->mgmt, MGMT_OP_SET_ADVERTISING_PARAMS,
+                                       adapter->dev_id, sizeof(cp), &cp,
+                                       NULL, NULL, NULL) > 0)
+                       return dbus_message_new_method_return(msg);
+
+               return btd_error_failed(msg, "set advertising param failed");
+       }
+}
+
+static DBusMessage *adapter_set_advertising_data(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct mgmt_cp_set_advertising_data cp;
+       uint8_t *value;
+       int32_t len = 0;
+       dbus_int32_t slot_id;
+       uint8_t *adv_data = NULL;
+       int adv_len = 0;
+       char *adapter_name = adapter->name;
+       char le_name[MAX_NAME_LENGTH + 1] = { 0 };
+
+       DBG("Set advertising data");
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       if (!dbus_message_get_args(msg, NULL,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &value, &len,
+                       DBUS_TYPE_INT32, &slot_id,
+                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       if (len > ADV_DATA_MAX_LENGTH - 3)
+               return btd_error_invalid_args(msg);
+
+       if (adapter->le_static_addr.b[5] != 0) {
+               char *ptr = NULL;
+
+               g_strlcpy(le_name, adapter_name,
+                               sizeof(le_name) - LE_BEARER_POSTFIX_LEN);
+               if (!g_utf8_validate(le_name, -1, (const char **)&ptr))
+                       *ptr = '\0';
+
+               g_strlcat(le_name, LE_BEARER_POSTFIX, sizeof(le_name));
+               adapter_name = le_name;
+       }
+
+       adapter_le_set_missed_adv_data(value, len, FALSE,
+                       adapter_name, adapter->adv_tx_power, &adv_data, &adv_len, adapter);
+
+       if (adapter_le_is_supported_multi_advertising() && slot_id > 0) {
+               if (adapter_le_set_multi_adv_data(slot_id, FALSE, adv_len, adv_data)) {
+                       g_free(adv_data);
+                       return dbus_message_new_method_return(msg);
+               } else {
+                       g_free(adv_data);
+                       return btd_error_failed(msg, "set advertising data failed");
+               }
+       } else {
+               memcpy(&cp, adv_data, adv_len);
+
+               if (mgmt_send(adapter->mgmt, MGMT_OP_SET_ADVERTISING_DATA,
+                                               adapter->dev_id, adv_len,
+                                               &cp, NULL, NULL, NULL) > 0) {
+                       g_free(adv_data);
+                       return dbus_message_new_method_return(msg);
+               }
+
+               g_free(adv_data);
+               return btd_error_failed(msg, "set advertising data failed");
+       }
+}
+
+static DBusMessage *adapter_le_scan_filter_param_setup(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       dbus_bool_t ctlr_filter_support = TRUE;
+#endif
+       dbus_int32_t client_if, action, filt_index;
+       dbus_int32_t feat_seln, list_logic_type, filt_logic_type;
+       dbus_int32_t rssi_high_thres, rssi_low_thres, dely_mode;
+       dbus_int32_t found_timeout, lost_timeout, found_timeout_cnt;
+       adapter_le_scan_filter_param_t params;
+       gboolean err;
+
+       DBG("adapter_le_scan_filter_param_setup");
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       if (adapter_le_get_scan_filter_size() == 0)
+#ifndef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+               return btd_error_not_supported(msg);
+#else
+               ctlr_filter_support = FALSE;
+#endif
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &client_if,
+                                               DBUS_TYPE_INT32, &action,
+                                               DBUS_TYPE_INT32, &filt_index,
+                                               DBUS_TYPE_INT32, &feat_seln,
+                                               DBUS_TYPE_INT32, &list_logic_type,
+                                               DBUS_TYPE_INT32, &filt_logic_type,
+                                               DBUS_TYPE_INT32, &rssi_high_thres,
+                                               DBUS_TYPE_INT32, &rssi_low_thres,
+                                               DBUS_TYPE_INT32, &dely_mode,
+                                               DBUS_TYPE_INT32, &found_timeout,
+                                               DBUS_TYPE_INT32, &lost_timeout,
+                                               DBUS_TYPE_INT32, &found_timeout_cnt,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       memset(&params, 0, sizeof(params));
+
+       params.action = action;
+       params.index = filt_index;
+       params.feature = feat_seln;
+       params.filter_logic_type = filt_logic_type;
+       params.list_logic_type = list_logic_type;
+       params.delivery_mode = dely_mode;
+       params.rssi_high_threshold = rssi_high_thres;
+
+       if (params.delivery_mode == ON_FOUND) {
+               params.rssi_low_threshold = rssi_low_thres;
+               params.onfound_timeout = found_timeout;
+               params.onfound_timeout_cnt = found_timeout_cnt;
+               params.onlost_timeout = lost_timeout;
+       }
+
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       if (ctlr_filter_support)
+#endif
+       err = adapter_le_set_scan_filter_params(&params);
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       else
+               err = adapter_le_set_platform_scan_filter_params(adapter, &params);
+#endif
+
+       if (!err)
+               return btd_error_failed(msg, "Failed to scan filter param setup");
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_le_scan_filter_add_remove(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct btd_device *dev = NULL;
+       dbus_int32_t client_if, action, filt_type, filt_index;
+       dbus_int32_t company_id, company_id_mask;
+       gchar *address = NULL;
+       dbus_uint32_t address_type = 0;
+       uint8_t addr_type;
+       GSList *list;
+       char ida_string[18];
+       uint8_t *p_uuid, *p_uuid_mask, *p_data, *p_mask;
+       int32_t uuid_len = 0, uuid_mask_len = 0, data_len = 0, mask_len = 0;
+       gboolean err;
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       dbus_bool_t ctlr_filter_support = TRUE;
+#endif
+
+       DBG("adapter_le_scan_filter_add_remove");
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       /* if controller does not support vendor specific scan filtering feature
+        * then add the filter into platform supported scan filters.
+        */
+       if (adapter_le_get_scan_filter_size() == 0) {
+#ifndef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+               return btd_error_not_supported(msg);
+#else
+               ctlr_filter_support = FALSE;
+#endif
+       }
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &client_if,
+                                               DBUS_TYPE_INT32, &action,
+                                               DBUS_TYPE_INT32, &filt_type,
+                                               DBUS_TYPE_INT32, &filt_index,
+                                               DBUS_TYPE_INT32, &company_id,
+                                               DBUS_TYPE_INT32, &company_id_mask,
+                                               DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &p_uuid, &uuid_len,
+                                               DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &p_uuid_mask, &uuid_mask_len,
+                                               DBUS_TYPE_STRING, &address,
+                                               DBUS_TYPE_UINT32, &address_type,
+                                               DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &p_data, &data_len,
+                                               DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &p_mask, &mask_len,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       list = g_slist_find_custom(adapter->devices, address, device_rpa_cmp);
+       if (!list)
+               list = g_slist_find_custom(adapter->devices, address,
+                                                       device_address_cmp);
+       if (list)
+               dev = list->data;
+       if (dev && device_get_rpa_exist(dev) == true) {
+               ba2str(device_get_address(dev), ida_string);
+               if (btd_device_get_bdaddr_type(dev) == BDADDR_LE_PUBLIC)
+                       addr_type = 0x00;
+               else
+                       addr_type = 0x01;
+       } else {
+               memcpy(ida_string, address, sizeof(ida_string));
+               addr_type = 0x00;
+       }
+
+       DBG("addr %s, type %d", ida_string, addr_type);
+
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       if (ctlr_filter_support)
+#endif
+       err = adapter_le_set_scan_filter_data(client_if, action, filt_type,
+                       filt_index, company_id, company_id_mask,
+                       uuid_len, p_uuid, uuid_mask_len, p_uuid_mask,
+                       ida_string, addr_type, data_len, p_data, mask_len, p_mask);
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       else
+               err = adapter_le_set_platform_scan_filter_data(adapter, client_if, action, filt_type,
+                               filt_index, company_id, company_id_mask,
+                               uuid_len, p_uuid, uuid_mask_len, p_uuid_mask,
+                               ida_string, addr_type, data_len, p_data, mask_len, p_mask);
+#endif
+       if (!err)
+               return btd_error_failed(msg, "Failed to add/remove filter");
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_le_scan_filter_clear(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       dbus_int32_t client_if = 0;
+       dbus_int32_t filt_index = 0;
+       gboolean err;
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       dbus_bool_t ctlr_filter_support = TRUE;
+#endif
+
+       DBG("adapter_le_scan_filter_clear");
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       if (adapter_le_get_scan_filter_size() == 0)
+#ifndef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+               return btd_error_not_supported(msg);
+#else
+               ctlr_filter_support = FALSE;
+#endif
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &client_if,
+                                               DBUS_TYPE_INT32, &filt_index,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       if (ctlr_filter_support)
+#endif
+       err = adapter_le_clear_scan_filter_data(client_if, filt_index);
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       else
+               err = adapter_le_clear_platform_scan_filter_data(adapter, filt_index);
+#endif
+
+       if (!err)
+               return btd_error_failed(msg, "Failed to clear filter");
+
+       return dbus_message_new_method_return(msg);
+}
+
+
+static DBusMessage *adapter_le_scan_filter_enable(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       dbus_bool_t enable = FALSE;
+       dbus_int32_t client_if = 0;
+       gboolean err;
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       dbus_bool_t ctlr_filter_support = TRUE;
+#endif
+
+       DBG("adapter_le_scan_filter_enable");
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       /* if controller does not support vendor specific scan filtering feature
+        * then enable platform supported scan filtering functionalites.
+        */
+#endif
+       if (adapter_le_get_scan_filter_size() == 0)
+#ifndef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+               return btd_error_not_supported(msg);
+#else
+               ctlr_filter_support = FALSE;
+#endif
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &client_if,
+                                               DBUS_TYPE_BOOLEAN, &enable,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       if (ctlr_filter_support)
+#endif
+       err = adapter_le_enable_scan_filtering(enable);
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       else
+               err = adapter_le_enable_platform_scan_filtering(adapter, enable);
+#endif
+
+       if (!err)
+               return btd_error_failed(msg, "Failed to enable scan filtering");
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_le_set_scan_params(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct mgmt_cp_le_set_scan_params cp;
+       uint32_t type;
+       uint32_t interval;
+       uint32_t window;
+
+       DBG("Set scan parameters");
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_UINT32, &type,
+                               DBUS_TYPE_UINT32, &interval,
+                               DBUS_TYPE_UINT32, &window,
+                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       DBG("scan type %x, interval %x, window %x",
+                               type, interval, window);
+       memset(&cp, 0, sizeof(cp));
+
+       cp.type = type;
+       cp.interval = interval;
+       cp.window = window;
+       adapter->scan_type = type;
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_LE_SET_SCAN_PARAMS,
+                                       adapter->dev_id, sizeof(cp), &cp,
+                                       NULL, NULL, NULL) > 0)
+               return dbus_message_new_method_return(msg);
+
+       return btd_error_failed(msg, "set scan parameters failed");
+}
+
+static DBusMessage *adapter_set_scan_rsp_data(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct mgmt_cp_set_scan_rsp_data cp;
+       uint8_t *value;
+       int32_t len = 0;
+       dbus_int32_t slot_id;
+       uint8_t *adv_data = NULL;
+       int adv_len = 0;
+
+       char *adapter_name = adapter->name;
+       char le_name[MAX_NAME_LENGTH + 1] = { 0 };
+
+       DBG("Set scan response data");
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       if (!dbus_message_get_args(msg, NULL,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &value, &len,
+                       DBUS_TYPE_INT32, &slot_id,
+                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       if (len > SCAN_RESPONSE_DATA_LENGTH_MAX)
+               return btd_error_invalid_args(msg);
+
+       if (adapter->le_static_addr.b[5] != 0) {
+               char *ptr = NULL;
+
+               g_strlcpy(le_name, adapter_name,
+                               sizeof(le_name) - LE_BEARER_POSTFIX_LEN);
+               if (!g_utf8_validate(le_name, -1, (const char **)&ptr))
+                       *ptr = '\0';
+
+               g_strlcat(le_name, LE_BEARER_POSTFIX, sizeof(le_name));
+               adapter_name = le_name;
+       }
+
+       adapter_le_set_missed_adv_data(value, len, TRUE,
+                       adapter_name, adapter->adv_tx_power, &adv_data, &adv_len, adapter);
+
+       if (adapter_le_is_supported_multi_advertising() && slot_id > 0) {
+               if (adapter_le_set_multi_adv_data(slot_id, TRUE, adv_len, (uint8_t *)adv_data)) {
+                       g_free(adv_data);
+                       return dbus_message_new_method_return(msg);
+               } else {
+                       g_free(adv_data);
+                       return btd_error_failed(msg, "set advertising data failed");
+               }
+       } else {
+               memcpy(&cp, adv_data, adv_len);
+
+               if (mgmt_send(adapter->mgmt, MGMT_OP_SET_SCAN_RSP_DATA,
+                                               adapter->dev_id, adv_len, &cp,
+                                               NULL, NULL, NULL) > 0) {
+                       g_free(adv_data);
+                       return dbus_message_new_method_return(msg);
+               }
+
+               g_free(adv_data);
+               return btd_error_failed(msg, "set scan reponse data failed");
+       }
+}
+
+static DBusMessage *adapter_add_device_white_list(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct mgmt_cp_add_dev_white_list cp;
+       const gchar *address;
+       bdaddr_t bdaddr;
+       dbus_uint32_t address_type;
+       struct btd_device *dev;
+
+       DBG("Add device whie list");
+       if (dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_STRING, &address,
+                                       DBUS_TYPE_UINT32, &address_type,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return btd_error_invalid_args(msg);
+
+       if (bachk(address) < 0)
+               return btd_error_invalid_args(msg);
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       DBG("addr %s, type %d", address, address_type);
+       str2ba(address, &bdaddr);
+
+       dev = btd_adapter_find_device(adapter, &bdaddr,
+                       address_type ? BDADDR_LE_RANDOM : BDADDR_LE_PUBLIC);
+       if (dev && device_get_rpa_exist(dev) == true) {
+               if (adapter_le_is_supported_offloading() == FALSE) {
+                       error("Spec based command is not supported yet");
+                       return btd_error_not_supported(msg);
+               }
+
+               /* Add IRK value to list */
+               if (adapter_le_add_irk_to_list(device_get_irk_value(dev),
+                                       device_get_address(dev),
+                                       btd_device_get_bdaddr_type(dev))) {
+                       return dbus_message_new_method_return(msg);
+               } else {
+                       return btd_error_failed(msg, "Add LE IRK to list failed");
+               }
+       }
+
+       memset(&cp, 0, sizeof(cp));
+
+       cp.bdaddr_type = address_type;
+       memcpy(&cp.bdaddr, &bdaddr, sizeof(bdaddr_t));
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_ADD_DEV_WHITE_LIST,
+                                       adapter->dev_id, sizeof(cp), &cp,
+                                       NULL, NULL, NULL) > 0)
+               return dbus_message_new_method_return(msg);
+
+       return btd_error_failed(msg, "add device white list failed");
+}
+
+static DBusMessage *adapter_remove_device_white_list(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct mgmt_cp_remove_dev_white_list cp;
+       const gchar *address;
+       bdaddr_t bdaddr;
+       dbus_uint32_t address_type;
+       struct btd_device *dev;
+
+       DBG("Remove device whie list");
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       if (dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_STRING, &address,
+                                       DBUS_TYPE_UINT32, &address_type,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return btd_error_invalid_args(msg);
+
+       if (bachk(address) < 0)
+               return btd_error_invalid_args(msg);
+
+       DBG("addr %s, type %d", address, address_type);
+       str2ba(address, &bdaddr);
+
+       dev = btd_adapter_find_device(adapter, &bdaddr,
+                       address_type ? BDADDR_LE_RANDOM : BDADDR_LE_PUBLIC);
+       if (dev && device_get_rpa_exist(dev) == true) {
+               if (adapter_le_is_supported_offloading() == FALSE) {
+                       error("Spec based command is not supported yet");
+                       return btd_error_not_supported(msg);
+               }
+
+               /* Remove IRK value to list */
+               if (adapter_le_remove_irk_to_list(device_get_address(dev),
+                                       btd_device_get_bdaddr_type(dev))) {
+                       return dbus_message_new_method_return(msg);
+               } else {
+                       return btd_error_failed(msg, "Remove IRK is failed");
+               }
+       }
+
+       memset(&cp, 0, sizeof(cp));
+
+       cp.bdaddr_type = address_type;
+       memcpy(&cp.bdaddr, &bdaddr, sizeof(bdaddr_t));
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST,
+                                       adapter->dev_id, sizeof(cp), &cp,
+                                       NULL, NULL, NULL) > 0)
+               return dbus_message_new_method_return(msg);
+
+       return btd_error_failed(msg, "remove device white list failed");
+}
+
+static DBusMessage *adapter_clear_device_white_list(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+
+       DBG("Clear device whie list");
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_CLEAR_DEV_WHITE_LIST,
+                                       adapter->dev_id, 0, NULL,
+                                       NULL, NULL, NULL) > 0)
+               return dbus_message_new_method_return(msg);
+
+       return btd_error_failed(msg, "clear white list failed");
+}
+
+static DBusMessage *adapter_set_le_privacy(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       dbus_bool_t err;
+       dbus_bool_t enable_privacy = FALSE;
+
+       if (!(adapter->supported_settings & MGMT_SETTING_PRIVACY))
+               return btd_error_not_supported(msg);
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN,
+                               &enable_privacy, DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       if (enable_privacy) {
+               if (adapter->current_settings & MGMT_SETTING_PRIVACY)
+                       return btd_error_already_exists(msg);
+       } else {
+               if (!(adapter->current_settings & MGMT_SETTING_PRIVACY))
+                       return btd_error_already_exists(msg);
+       }
+
+       err = set_privacy(adapter, enable_privacy);
+
+       if (!err)
+               return btd_error_failed(msg, "Set Le Privacy failed");
+
+       return dbus_message_new_method_return(msg);
+}
+
+static void set_le_static_address(struct btd_adapter *adapter)
+{
+       int fd;
+       int ret;
+       char address[18];
+       char dirname[PATH_MAX];
+
+       snprintf(dirname, PATH_MAX, STORAGEDIR "/%s", "le_static_addr");
+       if (access(dirname, F_OK) < 0) {
+               int i;
+               bdaddr_t le_static_addr;
+
+               le_static_addr.b[5] = adapter->bdaddr.b[5] | 0xc0;
+               for (i = 0; i < 5; i++) {
+                       le_static_addr.b[i] =
+                               (adapter->bdaddr.b[i] & 0x7f) << 1 |
+                               (adapter->bdaddr.b[i] & 0x80) >> 7;
+               }
+
+               /*
+                * < How to get Public address from above static address >
+                *
+                * for (i = 0; i < 5; i++) {
+                *      bredr_addr.b[i] =
+                *              (adapter->le_static_addr.b[i] & 0xfe) >> 1 |
+                *              (adapter->le_static_addr.b[i] & 0x01) << 7;
+                * }
+                * bredr_addr.b[5] = {the value from advertising data}
+                */
+
+               fd = open(dirname, O_WRONLY | O_CREAT, 0644);
+               if (fd >= 0) {
+                       ba2str(&le_static_addr, address);
+                       DBG("LE static random : %s", address);
+                       ret = write(fd, address, strlen(address));
+                       if (ret < 0) {
+                               error("Cannot save LE address : %s",
+                                               strerror(errno));
+                       }
+
+                       ret = fdatasync(fd);
+                       if (ret < 0)
+                               error("sync failed : %s", strerror(errno));
+
+                       close(fd);
+               } else {
+                       error("Cannot save LE address");
+               }
+               bacpy(&adapter->le_static_addr, &le_static_addr);
+       } else {
+               fd = open(dirname, O_RDONLY);
+               if (fd >= 0) {
+                       ret = read(fd, address, sizeof(address));
+                       if (ret >= 17) {
+                               /* xx:xx:xx:xx:xx:xx */
+                               address[17] = '\0';
+                               DBG("LE static random : %s", address);
+                               str2ba(address, &adapter->le_static_addr);
+                               adapter->le_static_addr.b[5] |= 0xc0;
+                       } else
+                               error("Invalid LE address");
+                       close(fd);
+               } else {
+                       error("Cannot get LE address");
+               }
+       }
+
+       return;
+}
+
+static void set_le_static_address_complete(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       DBG("index %u status 0x%02x", adapter->dev_id, status);
+
+       if (status != MGMT_STATUS_SUCCESS) {
+               error("Failed to set static address for index %u: %s (0x%02x)",
+                               adapter->dev_id, mgmt_errstr(status), status);
+               if (adapter->le_static_addr.b[5] != 0)
+                       bacpy(&adapter->le_static_addr, BDADDR_ANY);
+               else
+                       set_le_static_address(adapter);
+               return;
+       }
+
+       return;
+}
+
+static DBusMessage *adapter_set_le_static_address(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       dbus_bool_t is_enable = FALSE;
+       struct mgmt_cp_set_static_address cp;
+
+       if (!(adapter->supported_settings & MGMT_OP_SET_STATIC_ADDRESS)) {
+               error("LE static address is not supported");
+               return btd_error_not_supported(msg);
+       }
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &is_enable,
+                               DBUS_TYPE_INVALID)) {
+               error("Invalid arguments");
+               return btd_error_invalid_args(msg);
+       }
+
+       memset(&cp, 0x00, sizeof(cp));
+       if (is_enable)  {
+               if (adapter->le_static_addr.b[5] != 0) {
+                       DBG("LE static address is already configured");
+                       return dbus_message_new_method_return(msg);
+               }
+               set_le_static_address(adapter);
+               bacpy(&cp.bdaddr, &adapter->le_static_addr);
+       } else {
+               if (adapter->le_static_addr.b[5] == 0) {
+                       DBG("LE static address is not configured");
+                       return dbus_message_new_method_return(msg);
+               }
+               bacpy(&adapter->le_static_addr, BDADDR_ANY);
+       }
+       DBG("Set static random address : %d", is_enable);
+
+       if (mgmt_send(mgmt_master, MGMT_OP_SET_STATIC_ADDRESS, adapter->dev_id,
+                       sizeof(cp), &cp,
+                       set_le_static_address_complete, adapter, NULL) <= 0) {
+               error("Failed to set static address : %d", is_enable);
+               if (is_enable)
+                       bacpy(&adapter->le_static_addr, BDADDR_ANY);
+               else
+                       set_le_static_address(adapter);
+               return btd_error_failed(msg, "Unable to set static address");
+       }
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_enable_rssi(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct mgmt_cp_set_enable_rssi cp;
+       struct mgmt_cp_disable_rssi cp_dis;
+       bdaddr_t bt_addr = { { 0, } };
+       const gchar *address = NULL;
+
+       const char *sender = dbus_message_get_sender(msg);
+       dbus_int32_t link_type;
+       dbus_int32_t low_threshold;
+       dbus_int32_t in_range_threshold;
+       dbus_int32_t high_threshold;
+
+       DBG("Enable RSSI called");
+       DBG("sender %s", sender);
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_STRING, &address,
+                               DBUS_TYPE_INT32, &link_type,
+                               DBUS_TYPE_INT32, &low_threshold,
+                               DBUS_TYPE_INT32, &in_range_threshold,
+                               DBUS_TYPE_INT32, &high_threshold,
+                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       DBG("Enable RSSI: [%s %d %d %d %d]", address, link_type,
+                       low_threshold, in_range_threshold, high_threshold);
+
+       DBG("BT address [%s]", address);
+       memset(&bt_addr, 0, sizeof(bdaddr_t));
+       str2ba(address, &bt_addr);
+       memset(&cp, 0, sizeof(struct mgmt_cp_set_enable_rssi));
+       memset(&cp_dis, 0, sizeof(struct mgmt_cp_disable_rssi));
+
+       if (bachk(address) < 0)
+               return btd_error_invalid_args(msg);
+
+//     if (!btd_adapter_find_device(adapter, address))
+//             return btd_error_not_found(msg);
+
+       if (low_threshold == 0 && in_range_threshold == 0 && high_threshold == 0) {
+               cp_dis.bdaddr = bt_addr;
+               cp_dis.link_type = link_type;
+               DBG("Disable Request");
+               if (mgmt_send(adapter->mgmt, MGMT_OP_SET_RSSI_DISABLE,
+                                       adapter->dev_id, sizeof(cp_dis), &cp_dis,
+                                       NULL, NULL, NULL) > 0)
+                                       return dbus_message_new_method_return(msg);
+       } else {
+               cp.low_th = low_threshold;
+               cp.in_range_th = in_range_threshold;
+               cp.high_th = high_threshold;
+               cp.bdaddr = bt_addr;
+               cp.link_type = link_type;
+               DBG("Enable Request");
+               if (mgmt_send(adapter->mgmt, MGMT_OP_SET_RSSI_ENABLE,
+                                       adapter->dev_id, sizeof(cp), &cp,
+                                       NULL, NULL, NULL) > 0)
+                       return dbus_message_new_method_return(msg);
+       }
+       return btd_error_failed(msg, "Enable/Disable RSSI Failed");
+}
+
+static DBusMessage *adapter_get_rssi(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct mgmt_cp_get_raw_rssi cp;
+       bdaddr_t bt_addr;
+       const gchar *address = NULL;
+       dbus_int32_t link_type;
+       const char *sender = dbus_message_get_sender(msg);
+
+       DBG("Get RSSI called");
+       DBG("sender %s", sender);
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_STRING, &address,
+                               DBUS_TYPE_INT32, &link_type,
+                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       DBG("BT address [%s] link type [%d]", address, link_type);
+       memset(&bt_addr, 0, sizeof(bdaddr_t));
+       str2ba(address, &bt_addr);
+       memset(&cp, 0, sizeof(struct mgmt_cp_get_raw_rssi));
+
+       if (bachk(address) < 0)
+               return btd_error_invalid_args(msg);
+
+//     if (!btd_adapter_find_device(adapter, address))
+//             return btd_error_not_found(msg);
+
+       memcpy(&(cp.bt_address), &bt_addr, sizeof(bdaddr_t));
+       cp.link_type = link_type;
+       DBG("RAW RSSI Request");
+       if (mgmt_send(adapter->mgmt, MGMT_OP_GET_RAW_RSSI,
+                                       adapter->dev_id, sizeof(cp), &cp,
+                                       NULL, NULL, NULL) > 0)
+                       return dbus_message_new_method_return(msg);
+
+       return btd_error_failed(msg, "Get Raw RSSI Failed");
+}
+
+#if !defined(__SPRD_PATCH__)
+static void get_adv_tx_power_complete(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       const struct mgmt_rp_get_adv_tx_power *rp = param;
+
+       if (!rp) {
+               error("Error ocurred in Getting adv tx power, rp is NULL");
+               return;
+       }
+
+       if (status != MGMT_STATUS_SUCCESS) {
+               error("Failed to get adv tx power: %s (0x%02x)",
+                                               mgmt_errstr(status), status);
+               return;
+       }
+
+       if (length < sizeof(*rp)) {
+               error("Wrong size of get adv tx power");
+               return;
+       }
+
+       adapter->adv_tx_power = rp->adv_tx_power;
+       return;
+}
+
+static  void adapter_get_adv_tx_power(void *data)
+{
+       struct btd_adapter *adapter = data;
+
+       mgmt_send(adapter->mgmt, MGMT_OP_GET_ADV_TX_POWER,
+                                       adapter->dev_id, 0, NULL,
+                                       get_adv_tx_power_complete, adapter, NULL);
+       return;
+}
+#endif
+
+static DBusMessage *set_wbs_parameters(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       const gchar *role = NULL;
+       const gchar *address = NULL;
+       struct mgmt_cp_set_voice_setting cp;
+       bdaddr_t bt_addr = { { 0, } };
+
+       DBG("+");
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_STRING, &role,
+                               DBUS_TYPE_STRING, &address,
+                               DBUS_TYPE_INVALID)) {
+               return btd_error_invalid_args(msg);
+       }
+
+       DBG("Role = %s", role);
+       DBG("Address = %s", address);
+
+       memset(&cp, 0, sizeof(cp));
+
+       cp.voice_setting = BT_VOICE_TRANSPARENT | BT_VOICE_CVSD_16BIT;
+
+       if (g_strcmp0(role, "Handsfree") == 0)
+               cp.sco_role = MGMT_SCO_ROLE_HANDSFREE;
+       else if (g_strcmp0(role, "Gateway") == 0)
+               cp.sco_role = MGMT_SCO_ROLE_AUDIO_GATEWAY;
+
+       str2ba(address, &bt_addr);
+       memcpy(&(cp.bdaddr), &bt_addr, sizeof(bdaddr_t));
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_SET_VOICE_SETTING,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               NULL, NULL, NULL) == 0)
+               error("mgmt_send failed for voice setting");
+
+       DBG("-");
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_nb_parameters(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       const gchar *role;
+       const gchar *address = NULL;
+       struct mgmt_cp_set_voice_setting cp;
+       bdaddr_t bt_addr = { { 0, } };
+
+       DBG("+");
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_STRING, &role,
+                               DBUS_TYPE_STRING, &address,
+                               DBUS_TYPE_INVALID)) {
+               return btd_error_invalid_args(msg);
+       }
+
+       DBG("Role = %s", role);
+       DBG("Address = %s", address);
+
+       memset(&cp, 0, sizeof(cp));
+
+       cp.voice_setting = BT_VOICE_CVSD_16BIT;
+
+       if (g_strcmp0(role, "Handsfree") == 0)
+               cp.sco_role = MGMT_SCO_ROLE_HANDSFREE;
+       else if (g_strcmp0(role, "Gateway") == 0)
+               cp.sco_role = MGMT_SCO_ROLE_AUDIO_GATEWAY;
+
+       str2ba(address, &bt_addr);
+       memcpy(&(cp.bdaddr), &bt_addr, sizeof(bdaddr_t));
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_SET_VOICE_SETTING,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               NULL, NULL, NULL) == 0)
+               error("mgmt_send failed for voice setting");
+
+       DBG("-");
+
+       return dbus_message_new_method_return(msg);
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void btd_adapter_set_read_le_data_length_handler(
+                       struct btd_adapter *adapter,
+                       struct le_data_length_read_handler *handler)
+{
+       adapter->read_handler = handler;
+}
+
+static void le_read_maximum_data_length_return_param_complete(
+                       uint8_t status, uint16_t length,
+                       const void *param, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       const struct mgmt_rp_le_read_maximum_data_length *rp = param;
+       uint16_t max_tx_octects, max_tx_time;
+       uint16_t max_rx_octects, max_rx_time;
+
+       if (!rp) {
+               error("Error ocurred in Reading maximum data length, rp is NULL");
+               g_free(adapter->read_handler);
+               return;
+       }
+
+       if (status != MGMT_STATUS_SUCCESS) {
+               error("le read maximum data length failed: %s (0x%02x)",
+                       mgmt_errstr(status), status);
+               max_tx_octects = 0;
+               max_tx_time =0;
+               max_rx_octects = 0;
+               max_rx_time = 0;
+
+               g_free(adapter->read_handler);
+               return;
+       }
+
+       if (length < sizeof(*rp)) {
+               error("Too small le read maximum data length response");
+               g_free(adapter->read_handler);
+               return;
+       } else {
+               max_tx_octects = rp->max_tx_octets;
+               max_tx_time =rp->max_tx_time;
+               max_rx_octects = rp->max_rx_octets;
+               max_rx_time = rp->max_rx_time;
+       }
+
+       if (!adapter->read_handler ||
+               !adapter->read_handler->read_callback) {
+               g_free(adapter->read_handler);
+               return;
+       }
+
+       adapter->read_handler->read_callback(adapter,
+                       max_tx_octects, max_tx_time,
+                       max_rx_octects, max_rx_time,
+                       adapter->read_handler->user_data);
+
+       g_free(adapter->read_handler);
+       adapter->read_handler = NULL;
+}
+
+int btd_adapter_le_read_maximum_data_length(
+       struct btd_adapter *adapter)
+{
+       if (mgmt_send(adapter->mgmt,
+                        MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH,
+                        adapter->dev_id, 0, NULL,
+                        le_read_maximum_data_length_return_param_complete,
+                        adapter, NULL) > 0)
+               return 0;
+
+       return -EIO;
+}
+
+static gint read_request_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct le_data_length_read_request *data = a;
+       const struct btd_adapter *adapter = b;
+
+       return data->adapter !=  adapter;
+}
+
+static struct le_data_length_read_request *find_read_le_data_length_request(
+       struct btd_adapter *adapter)
+{
+       GSList *match;
+
+       match = g_slist_find_custom(read_requests, adapter, read_request_cmp);
+
+       if (match)
+               return match->data;
+
+       return NULL;
+}
+
+static void le_read_data_length_complete(
+                       struct btd_adapter *adapter,
+                       uint16_t max_tx_octects, uint16_t max_tx_time,
+                       uint16_t max_rx_octects, uint16_t max_rx_time,
+                       void *user_data)
+{
+       DBusMessage *reply;
+       struct le_data_length_read_request *read_request;
+
+       read_request = find_read_le_data_length_request(adapter);
+       if (!read_request)
+               return;
+
+       reply = g_dbus_create_reply(read_request->msg,
+                               DBUS_TYPE_UINT16, &max_tx_octects,
+                               DBUS_TYPE_UINT16, &max_tx_time,
+                               DBUS_TYPE_UINT16, &max_rx_octects,
+                               DBUS_TYPE_UINT16, &max_rx_time,
+                               DBUS_TYPE_INVALID);
+
+       if (!reply) {
+               btd_error_failed(read_request->msg,
+                                       "Failed to read max data length.");
+               return;
+       }
+
+       read_requests = g_slist_remove(read_requests, read_request);
+       dbus_message_unref(read_request->msg);
+       g_free(read_request);
+
+       if (!g_dbus_send_message(dbus_conn, reply))
+               error("D-Bus send failed");
+}
+
+static DBusMessage *le_read_maximum_data_length(
+                       DBusConnection *conn, DBusMessage *msg,
+                       void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       struct le_data_length_read_request *read_request;
+       struct le_data_length_read_handler *handler;
+
+       if (find_read_le_data_length_request(adapter))
+               return btd_error_in_progress(msg);
+
+       if (btd_adapter_le_read_maximum_data_length(adapter))
+               return btd_error_failed(msg, "Unable to read maximum le data length");
+
+       read_request = g_new(struct le_data_length_read_request, 1);
+
+       read_request->msg = dbus_message_ref(msg);
+       read_request->adapter = adapter;
+
+       read_requests = g_slist_append(read_requests, read_request);
+
+       handler = g_new0(struct le_data_length_read_handler, 1);
+
+       handler->read_callback =
+               (read_max_data_length_cb_t)le_read_data_length_complete;
+
+       btd_adapter_set_read_le_data_length_handler(
+                       read_request->adapter, handler);
+
+       return NULL;
+
+}
+
+void le_write_host_suggested_data_length_return_param_complete(
+                       uint8_t status, uint16_t length,
+                       const void *param, void *user_data)
+{
+       if (status != MGMT_STATUS_SUCCESS) {
+               error("le write host suggested data length failed: %s (0x%02x)",
+                       mgmt_errstr(status), status);
+       }
+
+       return;
+}
+
+static DBusMessage *le_write_host_suggested_default_data_length(
+                       DBusConnection *conn, DBusMessage *msg,
+                       void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       struct mgmt_cp_le_write_host_suggested_data_length cp;
+       dbus_uint16_t def_tx_Octets;
+       dbus_uint16_t def_tx_time;
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_UINT16, &def_tx_Octets,
+                                       DBUS_TYPE_UINT16, &def_tx_time,
+                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       memset(&cp, 0, sizeof(cp));
+       cp.def_tx_octets = def_tx_Octets;
+       cp.def_tx_time = def_tx_time;
+
+       if (mgmt_send(adapter->mgmt,
+                        MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH,
+                        adapter->dev_id, sizeof(cp), &cp,
+                        le_write_host_suggested_data_length_return_param_complete,
+                        adapter, NULL) > 0)
+               return dbus_message_new_method_return(msg);
+
+       return btd_error_failed(msg, "Unable to write host suggested le data length values");
+}
+
+static void le_read_suggested_default_data_length_return_param_complete(
+                       uint8_t status, uint16_t length,
+                       const void *param, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       const struct mgmt_rp_le_read_host_suggested_data_length *rp = param;
+       uint16_t def_tx_octects, def_tx_time;
+
+       if (!rp) {
+               error("Error ocurred in Reading suggested data length, rp is NULL");
+               if (adapter->def_read_handler)
+                       g_free(adapter->def_read_handler->user_data);
+
+               g_free(adapter->def_read_handler);
+               return;
+       }
+
+       if (status != MGMT_STATUS_SUCCESS) {
+               error("Read host suggested def le data length values failed: %s (0x%02x)",
+                       mgmt_errstr(status), status);
+               def_tx_octects = 0;
+               def_tx_time =0;
+
+               if (adapter->def_read_handler)
+                       g_free(adapter->def_read_handler->user_data);
+
+               g_free(adapter->def_read_handler);
+               return;
+       }
+
+       if (length < sizeof(*rp)) {
+               goto done;
+       } else {
+               def_tx_octects = rp->def_tx_octets;
+               def_tx_time =rp->def_tx_time;
+               DBG("retrieving host suggested data length values %d %d", def_tx_octects, def_tx_time);
+       }
+
+       if (!adapter->def_read_handler)
+               return;
+
+       if(!adapter->def_read_handler->read_callback) {
+               goto done;
+       }
+
+       adapter->def_read_handler->read_callback(adapter,
+                       def_tx_octects, def_tx_time,
+                       adapter->def_read_handler->user_data);
+done:
+       if (adapter->def_read_handler)
+               g_free(adapter->def_read_handler->user_data);
+
+       g_free(adapter->def_read_handler);
+       adapter->def_read_handler = NULL;
+}
+
+int btd_adapter_le_read_suggested_default_data_length(
+       struct btd_adapter *adapter)
+{
+       if (mgmt_send(adapter->mgmt,
+                        MGMT_OP_LE_READ_HOST_SUGGESTED_DATA_LENGTH,
+                        adapter->dev_id, 0, NULL,
+                        le_read_suggested_default_data_length_return_param_complete,
+                        adapter, NULL) > 0) {
+               return 0;
+       }
+
+       return -EIO;
+}
+
+static void le_read_host_suggested_default_length_complete(
+                       struct btd_adapter *adapter,
+                       uint16_t def_tx_octects, uint16_t def_tx_time,
+                       void *user_data)
+{
+       DBusMessage *reply;
+       struct le_data_length_read_request *read_request;
+
+       read_request = find_read_le_data_length_request(adapter);
+       if (!read_request)
+               return;
+
+       reply = g_dbus_create_reply(read_request->msg,
+                       DBUS_TYPE_UINT16, &def_tx_octects,
+                       DBUS_TYPE_UINT16, &def_tx_time,
+                       DBUS_TYPE_INVALID);
+
+       if (!reply) {
+               btd_error_failed(read_request->msg,
+                       "Failed to read host suggested def data length values");
+               return;
+       }
+
+       read_requests = g_slist_remove(read_requests, read_request);
+       dbus_message_unref(read_request->msg);
+       g_free(read_request);
+
+       if (!g_dbus_send_message(dbus_conn, reply))
+               error("D-Bus send failed");
+}
+
+static DBusMessage *le_read_host_suggested_default_data_length(
+                       DBusConnection *conn, DBusMessage *msg,
+                       void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       struct le_data_length_read_request *read_request;
+       struct le_data_length_read_default_data_length_handler *handler;
+
+       if (find_read_le_data_length_request(adapter))
+               return btd_error_in_progress(msg);
+
+       if (btd_adapter_le_read_suggested_default_data_length(adapter))
+               return btd_error_failed(msg, "Unable to read host suggested def data length");
+
+       read_request = g_new(struct le_data_length_read_request, 1);
+
+       read_request->msg = dbus_message_ref(msg);
+       read_request->adapter = adapter;
+
+       read_requests = g_slist_append(read_requests, read_request);
+
+       handler = g_new0(struct le_data_length_read_default_data_length_handler, 1);
+
+       handler->read_callback =
+               (read_host_suggested_default_data_length_cb_t)le_read_host_suggested_default_length_complete;
+
+       read_request->adapter->def_read_handler = handler;
+
+       return NULL;
+}
+
+void le_set_data_length_return_param_complete(
+                       uint8_t status, uint16_t length,
+                       const void *param, void *user_data)
+{
+       if (status != MGMT_STATUS_SUCCESS) {
+               error("le_set_data_length failed: %s (0x%02x)",
+                       mgmt_errstr(status), status);
+       }
+
+       return;
+}
+
+int btd_adapter_le_set_data_length(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                               uint16_t max_tx_octets, uint16_t max_tx_time)
+{
+       struct mgmt_cp_le_set_data_length cp;
+
+       memset(&cp, 0, sizeof(cp));
+
+       bacpy(&cp.bdaddr, bdaddr);
+
+       cp.max_tx_octets = max_tx_octets;
+       cp.max_tx_time = max_tx_time;
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_LE_SET_DATA_LENGTH,
+                       adapter->dev_id, sizeof(cp), &cp,
+                       le_set_data_length_return_param_complete,
+                       adapter, NULL) > 0)
+               return 0;
+
+       return -EIO;
+}
+#endif
+
+static DBusMessage *adapter_set_manufacturer_data(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct mgmt_cp_set_manufacturer_data cp;
+       uint8_t *value;
+       int32_t len = 0;
+
+       DBG("Set manufacturer data");
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       if (!dbus_message_get_args(msg, NULL,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &value, &len,
+                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       if (len > EIR_MANUFACTURER_DATA_LENGTH_MAX)
+               return btd_error_invalid_args(msg);
+
+       memcpy(&cp, value, len);
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_SET_MANUFACTURER_DATA,
+                                       adapter->dev_id, EIR_MANUFACTURER_DATA_LENGTH_MAX,
+                                       &cp, NULL, NULL, NULL) > 0)
+               return dbus_message_new_method_return(msg);
+
+       return btd_error_failed(msg, "Set manufacturer data failed");
+}
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
+static DBusMessage *start_discovery(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       const char *sender = dbus_message_get_sender(msg);
+       struct watch_client *client;
+       bool is_discovering;
+
+       DBG("sender %s", sender);
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       is_discovering = get_discovery_client(adapter, sender, &client);
+
+       /*
+        * Every client can only start one discovery, if the client
+        * already started a discovery then return an error.
+        */
+       if (is_discovering)
+               return btd_error_busy(msg);
+
+       /*
+        * If there was pre-set filter, just reconnect it to discovery_list,
+        * and trigger scan.
+        */
+       if (client) {
+               adapter->set_filter_list = g_slist_remove(
+                                            adapter->set_filter_list, client);
+               adapter->discovery_list = g_slist_prepend(
+                                             adapter->discovery_list, client);
+               update_discovery_filter(adapter);
+               return dbus_message_new_method_return(msg);
+       }
+
+       client = g_new0(struct watch_client, 1);
+
+       client->adapter = adapter;
+       client->owner = g_strdup(sender);
+       client->discovery_filter = NULL;
+       client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
+                                               discovery_disconnect, client,
+                                               discovery_destroy);
+       adapter->discovery_list = g_slist_prepend(adapter->discovery_list,
+                                                               client);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       adapter->disc_type = BT_DISC_TYPE_BREDR_ONLY;
+#endif
+
+       /*
+        * Just trigger the discovery here. In case an already running
+        * discovery in idle phase exists, it will be restarted right
+        * away.
+        */
+       update_discovery_filter(adapter);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static bool parse_uuids(DBusMessageIter *value, GSList **uuids)
+{
+       DBusMessageIter arriter;
+
+       if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_ARRAY)
+               return false;
+
+       dbus_message_iter_recurse(value, &arriter);
+       while (dbus_message_iter_get_arg_type(&arriter) != DBUS_TYPE_INVALID) {
+               bt_uuid_t uuid, u128;
+               char uuidstr[MAX_LEN_UUID_STR + 1];
+               char *uuid_param;
+
+               if (dbus_message_iter_get_arg_type(&arriter) !=
+                                               DBUS_TYPE_STRING)
+                       return false;
+
+               dbus_message_iter_get_basic(&arriter, &uuid_param);
 
                if (bt_string_to_uuid(&uuid, uuid_param))
                        return false;
@@ -2187,7 +5728,9 @@ static bool parse_transport(DBusMessageIter *value, uint8_t *transport)
                *transport = SCAN_TYPE_BREDR;
        else if (!strcmp(transport_str, "le"))
                *transport = SCAN_TYPE_LE;
-       else if (strcmp(transport_str, "auto"))
+       else if (!strcmp(transport_str, "auto"))
+               *transport = SCAN_TYPE_DUAL;
+       else
                return false;
 
        return true;
@@ -2368,6 +5911,9 @@ static DBusMessage *stop_discovery(DBusConnection *conn,
 
        client = list->data;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       adapter->disc_type = BT_DISC_TYPE_BREDR_ONLY;
+#endif
        cp.type = adapter->discovery_type;
 
        /*
@@ -2394,7 +5940,9 @@ static DBusMessage *stop_discovery(DBusConnection *conn,
 
                return dbus_message_new_method_return(msg);
        }
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       cp.type = 0x01;
+#endif
        mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
                                adapter->dev_id, sizeof(cp), &cp,
                                stop_discovery_complete, adapter, NULL);
@@ -2416,6 +5964,38 @@ static gboolean property_get_address(const GDBusPropertyTable *property,
        return TRUE;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean property_get_le_address(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       DBusMessageIter entry;
+       char addr[18];
+       const char *str = addr;
+       char *type = NULL;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                                       DBUS_TYPE_STRING_AS_STRING, &entry);
+
+       if (adapter->le_static_addr.b[5] != 0) {
+               ba2str(&adapter->le_static_addr, addr);
+               type = g_strdup_printf("%d", BDADDR_LE_RANDOM);
+       } else {
+               ba2str(&adapter->bdaddr, addr);
+               type = g_strdup_printf("%d", BDADDR_LE_PUBLIC);
+       }
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &type);
+       g_free((void *)type);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
+
+       dbus_message_iter_close_container(iter, &entry);
+
+       return TRUE;
+}
+#endif
+
 static gboolean property_get_name(const GDBusPropertyTable *property,
                                        DBusMessageIter *iter, void *user_data)
 {
@@ -2592,6 +6172,7 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting,
                len = sizeof(mode);
                break;
        case MGMT_SETTING_DISCOVERABLE:
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                if (kernel_conn_control) {
                        if (mode) {
                                set_mode(adapter, MGMT_OP_SET_CONNECTABLE,
@@ -2603,6 +6184,7 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting,
                                break;
                        }
                }
+#endif
 
                memset(&cp, 0, sizeof(cp));
                cp.val = mode;
@@ -2618,6 +6200,13 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting,
                param = &mode;
                len = sizeof(mode);
                break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       case MGMT_SETTING_CONNECTABLE:
+               opcode = MGMT_OP_SET_CONNECTABLE;
+               param = &mode;
+               len = sizeof(mode);
+               break;
+#endif
        default:
                goto failed;
        }
@@ -2632,8 +6221,16 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting,
        data->adapter = adapter;
        data->id = id;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /*
+        * Use mgmt_send_nowait to avoid dbus timeout in a state of bonding.
+        */
+       if (mgmt_send_nowait(adapter->mgmt, opcode, adapter->dev_id, len, param,
+                               property_set_mode_complete, data, g_free) > 0)
+#else
        if (mgmt_send(adapter->mgmt, opcode, adapter->dev_id, len, param,
                                property_set_mode_complete, data, g_free) > 0)
+#endif
                return;
 
        g_free(data);
@@ -2815,6 +6412,122 @@ static void iter_append_uuid(gpointer key, gpointer value, gpointer user_data)
        dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean property_get_le_discovering(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       dbus_bool_t discovering = adapter->le_discovering;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &discovering);
+
+       return TRUE;
+}
+
+static gboolean property_get_connectable(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       return property_get_mode(adapter, MGMT_SETTING_CONNECTABLE, iter);
+}
+
+static void property_set_connectable(const GDBusPropertyTable *property,
+                               DBusMessageIter *iter,
+                               GDBusPendingPropertySet id, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       property_set_mode(adapter, MGMT_SETTING_CONNECTABLE, iter, id);
+}
+
+static gboolean property_get_version(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       const char *str = adapter->version ? : "";
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+       return TRUE;
+}
+
+static gboolean property_get_supported_le_features(
+                                       const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       const char *str, *val;
+       int value;
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                                       DBUS_TYPE_STRING_AS_STRING, &entry);
+
+       value = adapter_le_get_max_adv_instance();
+       if (value > 0) {
+               str = g_strdup("adv_inst_max");
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
+
+               val = g_strdup_printf("%d", value);
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val);
+
+               g_free((void *)str);
+               g_free((void *)val);
+       }
+
+       value = adapter_le_is_supported_offloading();
+       if (value > 0) {
+               str = g_strdup("rpa_offloading");
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
+
+               val = g_strdup_printf("%d", value);
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val);
+
+               g_free((void *)str);
+               g_free((void *)val);
+       }
+
+       value = adapter_le_get_scan_filter_size();
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       if (value <= 0)
+               value = SCAN_FILTER_SLOTS_MAX;
+#endif
+       if (value > 0) {
+               str = g_strdup("max_filter");
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
+
+               val = g_strdup_printf("%d", value);
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val);
+
+               g_free((void *)str);
+               g_free((void *)val);
+       }
+
+       dbus_message_iter_close_container(iter, &entry);
+
+       return TRUE;
+}
+
+static gboolean property_get_ipsp_init_state(
+                                       const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct btd_adapter *adapter = data;
+       dbus_bool_t ipsp_initialized;
+
+       DBG("property_get_ipsp_init_state called");
+       if (adapter->ipsp_intialized)
+               ipsp_initialized = TRUE;
+       else
+               ipsp_initialized = FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+                                       &ipsp_initialized);
+
+       return TRUE;
+}
+#endif
+
 static gboolean property_get_uuids(const GDBusPropertyTable *property,
                                        DBusMessageIter *iter, void *user_data)
 {
@@ -2837,51 +6550,294 @@ static gboolean property_get_uuids(const GDBusPropertyTable *property,
                if (uuid == NULL)
                        continue;
 
-               g_hash_table_add(uuids, uuid);
+               g_hash_table_add(uuids, uuid);
+       }
+
+       /* GATT services */
+       db = btd_gatt_database_get_db(adapter->database);
+       if (db)
+               gatt_db_foreach_service(db, NULL, add_gatt_uuid, uuids);
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                                       DBUS_TYPE_STRING_AS_STRING, &entry);
+       g_hash_table_foreach(uuids, iter_append_uuid, &entry);
+       dbus_message_iter_close_container(iter, &entry);
+
+       g_hash_table_destroy(uuids);
+
+       return TRUE;
+}
+
+static gboolean property_exists_modalias(const GDBusPropertyTable *property,
+                                                       void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       return adapter->modalias ? TRUE : FALSE;
+}
+
+static gboolean property_get_modalias(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       const char *str = adapter->modalias ? : "";
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+       return TRUE;
+}
+
+static int device_path_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct btd_device *device = a;
+       const char *path = b;
+       const char *dev_path = device_get_path(device);
+
+       return strcasecmp(dev_path, path);
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *adapter_unpair_device(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       DBG("+");
+       struct btd_adapter *adapter = user_data;
+       struct btd_device *device;
+       const char *path;
+       GSList *list;
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               return btd_error_invalid_args(msg);
+
+       list = g_slist_find_custom(adapter->devices, path, device_path_cmp);
+       if (!list)
+               return btd_error_does_not_exist(msg);
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       device = list->data;
+
+       btd_device_set_temporary(device, TRUE);
+
+       if (!btd_device_is_connected(device)) {
+               btd_adapter_unpair_device(adapter, device);
+               return dbus_message_new_method_return(msg);
+       }
+
+       device_request_disconnect(device, msg);
+
+       DBG("-");
+       return NULL;
+}
+
+static DBusMessage *create_device(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       const gchar *address;
+       bdaddr_t addr;
+       DBG("+");
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               return btd_error_invalid_args(msg);
+
+       if (bachk(address) < 0)
+               return btd_error_invalid_args(msg);
+
+       DBG("%s", address);
+
+       str2ba(address, &addr);
+       btd_adapter_get_device(adapter, &addr, BDADDR_BREDR);
+
+       DBG("-");
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *find_device(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct btd_device *device;
+       DBusMessage *reply;
+       const gchar *address;
+       GSList *l;
+       const gchar *dev_path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
+                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       l = g_slist_find_custom(adapter->devices, address, device_rpa_cmp);
+       if (!l)
+               l = g_slist_find_custom(adapter->devices, address,
+                                                       device_address_cmp);
+       if (!l)
+               return btd_error_does_not_exist(msg);
+
+       device = l->data;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dev_path = device_get_path(device);
+
+       dbus_message_append_args(reply,
+                       DBUS_TYPE_OBJECT_PATH, &dev_path,
+                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static gboolean adapter_ipsp_connected(struct btd_adapter *adapter)
+{
+       GSList *l, *next;
+
+       DBG("%s", adapter->path);
+
+       for (l = adapter->connections; l != NULL; l = next) {
+               struct btd_device *dev = l->data;
+
+               next = g_slist_next(l);
+
+               if (device_is_ipsp_connected(dev))
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void adapter_set_ipsp_init_state(struct btd_adapter *adapter, gboolean initialized)
+{
+       if (adapter->ipsp_intialized == initialized)
+               return;
+
+       adapter->ipsp_intialized = initialized;
+
+       DBG("Set Ipsp init state for adapter %s", adapter->path);
+
+       g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                                               ADAPTER_INTERFACE, "IpspInitStateChanged");
+}
+
+static void deinitialize_6lowpan_complete(uint8_t status, uint16_t length,
+       const void *param, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       bool initialized = FALSE;
+
+       if (status != MGMT_STATUS_SUCCESS)
+               error("De-Initialize BT 6lowpan failed for hci%u: %s (0x%02x)",
+                       adapter->dev_id, mgmt_errstr(status), status);
+       else {
+               adapter_set_ipsp_init_state(adapter, initialized);
+               DBG("De-Initialize BT 6lowpan successfully for hci%u",
+                       adapter->dev_id);
+       }
+}
+
+static bool deinitialize_6lowpan(struct btd_adapter *adapter)
+{
+       struct mgmt_cp_enable_6lowpan cp;
+
+       memset(&cp, 0, sizeof(cp));
+
+       cp.enable_6lowpan = DEINIT_6LOWPAN;
+       if (mgmt_send(adapter->mgmt, MGMT_OP_ENABLE_6LOWPAN,
+                       adapter->dev_id, sizeof(cp), &cp,
+                       deinitialize_6lowpan_complete, adapter, NULL) > 0)
+               return true;
+
+       error("Failed to de-initialize BT 6Lowpan for index %u",
+               adapter->dev_id);
+       return false;
+}
+
+static void initialize_6lowpan_complete(uint8_t status, uint16_t length,
+       const void *param, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       bool initialized = TRUE;
+
+       if (status != MGMT_STATUS_SUCCESS)
+               error("Initialize BT 6lowpan failed for hci%u: %s (0x%02x)",
+                       adapter->dev_id, mgmt_errstr(status), status);
+       else {
+               adapter_set_ipsp_init_state(adapter, initialized);
+               DBG("Initialize BT 6lowpan successfully for hci%u",
+                       adapter->dev_id);
        }
+}
 
-       /* GATT services */
-       db = btd_gatt_database_get_db(adapter->database);
-       if (db)
-               gatt_db_foreach_service(db, NULL, add_gatt_uuid, uuids);
+static bool initialize_6lowpan(struct btd_adapter *adapter)
+{
+       struct mgmt_cp_enable_6lowpan cp;
 
-       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
-                                       DBUS_TYPE_STRING_AS_STRING, &entry);
-       g_hash_table_foreach(uuids, iter_append_uuid, &entry);
-       dbus_message_iter_close_container(iter, &entry);
+       memset(&cp, 0, sizeof(cp));
 
-       g_hash_table_destroy(uuids);
+       cp.enable_6lowpan = INIT_6LOWPAN;
+       if (mgmt_send(adapter->mgmt, MGMT_OP_ENABLE_6LOWPAN,
+                       adapter->dev_id, sizeof(cp), &cp,
+                       initialize_6lowpan_complete, adapter, NULL) > 0)
+               return true;
 
-       return TRUE;
+       error("Failed to initialize BT 6Lowpan for index %u",
+               adapter->dev_id);
+       return false;
 }
 
-static gboolean property_exists_modalias(const GDBusPropertyTable *property,
-                                                       void *user_data)
+static DBusMessage *adapter_initialize_ipsp(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
 {
-       struct btd_adapter *adapter = user_data;
+       struct btd_adapter *adapter = data;
+       dbus_bool_t err;
 
-       return adapter->modalias ? TRUE : FALSE;
-}
+       DBG("Initialize IPSP");
 
-static gboolean property_get_modalias(const GDBusPropertyTable *property,
-                                       DBusMessageIter *iter, void *user_data)
-{
-       struct btd_adapter *adapter = user_data;
-       const char *str = adapter->modalias ? : "";
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
 
-       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+       if (adapter->ipsp_intialized)
+               return btd_error_already_exists(msg);
 
-       return TRUE;
+       /* Enable BT 6lowpan in kernel */
+       err = initialize_6lowpan(adapter);
+
+       if (!err)
+               return btd_error_failed(msg, "Failed to initialize BT 6lowpan");
+
+       return dbus_message_new_method_return(msg);
 }
 
-static int device_path_cmp(gconstpointer a, gconstpointer b)
+static DBusMessage *adapter_deinitialize_ipsp(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
 {
-       const struct btd_device *device = a;
-       const char *path = b;
-       const char *dev_path = device_get_path(device);
+       struct btd_adapter *adapter = data;
+       dbus_bool_t err;
 
-       return strcasecmp(dev_path, path);
+       DBG("De-initialize IPSP");
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       if (!adapter->ipsp_intialized)
+               return btd_error_not_permitted(msg, "IPSP not initialized");
+
+       if (adapter_ipsp_connected(adapter))
+               return btd_error_not_permitted(msg, "IPSP Client device found connected");
+
+       /* Disable BT 6lowpan in kernel */
+       err = deinitialize_6lowpan(adapter);
+
+       if (!err)
+               return btd_error_failed(msg, "Failed to deinitialize BT 6lowpan");
+
+       return dbus_message_new_method_return(msg);
 }
+#endif
 
 static DBusMessage *remove_device(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
@@ -2922,10 +6878,157 @@ static const GDBusMethodTable adapter_methods[] = {
                                GDBUS_ARGS({ "properties", "a{sv}" }), NULL,
                                set_discovery_filter) },
        { GDBUS_METHOD("StopDiscovery", NULL, NULL, stop_discovery) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { GDBUS_METHOD("StartCustomDiscovery",
+                       GDBUS_ARGS({ "type", "s" }), NULL,
+                       adapter_start_custom_discovery) },
+       { GDBUS_METHOD("StartLEDiscovery", NULL, NULL,
+                       adapter_start_le_discovery) },
+       { GDBUS_ASYNC_METHOD("StopLEDiscovery", NULL, NULL,
+                       adapter_stop_le_discovery) },
+       { GDBUS_METHOD("SetAdvertising",
+                       GDBUS_ARGS({ "enable", "b" },
+                               { "slot_id", "i" }), NULL,
+                       adapter_set_advertising) },
+       { GDBUS_METHOD("SetAdvertisingParameters",
+                       GDBUS_ARGS({ "interval_min", "u" },
+                               { "interval_max", "u" },
+                               { "filter_policy", "u" },
+                               { "type", "u" },
+                               { "slot_id", "i" }), NULL,
+                       adapter_set_advertising_params) },
+       { GDBUS_METHOD("SetAdvertisingData",
+                       GDBUS_ARGS({ "value", "ay" },
+                               { "slot_id", "i" }), NULL,
+                       adapter_set_advertising_data) },
+       { GDBUS_METHOD("SetScanParameters",
+                       GDBUS_ARGS({ "type", "u" },
+                               { "interval", "u" },
+                               { "window", "u" }), NULL,
+                       adapter_le_set_scan_params) },
+       { GDBUS_ASYNC_METHOD("scan_filter_param_setup",
+                       GDBUS_ARGS({ "client_if", "i" }, { "action", "i" },
+                               { "filt_index", "i" }, { "feat_seln", "i"},
+                               { "list_logic_type", "i" }, { "filt_logic_type", "i"},
+                               { "rssi_high_thres", "i" }, { "rssi_low_thres", "i"},
+                               { "dely_mode", "i" }, { "found_timeout", "i"},
+                               { "lost_timeout", "i" }, { "found_timeout_cnt", "i"}), NULL,
+                       adapter_le_scan_filter_param_setup) },
+       { GDBUS_ASYNC_METHOD("scan_filter_add_remove",
+                       GDBUS_ARGS({ "client_if", "i" }, { "action", "i" },
+                               { "filt_type", "i" }, { "filt_index", "i"},
+                               { "company_id", "i" }, { "company_id_mask", "i"},
+                               { "p_uuid", "ay" }, { "p_uuid_mask", "ay" },
+                               { "string", "s" }, { "address_type", "u" },
+                               /*{ "data_len", "i" },*/ { "p_data", "ay" },
+                               /*{ "mask_len", "i" },*/ { "p_mask", "ay" }), NULL,
+                       adapter_le_scan_filter_add_remove) },
+       { GDBUS_ASYNC_METHOD("scan_filter_clear",
+                       GDBUS_ARGS({ "client_if", "i" }, { "filt_index", "i" }), NULL,
+                       adapter_le_scan_filter_clear) },
+       { GDBUS_ASYNC_METHOD("scan_filter_enable",
+                       GDBUS_ARGS({ "client_if", "i" }, { "enable", "b" }), NULL,
+                       adapter_le_scan_filter_enable) },
+       { GDBUS_METHOD("InitializeIpsp",
+                       NULL, NULL,
+                       adapter_initialize_ipsp) },
+       { GDBUS_METHOD("DeinitializeIpsp",
+                       NULL, NULL,
+                       adapter_deinitialize_ipsp) },
+       { GDBUS_METHOD("SetScanRespData",
+                       GDBUS_ARGS({ "value", "ay" },
+                               { "slot_id", "i" }), NULL,
+                       adapter_set_scan_rsp_data) },
+       { GDBUS_METHOD("AddDeviceWhiteList",
+                       GDBUS_ARGS({ "address", "s" },
+                               { "address_type", "u" }), NULL,
+                       adapter_add_device_white_list) },
+       { GDBUS_METHOD("RemoveDeviceWhiteList",
+                       GDBUS_ARGS({ "address", "s" },
+                               { "address_type", "u" }), NULL,
+                       adapter_remove_device_white_list) },
+       { GDBUS_METHOD("ClearDeviceWhiteList",
+                       NULL, NULL,
+                       adapter_clear_device_white_list) },
+       { GDBUS_METHOD("SetLePrivacy",
+                       GDBUS_ARGS({ "enable", "b" }), NULL,
+                       adapter_set_le_privacy) },
+       { GDBUS_METHOD("SetLeStaticRandomAddress",
+                       GDBUS_ARGS({ "enable", "b" }), NULL,
+                       adapter_set_le_static_address) },
+       { GDBUS_ASYNC_METHOD("EnableRssi",
+                       GDBUS_ARGS({ "bt_address", "s" },
+                               { "link_type", "i" },
+                               { "low_th", "i" },
+                               { "in_range_th", "i" },
+                               { "high_th", "i"}),
+                       NULL,
+                       adapter_enable_rssi) },
+       { GDBUS_ASYNC_METHOD("GetRssiStrength",
+                       GDBUS_ARGS({ "bt_address", "s" }, { "link_type", "i" }),
+                       NULL,
+                       adapter_get_rssi) },
+       { GDBUS_ASYNC_METHOD("UnpairDevice",
+                       GDBUS_ARGS({ "device", "o" }), NULL, adapter_unpair_device) },
+       { GDBUS_METHOD("FindDevice",
+                       GDBUS_ARGS({ "address", "s" }),
+                       GDBUS_ARGS({ "device", "o" }),
+                       find_device) },
+       { GDBUS_METHOD("SetWbsParameters",
+                       GDBUS_ARGS({ "role", "s" }, { "bt_address", "s" }),
+                       NULL,
+                       set_wbs_parameters) },
+       { GDBUS_METHOD("SetNbParameters",
+                       GDBUS_ARGS({ "role", "s" }, { "bt_address", "s" }),
+                       NULL,
+                       set_nb_parameters) },
+       { GDBUS_METHOD("SetManufacturerData",
+                       GDBUS_ARGS({ "value", "ay" }), NULL,
+                       adapter_set_manufacturer_data) },
+       { GDBUS_ASYNC_METHOD("CreateDevice",
+                       GDBUS_ARGS({ "address", "s" }), NULL,
+                       create_device) },
+#endif
        { GDBUS_ASYNC_METHOD("RemoveDevice",
                        GDBUS_ARGS({ "device", "o" }), NULL, remove_device) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { GDBUS_ASYNC_METHOD("LEReadMaximumDataLength", NULL,
+                       GDBUS_ARGS({"maxTxOctets", "q" }, { "maxTxTime", "q" },
+                               {"maxRxOctets", "q" }, { "maxRxTime", "q" }),
+                       le_read_maximum_data_length)},
+       { GDBUS_ASYNC_METHOD("LEWriteHostSuggestedDataLength",
+                       GDBUS_ARGS({"def_tx_octets", "q" }, { "def_tx_time", "q" }), NULL,
+                       le_write_host_suggested_default_data_length)},
+       { GDBUS_ASYNC_METHOD("LEReadHostSuggestedDataLength", NULL,
+                       GDBUS_ARGS({"def_tx_octets", "q" }, { "def_tx_time", "q" }),
+                       le_read_host_suggested_default_data_length)},
+#endif
+       { }
+};
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static const GDBusSignalTable adapter_signals[] = {
+       { GDBUS_SIGNAL("AdvertisingEnabled",
+                       GDBUS_ARGS({ "slot_id", "i" },
+                                       { "enabled", "b"})) },
+       { GDBUS_SIGNAL("RssiEnabled",
+                       GDBUS_ARGS({"address","s"},
+                                       { "link_type", "i" },
+                                       { "enabled", "b"})) },
+       { GDBUS_SIGNAL("RssiAlert",
+                       GDBUS_ARGS({"address","s"},
+                                       { "link_type", "i" },
+                                       { "alert_type", "i" },
+                                       { "rssi_dbm", "i"})) },
+       { GDBUS_SIGNAL("RawRssi",
+                       GDBUS_ARGS({"address","s"},
+                                       { "link_type", "i" },
+                                       { "rssi_dbm", "i"})) },
+       { GDBUS_SIGNAL("HardwareError", NULL) },
+       { GDBUS_SIGNAL("TxTimeoutError", NULL) },
        { }
 };
+#endif
 
 static const GDBusPropertyTable adapter_properties[] = {
        { "Address", "s", property_get_address },
@@ -2941,9 +7044,21 @@ static const GDBusPropertyTable adapter_properties[] = {
        { "PairableTimeout", "u", property_get_pairable_timeout,
                                        property_set_pairable_timeout },
        { "Discovering", "b", property_get_discovering },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { "LEDiscovering", "b", property_get_le_discovering },
+#endif
        { "UUIDs", "as", property_get_uuids },
        { "Modalias", "s", property_get_modalias, NULL,
                                        property_exists_modalias },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { "Connectable", "b", property_get_connectable,
+                                       property_set_connectable },
+       { "Version", "s", property_get_version },
+       { "SupportedLEFeatures", "as", property_get_supported_le_features},
+       { "IpspInitStateChanged", "b", property_get_ipsp_init_state},
+       { "LEAddress", "as", property_get_le_address },
+#endif
+
        { }
 };
 
@@ -3094,8 +7209,17 @@ static struct irk_info *get_irk_info(GKeyFile *key_file, const char *peer,
        char *str;
 
        str = g_key_file_get_string(key_file, "IdentityResolvingKey", "Key", NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (!str)
+               return NULL;
+       if (strlen(str) < 32) {
+               g_free(str);
+               return NULL;
+       }
+#else
        if (!str || strlen(str) < 32)
                goto failed;
+#endif
 
        irk = g_new0(struct irk_info, 1);
 
@@ -3106,8 +7230,9 @@ static struct irk_info *get_irk_info(GKeyFile *key_file, const char *peer,
                str2buf(&str[2], irk->val, sizeof(irk->val));
        else
                str2buf(&str[0], irk->val, sizeof(irk->val));
-
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
 failed:
+#endif
        g_free(str);
 
        return irk;
@@ -3216,6 +7341,7 @@ static int load_irk(struct btd_adapter *adapter, uint8_t *irk)
        return 0;
 }
 
+#if 0
 static void set_privacy_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
@@ -3259,6 +7385,7 @@ static int set_privacy(struct btd_adapter *adapter, uint8_t privacy)
 
        return -1;
 }
+#endif
 
 static void load_link_keys_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
@@ -3611,6 +7738,47 @@ static uint8_t get_le_addr_type(GKeyFile *keyfile)
        return addr_type;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static uint8_t get_addr_type(GKeyFile *keyfile)
+{
+       char **techno, **t;
+       char *str;
+       uint8_t bdaddr_type = BDADDR_BREDR;
+       bool le = false;
+
+       /* Load device technology */
+       techno = g_key_file_get_string_list(keyfile, "General",
+                                       "SupportedTechnologies", NULL, NULL);
+       if (!techno)
+               return 0xff;
+
+       for (t = techno; *t; t++) {
+               if (g_str_equal(*t, "LE"))
+                       le = true;
+       }
+
+       if (!le) {
+               bdaddr_type = BDADDR_BREDR;
+       } else {
+               str = g_key_file_get_string(keyfile, "General",
+                                               "AddressType", NULL);
+
+               if (str && g_str_equal(str, "public"))
+                       bdaddr_type = BDADDR_LE_PUBLIC;
+               else if (str && g_str_equal(str, "static"))
+                       bdaddr_type = BDADDR_LE_RANDOM;
+               else
+                       error("Unknown LE device technology");
+
+               g_free(str);
+       }
+
+       g_strfreev(techno);
+
+       return bdaddr_type;
+}
+#endif
+
 static void probe_devices(void *user_data)
 {
        struct btd_device *device = user_data;
@@ -3646,8 +7814,14 @@ static void load_devices(struct btd_adapter *adapter)
                struct btd_device *device;
                char filename[PATH_MAX];
                GKeyFile *key_file;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               struct link_key_info *key_info = NULL;
+               GSList *list, *ltk_info = NULL;
+               struct device_addr_type addr;
+#else
                struct link_key_info *key_info;
                GSList *list, *ltk_info;
+#endif
                struct irk_info *irk_info;
                struct conn_param *param;
                uint8_t bdaddr_type;
@@ -3658,6 +7832,18 @@ static void load_devices(struct btd_adapter *adapter)
                if (entry->d_type != DT_DIR || bachk(entry->d_name) < 0)
                        continue;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+{
+               bdaddr_t bdaddr;
+
+               str2ba(entry->d_name, &bdaddr);
+
+               if (!bacmp(&bdaddr, BDADDR_ANY)) {
+                       error("No Bluetooth address");
+                       continue;
+               }
+}
+#endif
                snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", srcaddr,
                                entry->d_name);
 
@@ -3681,9 +7867,25 @@ static void load_devices(struct btd_adapter *adapter)
                if (param)
                        params = g_slist_append(params, param);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               str2ba(entry->d_name, &addr.bdaddr);
+               addr.bdaddr_type = get_addr_type(key_file);
+               if (addr.bdaddr_type == 0xff) {
+                       error("No SupportedTechnologies. Skipping");
+                       goto free;
+               }
+
+               list = g_slist_find_custom(adapter->devices, &addr,
+                                               device_addr_type_strict_cmp);
+#else
                list = g_slist_find_custom(adapter->devices, entry->d_name,
                                                        device_address_cmp);
+#endif
                if (list) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                       DBG("Skip already loaded device [%s] [%d]",
+                                       entry->d_name, addr.bdaddr_type);
+#endif
                        device = list->data;
                        goto device_exist;
                }
@@ -3693,6 +7895,42 @@ static void load_devices(struct btd_adapter *adapter)
                if (!device)
                        goto free;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+{
+               char idaddr[18];
+
+               /*
+                * After loading IRK information from file,
+                * store it into device->bdaddr.
+                * RPA is stored in device->rpa_addr
+                */
+               ba2str(device_get_address(device), idaddr);
+
+               DBG("irk address: %s, rpa_exist %d",
+                               idaddr, device_get_rpa_exist(device));
+
+               if (device_get_rpa_exist(device) == true) {
+                       if (key_info)
+                               str2ba(idaddr, &key_info->bdaddr);
+
+                       if (ltk_info) {
+                               ltks = g_slist_remove(ltks, ltk_info);
+                               ltk_info = get_ltk_info(key_file,
+                                               idaddr, bdaddr_type);
+                               ltks = g_slist_concat(ltks, ltk_info);
+                       }
+
+                       if (irk_info) {
+                               str2ba(idaddr, &irk_info->bdaddr);
+                               device_set_irk_value(device, irk_info->val);
+                       }
+
+                       if (param)
+                               str2ba(idaddr, &param->bdaddr);
+               }
+}
+#endif
+
                btd_device_set_temporary(device, false);
                adapter->devices = g_slist_append(adapter->devices, device);
 
@@ -4276,8 +8514,30 @@ void adapter_auto_connect_remove(struct btd_adapter *adapter,
 
 static void adapter_start(struct btd_adapter *adapter)
 {
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && !defined(__SPRD_PATCH__)
+       if (adapter_le_read_ble_feature_info())
+               g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                       ADAPTER_INTERFACE, "SupportedLEFeatures");
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       else
+               g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                       ADAPTER_INTERFACE, "SupportedLEFeatures");
+#endif
+
+       adapter_get_adv_tx_power(adapter);
+
+       /* By default enable offloading for testing, this should be modified */
+       if (adapter_le_is_supported_offloading())
+               adapter_le_enable_offloading(TRUE);
+#endif
+
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        g_dbus_emit_property_changed(dbus_conn, adapter->path,
                                                ADAPTER_INTERFACE, "Powered");
+#else
+       g_dbus_emit_property_changed_full(dbus_conn, adapter->path,
+                                               ADAPTER_INTERFACE, "Powered", 1);
+#endif
 
        DBG("adapter %s has been enabled", adapter->path);
 
@@ -5234,7 +9494,9 @@ static void load_config(struct btd_adapter *adapter)
        char address[18];
        struct stat st;
        GError *gerr = NULL;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char *str;
+#endif
        ba2str(&adapter->bdaddr, address);
 
        key_file = g_key_file_new();
@@ -5284,12 +9546,35 @@ static void load_config(struct btd_adapter *adapter)
                gerr = NULL;
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* Get A2DP Role */
+       str = g_key_file_get_string(key_file, "General", "DefaultA2DPRole", &gerr);
+       if (gerr || !str) {
+               adapter->a2dp_role = BLUETOOTH_A2DP_SOURCE_ROLE;
+               g_error_free(gerr);
+               gerr = NULL;
+       } else {
+               if (g_strcmp0(str, "sink") == 0)
+                       adapter->a2dp_role = BLUETOOTH_A2DP_SINK_ROLE;
+               else if (g_strcmp0(str, "source") == 0)
+                       adapter->a2dp_role = BLUETOOTH_A2DP_SOURCE_ROLE;
+       }
+#endif
+
        g_key_file_free(key_file);
 }
 
 static struct btd_adapter *btd_adapter_new(uint16_t index)
 {
        struct btd_adapter *adapter;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+       DBusConnection *conn = btd_get_dbus_connection();
+       DBusMessage *msg = NULL;
+       DBusMessage *reply = NULL;
+       int charging_state = 0;
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
 
        adapter = g_try_new0(struct btd_adapter, 1);
        if (!adapter)
@@ -5316,6 +9601,9 @@ static struct btd_adapter *btd_adapter_new(uint16_t index)
                                                main_opts.did_version);
        adapter->discoverable_timeout = main_opts.discovto;
        adapter->pairable_timeout = main_opts.pairto;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       adapter->advertising = FALSE;
+#endif
 
        DBG("System name: %s", adapter->system_name);
        DBG("Major class: %u", adapter->major_class);
@@ -5323,19 +9611,67 @@ static struct btd_adapter *btd_adapter_new(uint16_t index)
        DBG("Modalias: %s", adapter->modalias);
        DBG("Discoverable timeout: %u seconds", adapter->discoverable_timeout);
        DBG("Pairable timeout: %u seconds", adapter->pairable_timeout);
-
        adapter->auths = g_queue_new();
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+       adapter->charging_watch = g_dbus_add_signal_watch(conn, DEVICED_DEST,
+                       DEVICED_BATT_OBJECT_PATH,
+                       DEVICED_BATT_INTERFACE, "ChargerType",
+                       charging_state_changed, adapter, NULL);
+       if (adapter->charging_watch == 0)
+               error("Cannot add signal watch for ChargerType");
+
+       msg = dbus_message_new_method_call(DEVICED_DEST,
+                       DEVICED_BATT_OBJECT_PATH,
+                       DEVICED_BATT_INTERFACE, "ChargerType");
+       if (msg) {
+               reply = dbus_connection_send_with_reply_and_block(conn,
+                               msg, 1000, NULL);
+               if (reply) {
+                       if (dbus_message_get_args(reply, NULL,
+                                       DBUS_TYPE_INT32, &charging_state,
+                                       DBUS_TYPE_INVALID) == TRUE) {
+                               set_charging_state(adapter, charging_state);
+                       }
+                       dbus_message_unref(reply);
+               } else {
+                       error("Reply is NULL");
+               }
+               dbus_message_unref(msg);
+       } else {
+               error("Unable to create dbus message for charging state");
+       }
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
+
        return btd_adapter_ref(adapter);
 }
 
 static void adapter_remove(struct btd_adapter *adapter)
 {
        GSList *l;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        struct gatt_db *db;
+#endif
 
        DBG("Removing adapter %s", adapter->path);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+       if (adapter->charging_watch > 0) {
+               g_dbus_remove_watch(btd_get_dbus_connection(),
+                               adapter->charging_watch);
+               adapter->charging_watch = 0;
+       }
+
+       if (adapter->charging_timeout) {
+               g_source_remove(adapter->charging_timeout);
+               adapter->charging_timeout = 0;
+       }
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
+
        if (adapter->discovery_idle_timeout > 0) {
                g_source_remove(adapter->discovery_idle_timeout);
                adapter->discovery_idle_timeout = 0;
@@ -5359,12 +9695,16 @@ static void adapter_remove(struct btd_adapter *adapter)
 
        unload_drivers(adapter);
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        db = btd_gatt_database_get_db(adapter->database);
        gatt_db_unregister(db, adapter->db_id);
        adapter->db_id = 0;
 
        btd_gatt_database_destroy(adapter->database);
        adapter->database = NULL;
+#else
+       btd_adapter_gatt_server_stop(adapter);
+#endif
 
        btd_advertising_manager_destroy(adapter->adv_manager);
        adapter->adv_manager = NULL;
@@ -5389,6 +9729,24 @@ const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter)
        return &adapter->bdaddr;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+const bdaddr_t *btd_adapter_get_le_address(struct btd_adapter *adapter)
+{
+       if (adapter->le_static_addr.b[5] != 0)
+               return &adapter->le_static_addr;
+       else
+               return &adapter->bdaddr;
+}
+
+uint8_t btd_adapter_get_le_address_type(struct btd_adapter * adapter)
+{
+       if (adapter->le_static_addr.b[5] != 0)
+               return BDADDR_LE_RANDOM;
+       else
+               return BDADDR_LE_PUBLIC;
+}
+#endif
+
 static gboolean confirm_name_timeout(gpointer user_data)
 {
        struct btd_adapter *adapter = user_data;
@@ -5554,28 +9912,53 @@ static bool is_filter_match(GSList *discovery_filter, struct eir_data *eir_data,
        return got_match;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void update_found_devices(struct btd_adapter *adapter,
+                                       const bdaddr_t *bdaddr,
+                                       uint8_t bdaddr_type, int8_t rssi,
+                                       bool confirm, bool legacy, uint8_t adv_type,
+                                       const uint8_t *data, uint8_t data_len)
+#else
 static void update_found_devices(struct btd_adapter *adapter,
                                        const bdaddr_t *bdaddr,
                                        uint8_t bdaddr_type, int8_t rssi,
                                        bool confirm, bool legacy,
                                        bool not_connectable,
                                        const uint8_t *data, uint8_t data_len)
+#endif
 {
        struct btd_device *dev;
        struct eir_data eir_data;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        bool name_known, discoverable;
+#else
+       bool name_known;
+#endif
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       uint8_t allow_report;
+#endif
        char addr[18];
 
        memset(&eir_data, 0, sizeof(eir_data));
        eir_parse(&eir_data, data, data_len);
-
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (bdaddr_type == BDADDR_BREDR || adapter->filtered_discovery)
                discoverable = true;
        else
                discoverable = eir_data.flags & (EIR_LIM_DISC | EIR_GEN_DISC);
-
+#endif
        ba2str(bdaddr, addr);
 
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+       /* Check if the any filter policy */
+       allow_report = validate_for_filter_policy(adapter, &eir_data, addr);
+       if (allow_report == NONE_REPORT &&
+               ((adapter->scan_type == LE_ACTIVE_SCAN && adv_type == ADV_TYPE_SCAN_RESPONSE) ||
+               adapter->scan_type == LE_PASSIVE_SCAN)) {
+               eir_data_free(&eir_data);
+               return;
+       }
+#endif
        dev = btd_adapter_find_device(adapter, bdaddr, bdaddr_type);
        if (!dev) {
                /*
@@ -5583,10 +9966,21 @@ static void update_found_devices(struct btd_adapter *adapter,
                 * not marked as discoverable, then do not create new
                 * device objects.
                 */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               /*DBG("List BREDR:%p LE:%p Discoverable:%d", adapter->discovery_list,
+                       adapter->le_discovery_list, discoverable);*/
+               if ((adapter->discovery_list == NULL &&
+                               adapter->le_discovery_list == NULL)) {
+                       DBG("discovery list is NULL");
+                       eir_data_free(&eir_data);
+                       return;
+               }
+#else
                if (!adapter->discovery_list || !discoverable) {
                        eir_data_free(&eir_data);
                        return;
                }
+#endif
 
                dev = adapter_create_device(adapter, bdaddr, bdaddr_type);
        }
@@ -5598,6 +9992,11 @@ static void update_found_devices(struct btd_adapter *adapter,
                return;
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if(device_get_rpa_exist(dev) == true)
+               bdaddr_type = BDADDR_LE_RANDOM;
+#endif
+
        device_update_last_seen(dev, bdaddr_type);
 
        /*
@@ -5620,11 +10019,26 @@ static void update_found_devices(struct btd_adapter *adapter,
         * If no client has requested discovery, then only update
         * already paired devices (skip temporary ones).
         */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (device_is_temporary(dev) && adapter->discovery_list == NULL &&
+                       adapter->le_discovery_list == NULL) {
+               DBG("discovery list is NULL");
+               eir_data_free(&eir_data);
+               return;
+       }
+
+       device_set_last_addr_type(dev, bdaddr_type);
+       device_set_ipsp_connected(dev, FALSE, NULL);
+#else
        if (device_is_temporary(dev) && !adapter->discovery_list) {
                eir_data_free(&eir_data);
                return;
        }
+#endif
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (bdaddr_type == BDADDR_BREDR) {
+#endif
        if (adapter->filtered_discovery &&
            !is_filter_match(adapter->discovery_list, &eir_data, rssi)) {
                eir_data_free(&eir_data);
@@ -5640,13 +10054,23 @@ static void update_found_devices(struct btd_adapter *adapter,
 
        if (eir_data.tx_power != 127)
                device_set_tx_power(dev, eir_data.tx_power);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       }
+#endif
 
        if (eir_data.appearance != 0)
                device_set_appearance(dev, eir_data.appearance);
 
        /* Report an unknown name to the kernel even if there is a short name
         * known, but still update the name with the known short name. */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (eir_data.name_complete)
+               name_known = device_name_known(dev);
+       else
+               name_known = false;
+#else
        name_known = device_name_known(dev);
+#endif
 
        if (eir_data.name && (eir_data.name_complete || !name_known))
                btd_device_device_set_name(dev, eir_data.name);
@@ -5663,6 +10087,19 @@ static void update_found_devices(struct btd_adapter *adapter,
 
        device_add_eir_uuids(dev, eir_data.services);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (eir_data.flags != 0)
+               device_set_remote_feature_flag(dev, eir_data.flags);
+
+       if (bdaddr_type == BDADDR_BREDR)
+               device_set_manufacturer_info(dev, &eir_data);
+       else {
+               /* if the application has registered for iBeacon report,
+                * then send ibeacon report along with advertisement report */
+               device_set_adv_report_info(dev, (void*)data, data_len, adv_type, rssi);
+       }
+#endif
+
        if (eir_data.msd_list) {
                device_set_manufacturer_data(dev, eir_data.msd_list);
                adapter_msd_notify(adapter, dev, eir_data.msd_list);
@@ -5682,7 +10119,11 @@ static void update_found_devices(struct btd_adapter *adapter,
         * Otherwise, this is an event from passive discovery and we
         * should check if the device needs connecting to.
         */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (!adapter->discovery_list && !adapter->le_discovery_list)
+#else
        if (!adapter->discovery_list)
+#endif
                goto connect_le;
 
        if (g_slist_find(adapter->discovery_found, dev))
@@ -5697,9 +10138,11 @@ static void update_found_devices(struct btd_adapter *adapter,
        return;
 
 connect_le:
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        /* Ignore non-connectable events */
        if (not_connectable)
                return;
+#endif
 
        /*
         * If we're in the process of stopping passive scanning and
@@ -5768,11 +10211,65 @@ static void device_found_callback(uint16_t index, uint16_t length,
        confirm_name = (flags & MGMT_DEV_FOUND_CONFIRM_NAME);
        legacy = (flags & MGMT_DEV_FOUND_LEGACY_PAIRING);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       update_found_devices(adapter, &ev->addr.bdaddr, ev->addr.type,
+                                       ev->rssi, confirm_name, legacy, 0,
+                                       eir, eir_len);
+#else
        update_found_devices(adapter, &ev->addr.bdaddr, ev->addr.type,
                                        ev->rssi, confirm_name, legacy,
                                        flags & MGMT_DEV_FOUND_NOT_CONNECTABLE,
                                        eir, eir_len);
+#endif
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void le_device_found_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_ev_le_device_found *ev = param;
+       struct btd_adapter *adapter = user_data;
+       const uint8_t *eir;
+       uint16_t eir_len;
+       uint32_t flags;
+       bool confirm_name;
+       bool legacy;
+       char addr[18];
+
+       if (length < sizeof(*ev)) {
+               error("Too short device found event (%u bytes)", length);
+               return;
+       }
+
+       eir_len = btohs(ev->eir_len);
+       if (length != sizeof(*ev) + eir_len) {
+               error("Device found event size mismatch (%u != %zu)",
+                                       length, sizeof(*ev) + eir_len);
+               return;
+       }
+
+       if (eir_len == 0)
+               eir = NULL;
+       else
+               eir = ev->eir;
+
+       flags = btohl(ev->flags);
+
+       ba2str(&ev->addr.bdaddr, addr);
+       /*DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u",
+                       index, addr, ev->rssi, flags, eir_len);*/
+
+       confirm_name = (flags & MGMT_DEV_FOUND_CONFIRM_NAME);
+       legacy = (flags & MGMT_DEV_FOUND_LEGACY_PAIRING);
+
+       /*DBG("hci%u addr %s, addr_type %d rssi %d flags 0x%04x eir_len %u confirm_name %d legacy %d, adv_type %02x",
+                       index, addr, ev->addr.type, ev->rssi, flags, eir_len, confirm_name, legacy, ev->adv_type);*/
+
+       update_found_devices(adapter, &ev->addr.bdaddr, ev->addr.type,
+                                       ev->rssi, confirm_name, legacy, ev->adv_type,
+                                       eir, eir_len);
 }
+#endif
 
 struct agent *adapter_get_agent(struct btd_adapter *adapter)
 {
@@ -5792,7 +10289,11 @@ static void adapter_remove_connection(struct btd_adapter *adapter,
 
        device_remove_connection(device, bdaddr_type);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (device_is_authenticating(device, bdaddr_type))
+#else
        if (device_is_authenticating(device))
+#endif
                device_cancel_authentication(device, TRUE);
 
        /* If another bearer is still connected */
@@ -5800,12 +10301,31 @@ static void adapter_remove_connection(struct btd_adapter *adapter,
                return;
 
        adapter->connections = g_slist_remove(adapter->connections, device);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       DBG("isPaired %d, isBonded %d", device_is_paired(device, bdaddr_type),
+                       device_is_bonded(device, bdaddr_type));
+       if ((device_is_temporary(device) && !device_is_retrying(device)) ||
+                       (!device_is_bonded(device, bdaddr_type))) {
+#else
        if (device_is_temporary(device) && !device_is_retrying(device)) {
+#endif
                const char *path = device_get_path(device);
 
                DBG("Removing temporary device %s", path);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               /* device_is_paired is added incase of tempoary bonded
+                * oop file transfer in that device is not bonded it's paired.
+                */
+               if (!(device_is_bonded(device, bdaddr_type) ||
+                               device_is_paired(device, bdaddr_type))) {
+                       DBG("addr type %d, bonded", bdaddr_type);
+                       return;
+               }
+
+               btd_adapter_unpair_device(adapter, device);
+#else
                btd_adapter_remove_device(adapter, device);
+#endif
        }
 }
 
@@ -5858,6 +10378,10 @@ static void adapter_stop(struct btd_adapter *adapter)
 
        g_dbus_emit_property_changed(dbus_conn, adapter->path,
                                        ADAPTER_INTERFACE, "Discovering");
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                                               ADAPTER_INTERFACE, "LEDiscovering");
+#endif
 
        if (adapter->dev_class) {
                /* the kernel should reset the class of device when powering
@@ -5867,6 +10391,9 @@ static void adapter_stop(struct btd_adapter *adapter)
                                                ADAPTER_INTERFACE, "Class");
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       advertiser_cleanup(adapter);
+#endif
        g_dbus_emit_property_changed(dbus_conn, adapter->path,
                                                ADAPTER_INTERFACE, "Powered");
 
@@ -5949,14 +10476,31 @@ static gboolean process_auth_queue(gpointer user_data)
                const char *dev_path;
 
                /* Wait services to be resolved before asking authorization */
-               if (auth->svc_id > 0)
+               if (auth->svc_id > 0) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                       DBG("Wait services to be resolved before asking authorization");
+#endif
                        return FALSE;
+               }
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                if (device_is_trusted(device) == TRUE) {
+#else
+               if (device_is_trusted(device) == TRUE ||
+                               device_is_profile_trusted(device, auth->uuid)) {
+#endif
                        auth->cb(NULL, auth->user_data);
                        goto next;
                }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               /* If Profile is Blocked, Simply reject Authorization*/
+               if (device_is_profile_blocked(device, auth->uuid) == TRUE) {
+                       auth->cb(&err, auth->user_data);
+                       goto next;
+               }
+#endif
+
                /* If agent is set authorization is already ongoing */
                if (auth->agent)
                        return FALSE;
@@ -6218,210 +10762,558 @@ int btd_adapter_pincode_reply(struct btd_adapter *adapter,
                bacpy(&cp.addr.bdaddr, bdaddr);
                cp.addr.type = BDADDR_BREDR;
 
-               id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_NEG_REPLY,
-                                       adapter->dev_id, sizeof(cp), &cp,
-                                       NULL, NULL, NULL);
-       } else {
-               struct mgmt_cp_pin_code_reply cp;
+               id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_NEG_REPLY,
+                                       adapter->dev_id, sizeof(cp), &cp,
+                                       NULL, NULL, NULL);
+       } else {
+               struct mgmt_cp_pin_code_reply cp;
+
+               if (pin_len > 16)
+                       return -EINVAL;
+
+               memset(&cp, 0, sizeof(cp));
+               bacpy(&cp.addr.bdaddr, bdaddr);
+               cp.addr.type = BDADDR_BREDR;
+               cp.pin_len = pin_len;
+               memcpy(cp.pin_code, pin, pin_len);
+
+               /* Since a pincode was requested, update the starting time to
+                * the point where the pincode is provided. */
+               device = btd_adapter_find_device(adapter, bdaddr, BDADDR_BREDR);
+               device_bonding_restart_timer(device);
+
+               id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_REPLY,
+                                       adapter->dev_id, sizeof(cp), &cp,
+                                       pincode_reply_complete, device, NULL);
+       }
+
+       if (id == 0)
+               return -EIO;
+
+       return 0;
+}
+
+int btd_adapter_confirm_reply(struct btd_adapter *adapter,
+                               const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+                               gboolean success)
+{
+       struct mgmt_cp_user_confirm_reply cp;
+       uint16_t opcode;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%u addr %s success %d", adapter->dev_id, addr, success);
+
+       if (success)
+               opcode = MGMT_OP_USER_CONFIRM_REPLY;
+       else
+               opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY;
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.addr.bdaddr, bdaddr);
+       cp.addr.type = bdaddr_type;
+
+       if (mgmt_reply(adapter->mgmt, opcode, adapter->dev_id, sizeof(cp), &cp,
+                                                       NULL, NULL, NULL) > 0)
+               return 0;
+
+       return -EIO;
+}
+
+static void user_confirm_request_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_ev_user_confirm_request *ev = param;
+       struct btd_adapter *adapter = user_data;
+       struct btd_device *device;
+       char addr[18];
+       int err;
+
+       if (length < sizeof(*ev)) {
+               btd_error(adapter->dev_id,
+                               "Too small user confirm request event");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, addr);
+       DBG("hci%u %s confirm_hint %u", adapter->dev_id, addr,
+                                                       ev->confirm_hint);
+       device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+                                                               ev->addr.type);
+       if (!device) {
+               btd_error(adapter->dev_id,
+                               "Unable to get device object for %s", addr);
+               return;
+       }
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       device_set_auth_addr_type(device, ev->addr.type);
+#endif
+       err = device_confirm_passkey(device, ev->addr.type, btohl(ev->value),
+                                                       ev->confirm_hint);
+       if (err < 0) {
+               btd_error(adapter->dev_id,
+                               "device_confirm_passkey: %s", strerror(-err));
+               btd_adapter_confirm_reply(adapter, &ev->addr.bdaddr,
+                                                       ev->addr.type, FALSE);
+       }
+}
+
+int btd_adapter_passkey_reply(struct btd_adapter *adapter,
+                               const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+                               uint32_t passkey)
+{
+       unsigned int id;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%u addr %s passkey %06u", adapter->dev_id, addr, passkey);
+
+       if (passkey == INVALID_PASSKEY) {
+               struct mgmt_cp_user_passkey_neg_reply cp;
+
+               memset(&cp, 0, sizeof(cp));
+               bacpy(&cp.addr.bdaddr, bdaddr);
+               cp.addr.type = bdaddr_type;
+
+               id = mgmt_reply(adapter->mgmt, MGMT_OP_USER_PASSKEY_NEG_REPLY,
+                                       adapter->dev_id, sizeof(cp), &cp,
+                                       NULL, NULL, NULL);
+       } else {
+               struct mgmt_cp_user_passkey_reply cp;
+
+               memset(&cp, 0, sizeof(cp));
+               bacpy(&cp.addr.bdaddr, bdaddr);
+               cp.addr.type = bdaddr_type;
+               cp.passkey = htobl(passkey);
+
+               id = mgmt_reply(adapter->mgmt, MGMT_OP_USER_PASSKEY_REPLY,
+                                       adapter->dev_id, sizeof(cp), &cp,
+                                       NULL, NULL, NULL);
+       }
+
+       if (id == 0)
+               return -EIO;
+
+       return 0;
+}
+
+static void user_passkey_request_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_ev_user_passkey_request *ev = param;
+       struct btd_adapter *adapter = user_data;
+       struct btd_device *device;
+       char addr[18];
+       int err;
+
+       if (length < sizeof(*ev)) {
+               btd_error(adapter->dev_id, "Too small passkey request event");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, addr);
+       DBG("hci%u %s", index, addr);
+
+       device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+                                                               ev->addr.type);
+       if (!device) {
+               btd_error(adapter->dev_id,
+                               "Unable to get device object for %s", addr);
+               return;
+       }
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       device_set_auth_addr_type(device, ev->addr.type);
+#endif
+       err = device_request_passkey(device, ev->addr.type);
+       if (err < 0) {
+               btd_error(adapter->dev_id,
+                               "device_request_passkey: %s", strerror(-err));
+               btd_adapter_passkey_reply(adapter, &ev->addr.bdaddr,
+                                       ev->addr.type, INVALID_PASSKEY);
+       }
+}
+
+static void user_passkey_notify_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_ev_passkey_notify *ev = param;
+       struct btd_adapter *adapter = user_data;
+       struct btd_device *device;
+       uint32_t passkey;
+       char addr[18];
+       int err;
+
+       if (length < sizeof(*ev)) {
+               btd_error(adapter->dev_id, "Too small passkey notify event");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, addr);
+       DBG("hci%u %s", index, addr);
+
+       device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+                                                               ev->addr.type);
+       if (!device) {
+               btd_error(adapter->dev_id,
+                               "Unable to get device object for %s", addr);
+               return;
+       }
+
+       passkey = get_le32(&ev->passkey);
+
+       DBG("passkey %06u entered %u", passkey, ev->entered);
+
+       err = device_notify_passkey(device, ev->addr.type, passkey,
+                                                               ev->entered);
+       if (err < 0)
+               btd_error(adapter->dev_id,
+                               "device_notify_passkey: %s", strerror(-err));
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void rssi_alert_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_ev_vendor_specific_rssi_alert *ev = param;
+       struct btd_adapter *adapter = user_data;
+       char addr[18];
+       char *bt_addr =  NULL;
+       int link_type = -1;
+       int alert_type = -1;
+       int rssi_dbm = 0;
+
+       if (length < sizeof(*ev)) {
+               error("Too small rssi alert event");
+               return;
+       }
+
+       ba2str(&ev->bdaddr, addr);
+       DBG("hci%u %s %d", index, addr, ev->link_type);
+       DBG("RSSI Alert Params [%d %d]", ev->alert_type, ev->rssi_dbm);
+
+       bt_addr = (char *)&addr;
+       link_type = ev->link_type;
+       alert_type = ev->alert_type;
+       rssi_dbm = ev->rssi_dbm;
+       g_dbus_emit_signal(dbus_conn, adapter->path,
+                       ADAPTER_INTERFACE, "RssiAlert",
+                       DBUS_TYPE_STRING, &bt_addr,
+                       DBUS_TYPE_INT32, &link_type,
+                       DBUS_TYPE_INT32, &alert_type,
+                       DBUS_TYPE_INT32, &rssi_dbm,
+                       DBUS_TYPE_INVALID);
+}
+
+static void get_raw_rssi_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_cc_rp_get_raw_rssi *ev = param;
+       struct btd_adapter *adapter = user_data;
+       char addr[18];
+       char *bt_addr =  NULL;
+       int link_type = -1;
+       int rssi_dbm = 0;
+
+       if (length < sizeof(*ev)) {
+               error("Too small raw RSSI event");
+               return;
+       }
+
+       ba2str(&ev->bt_address, addr);
+       DBG("hci%u %s", index, addr);
+       DBG("Raw RSSI Params [%d %d]", ev->link_type, ev->rssi_dbm);
+
+       bt_addr = (char *)&addr;
+       link_type = ev->link_type;
+       rssi_dbm = ev->rssi_dbm;
+
+       g_dbus_emit_signal(dbus_conn, adapter->path,
+                       ADAPTER_INTERFACE, "RawRssi",
+                       DBUS_TYPE_STRING, &bt_addr,
+                       DBUS_TYPE_INT32, &link_type,
+                       DBUS_TYPE_INT32, &rssi_dbm,
+                       DBUS_TYPE_INVALID);
+}
+
+static void rssi_enabled_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_cc_rsp_enable_rssi *ev = param;
+       struct btd_adapter *adapter = user_data;
+       char addr[18];
+       char *bt_addr =  NULL;
+       int enabled = TRUE;
+       int link_type = -1;
+
+       if (length < sizeof(*ev)) {
+               error("Too small rssi enabled event");
+               return;
+       }
+
+       ba2str(&ev->bt_address, addr);
+       DBG("hci%u %s %d", index, addr, ev->link_type);
+       DBG("RSSI Enabled [%d %d]", ev->le_ext_opcode, ev->status);
+
+       bt_addr = (char *)&addr;
+       link_type = ev->link_type;
+
+       g_dbus_emit_signal(dbus_conn, adapter->path,
+                       ADAPTER_INTERFACE, "RssiEnabled",
+                       DBUS_TYPE_STRING, &bt_addr,
+                       DBUS_TYPE_INT32, &link_type,
+                       DBUS_TYPE_BOOLEAN, &enabled,
+                       DBUS_TYPE_INVALID);
+}
+
+static void rssi_disabled_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_cc_rp_disable_rssi *ev = param;
+       struct btd_adapter *adapter = user_data;
+       char addr[18];
+       char *bt_addr =  NULL;
+       int disabled = FALSE;
+       int link_type = -1;
+
+       if (length < sizeof(*ev)) {
+               error("Too small RSSI disabled event");
+               return;
+       }
+
+       ba2str(&ev->bt_address, addr);
+       DBG("hci%u %s %d", index, addr, ev->link_type);
+       DBG("RSSI Disabled Params [%d %d]", ev->le_ext_opcode, ev->status);
 
-               if (pin_len > 16)
-                       return -EINVAL;
+       bt_addr = (char *)&addr;
+       link_type = ev->link_type;
 
-               memset(&cp, 0, sizeof(cp));
-               bacpy(&cp.addr.bdaddr, bdaddr);
-               cp.addr.type = BDADDR_BREDR;
-               cp.pin_len = pin_len;
-               memcpy(cp.pin_code, pin, pin_len);
+       g_dbus_emit_signal(dbus_conn, adapter->path,
+                       ADAPTER_INTERFACE, "RssiEnabled",
+                       DBUS_TYPE_STRING, &bt_addr,
+                       DBUS_TYPE_INT32, &link_type,
+                       DBUS_TYPE_BOOLEAN, &disabled,
+                       DBUS_TYPE_INVALID);
+}
 
-               /* Since a pincode was requested, update the starting time to
-                * the point where the pincode is provided. */
-               device = btd_adapter_find_device(adapter, bdaddr, BDADDR_BREDR);
-               device_bonding_restart_timer(device);
+void adapter_check_version(struct btd_adapter *adapter, uint8_t hci_ver)
+{
+       char *ver;
 
-               id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_REPLY,
-                                       adapter->dev_id, sizeof(cp), &cp,
-                                       pincode_reply_complete, device, NULL);
+       switch (hci_ver) {
+       case 0:
+               ver = "Bluetooth 1.0b";
+               break;
+       case 1:
+               ver = "Bluetooth 1.1";
+               break;
+       case 2:
+               ver = "Bluetooth 1.2";
+               break;
+       case 3:
+               ver = "Bluetooth 2.0 + EDR";
+               break;
+       case 4:
+               ver = "Bluetooth 2.1 + EDR";
+               break;
+       case 5:
+               ver = "Bluetooth 3.0 + HS";
+               break;
+       case 6:
+               ver = "Bluetooth 4.0";
+               break;
+       case 7:
+               ver = "Bluetooth 4.1";
+               break;
+       default:
+               ver = "Unknown";
+               break;
        }
 
-       if (id == 0)
-               return -EIO;
+       if (adapter->version)
+               g_free(adapter->version);
 
-       return 0;
+       adapter->version = g_strdup(ver);
 }
 
-int btd_adapter_confirm_reply(struct btd_adapter *adapter,
-                               const bdaddr_t *bdaddr, uint8_t bdaddr_type,
-                               gboolean success)
+static void hardware_error_callback(uint16_t index, uint16_t length,
+               const void *param, void *user_data)
 {
-       struct mgmt_cp_user_confirm_reply cp;
-       uint16_t opcode;
-       char addr[18];
-
-       ba2str(bdaddr, addr);
-       DBG("hci%u addr %s success %d", adapter->dev_id, addr, success);
+       const struct mgmt_ev_hardware_error *ev = param;
+       struct btd_adapter *adapter = user_data;
 
-       if (success)
-               opcode = MGMT_OP_USER_CONFIRM_REPLY;
-       else
-               opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY;
+       if (length < sizeof(*ev)) {
+               error("Too small Hardware error event");
+               return;
+       }
 
-       memset(&cp, 0, sizeof(cp));
-       bacpy(&cp.addr.bdaddr, bdaddr);
-       cp.addr.type = bdaddr_type;
+       error("Hardware error occurred : %d", ev->error_code);
+       g_dbus_emit_signal(dbus_conn, adapter->path,
+                       ADAPTER_INTERFACE, "HardwareError",
+                       DBUS_TYPE_INVALID);
+}
 
-       if (mgmt_reply(adapter->mgmt, opcode, adapter->dev_id, sizeof(cp), &cp,
-                                                       NULL, NULL, NULL) > 0)
-               return 0;
+static void tx_timeout_error_callback(uint16_t index, uint16_t length,
+               const void *param, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
 
-       return -EIO;
+       error("Tx Timeout error occurred");
+       g_dbus_emit_signal(dbus_conn, adapter->path,
+                       ADAPTER_INTERFACE, "TxTimeoutError",
+                       DBUS_TYPE_INVALID);
 }
 
-static void user_confirm_request_callback(uint16_t index, uint16_t length,
-                                       const void *param, void *user_data)
+static void device_name_update_callback(uint16_t index, uint16_t length,
+               const void *param, void *user_data)
 {
-       const struct mgmt_ev_user_confirm_request *ev = param;
+       const struct mgmt_ev_device_name_update *ev = param;
        struct btd_adapter *adapter = user_data;
        struct btd_device *device;
        char addr[18];
-       int err;
+       const uint8_t *eir_name;
+       struct eir_data eir_data;
 
        if (length < sizeof(*ev)) {
-               btd_error(adapter->dev_id,
-                               "Too small user confirm request event");
+               error("Name update error event");
                return;
        }
 
        ba2str(&ev->addr.bdaddr, addr);
-       DBG("hci%u %s confirm_hint %u", adapter->dev_id, addr,
-                                                       ev->confirm_hint);
+       DBG("hci%u %s", index, addr);
+
        device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
                                                                ev->addr.type);
        if (!device) {
-               btd_error(adapter->dev_id,
-                               "Unable to get device object for %s", addr);
+               error("Unable to get device object for %s", addr);
                return;
        }
 
-       err = device_confirm_passkey(device, ev->addr.type, btohl(ev->value),
-                                                       ev->confirm_hint);
-       if (err < 0) {
-               btd_error(adapter->dev_id,
-                               "device_confirm_passkey: %s", strerror(-err));
-               btd_adapter_confirm_reply(adapter, &ev->addr.bdaddr,
-                                                       ev->addr.type, FALSE);
-       }
+       if (ev->eir_len == 0)
+               return;
+
+       eir_name = ev->eir;
+
+       memset(&eir_data, 0, sizeof(eir_data));
+       eir_parse(&eir_data, eir_name, ev->eir_len);
+
+       if (eir_data.name)
+               btd_device_device_set_name(device, eir_data.name);
+
+       eir_data_free(&eir_data);
 }
 
-int btd_adapter_passkey_reply(struct btd_adapter *adapter,
-                               const bdaddr_t *bdaddr, uint8_t bdaddr_type,
-                               uint32_t passkey)
+static void multi_adv_state_change_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
 {
-       unsigned int id;
-       char addr[18];
-
-       ba2str(bdaddr, addr);
-       DBG("hci%u addr %s passkey %06u", adapter->dev_id, addr, passkey);
+       const struct mgmt_ev_vendor_specific_multi_adv_state_changed *ev = param;
+       struct btd_adapter *adapter = user_data;
 
-       if (passkey == INVALID_PASSKEY) {
-               struct mgmt_cp_user_passkey_neg_reply cp;
+       if (length < sizeof(*ev)) {
+               error("Too small adv state change event");
+               return;
+       }
 
-               memset(&cp, 0, sizeof(cp));
-               bacpy(&cp.addr.bdaddr, bdaddr);
-               cp.addr.type = bdaddr_type;
+       DBG("adv id %d, state change reason %d, connection_handle %x",
+               ev->adv_instance, ev->state_change_reason, ev->connection_handle);
 
-               id = mgmt_reply(adapter->mgmt, MGMT_OP_USER_PASSKEY_NEG_REPLY,
-                                       adapter->dev_id, sizeof(cp), &cp,
-                                       NULL, NULL, NULL);
-       } else {
-               struct mgmt_cp_user_passkey_reply cp;
+       if ((ev->adv_instance > 0 && ev->adv_instance < adapter_le_get_max_adv_instance()) &&
+                       ev->state_change_reason == 0)
+               adapter_le_enable_multi_adv(adapter, TRUE, ev->adv_instance);
+}
 
-               memset(&cp, 0, sizeof(cp));
-               bacpy(&cp.addr.bdaddr, bdaddr);
-               cp.addr.type = bdaddr_type;
-               cp.passkey = htobl(passkey);
+static void le_conn_update_completed_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_ev_conn_updated *ev = param;
+       struct btd_adapter *adapter = user_data;
+       struct btd_device *device;
+       char addr[18];
+       GSList *list;
 
-               id = mgmt_reply(adapter->mgmt, MGMT_OP_USER_PASSKEY_REPLY,
-                                       adapter->dev_id, sizeof(cp), &cp,
-                                       NULL, NULL, NULL);
+       if (length < sizeof(*ev)) {
+               error("Too small le conn update completed event");
+               return;
        }
 
-       if (id == 0)
-               return -EIO;
-
-       return 0;
+       ba2str(&ev->addr.bdaddr, addr);
+       list = g_slist_find_custom(adapter->devices, addr,
+                                                       device_address_cmp);
+       if (list) {
+               device = list->data;
+               if (device_get_conn_update_state(device))
+                       device_set_conn_update_state(device, false);
+       }
 }
 
-static void user_passkey_request_callback(uint16_t index, uint16_t length,
+static void bt_6lowpan_conn_state_change_callback(uint16_t index, uint16_t length,
                                        const void *param, void *user_data)
 {
-       const struct mgmt_ev_user_passkey_request *ev = param;
+       const struct mgmt_ev_6lowpan_conn_state_changed *ev = param;
        struct btd_adapter *adapter = user_data;
        struct btd_device *device;
        char addr[18];
-       int err;
+       gboolean connected = 0;
 
        if (length < sizeof(*ev)) {
-               btd_error(adapter->dev_id, "Too small passkey request event");
+               btd_error(adapter->dev_id,
+                       "Too small device connected event");
                return;
        }
 
        ba2str(&ev->addr.bdaddr, addr);
-       DBG("hci%u %s", index, addr);
 
-       device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+       DBG("hci%u device %s", index, addr);
+
+       device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
                                                                ev->addr.type);
        if (!device) {
                btd_error(adapter->dev_id,
-                               "Unable to get device object for %s", addr);
+                       "Unable to get device object for %s", addr);
                return;
        }
 
-       err = device_request_passkey(device, ev->addr.type);
-       if (err < 0) {
-               btd_error(adapter->dev_id,
-                               "device_request_passkey: %s", strerror(-err));
-               btd_adapter_passkey_reply(adapter, &ev->addr.bdaddr,
-                                       ev->addr.type, INVALID_PASSKEY);
-       }
+       if (ev->connected)
+               connected = TRUE;
+       else
+               connected = FALSE;
+
+       device_set_ipsp_connected(device, connected, ev->ifname);
 }
 
-static void user_passkey_notify_callback(uint16_t index, uint16_t length,
+static void bt_le_data_length_changed_callback(uint16_t index, uint16_t length,
                                        const void *param, void *user_data)
 {
-       const struct mgmt_ev_passkey_notify *ev = param;
+       const struct mgmt_ev_le_data_length_changed *ev = param;
        struct btd_adapter *adapter = user_data;
        struct btd_device *device;
-       uint32_t passkey;
        char addr[18];
-       int err;
 
        if (length < sizeof(*ev)) {
-               btd_error(adapter->dev_id, "Too small passkey notify event");
+               btd_error(adapter->dev_id,
+                       "Too small data length changed event");
                return;
        }
 
        ba2str(&ev->addr.bdaddr, addr);
-       DBG("hci%u %s", index, addr);
 
-       device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+       DBG("hci%u device %s", index, addr);
+
+       device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
                                                                ev->addr.type);
        if (!device) {
                btd_error(adapter->dev_id,
-                               "Unable to get device object for %s", addr);
+                       "Unable to get device object for %s", addr);
                return;
        }
 
-       passkey = get_le32(&ev->passkey);
-
-       DBG("passkey %06u entered %u", passkey, ev->entered);
-
-       err = device_notify_passkey(device, ev->addr.type, passkey,
-                                                               ev->entered);
-       if (err < 0)
-               btd_error(adapter->dev_id,
-                               "device_notify_passkey: %s", strerror(-err));
+       device_le_data_length_changed(device, ev->max_tx_octets, ev->max_tx_time,
+               ev->max_rx_octets, ev->max_rx_time);
 }
 
+#endif
+
 struct btd_adapter_pin_cb_iter *btd_adapter_pin_cb_iter_new(
                                                struct btd_adapter *adapter)
 {
@@ -6499,6 +11391,9 @@ static void pin_code_request_callback(uint16_t index, uint16_t length,
 
        /* Flag the request of a pincode to allow a bonding retry. */
        adapter->pincode_requested = true;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       btd_device_set_legacy_pairing(device, true);
+#endif
 
        memset(pin, 0, sizeof(pin));
 
@@ -6586,8 +11481,9 @@ static void bonding_complete(struct btd_adapter *adapter,
        if (device != NULL)
                device_bonding_complete(device, addr_type, status);
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        resume_discovery(adapter);
-
+#endif
        check_oob_bonding_complete(adapter, bdaddr, status);
 }
 
@@ -6704,8 +11600,9 @@ int adapter_create_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
                return -EBUSY;
        }
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        suspend_discovery(adapter);
-
+#endif
        return adapter_bonding_attempt(adapter, bdaddr, addr_type, io_cap);
 }
 
@@ -6775,16 +11672,38 @@ static void dev_disconnected(struct btd_adapter *adapter,
 {
        struct btd_device *device;
        char dst[18];
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct device_addr_type t_addr;
+#endif
 
        ba2str(&addr->bdaddr, dst);
 
        DBG("Device %s disconnected, reason %u", dst, reason);
 
        device = btd_adapter_find_device(adapter, &addr->bdaddr, addr->type);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (device) {
+               device_get_tizen_addr(device, addr->type, &t_addr);
+
+               device_set_disconnect_reason(device, reason);
+               adapter_remove_connection(adapter, device, t_addr.bdaddr_type);
+               disconnect_notify(device, reason);
+               if (device_is_bonded(device, t_addr.bdaddr_type)) {
+                       DBG("addr type %d, bonded", t_addr.bdaddr_type);
+                       return;
+               }
+
+               bonding_attempt_complete(adapter, &t_addr.bdaddr,
+                               t_addr.bdaddr_type, MGMT_STATUS_DISCONNECTED);
+               return;
+       }
+#else
        if (device) {
                adapter_remove_connection(adapter, device, addr->type);
                disconnect_notify(device, reason);
        }
+#endif
 
        bonding_attempt_complete(adapter, &addr->bdaddr, addr->type,
                                                MGMT_STATUS_DISCONNECTED);
@@ -6822,7 +11741,12 @@ static void disconnect_complete(uint8_t status, uint16_t length,
                return;
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* Use HCI error code instead of MGMT disconnection reason */
+       dev_disconnected(adapter, &rp->addr, 0x16);
+#else
        dev_disconnected(adapter, &rp->addr, MGMT_DEV_DISCONN_LOCAL_HOST);
+#endif
 }
 
 int btd_adapter_disconnect_device(struct btd_adapter *adapter,
@@ -6938,7 +11862,16 @@ static void new_link_key_callback(uint16_t index, uint16_t length,
                                                                key->pin_len);
 
                device_set_bonded(device, BDADDR_BREDR);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       } else {
+               if (btd_adapter_get_a2dp_role(adapter) == BLUETOOTH_A2DP_SINK_ROLE) {
+                       DBG("store_hint %d", ev->store_hint);
+                       btd_device_set_temporary(device, false);
+               }
+       }
+#else
        }
+#endif
 
        bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
 }
@@ -7005,6 +11938,9 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
        struct btd_device *device;
        bool persistent;
        char dst[18];
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct device_addr_type t_addr;
+#endif
 
        if (length < sizeof(*ev)) {
                btd_error(adapter->dev_id, "Too small long term key event");
@@ -7049,9 +11985,16 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
                ediv = le16_to_cpu(key->ediv);
                rand = le64_to_cpu(key->rand);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               device_get_tizen_addr(device, addr->type, &t_addr);
+               store_longtermkey(bdaddr, &t_addr.bdaddr, t_addr.bdaddr_type,
+                                       key->val, key->master, key->type,
+                                       key->enc_size, ediv, rand);
+#else
                store_longtermkey(bdaddr, &key->addr.bdaddr,
                                        key->addr.type, key->val, key->master,
                                        key->type, key->enc_size, ediv, rand);
+#endif
 
                device_set_bonded(device, addr->type);
        }
@@ -7131,6 +12074,9 @@ static void new_csrk_callback(uint16_t index, uint16_t length,
        const bdaddr_t *bdaddr = btd_adapter_get_address(adapter);
        struct btd_device *device;
        char dst[18];
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct device_addr_type t_addr;
+#endif
 
        if (length < sizeof(*ev)) {
                btd_error(adapter->dev_id, "Too small CSRK event");
@@ -7152,8 +12098,14 @@ static void new_csrk_callback(uint16_t index, uint16_t length,
        if (!ev->store_hint)
                return;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       device_get_tizen_addr(device, addr->type, &t_addr);
+       store_csrk(bdaddr, &t_addr.bdaddr, t_addr.bdaddr_type, key->val, 0,
+                                                               key->type);
+#else
        store_csrk(bdaddr, &key->addr.bdaddr, key->addr.type, key->val, 0,
                                                                key->type);
+#endif
 
        btd_device_set_temporary(device, false);
 }
@@ -7202,6 +12154,9 @@ static void new_irk_callback(uint16_t index, uint16_t length,
        struct btd_device *device, *duplicate;
        bool persistent;
        char dst[18], rpa[18];
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct device_addr_type t_addr;
+#endif
 
        if (length < sizeof(*ev)) {
                btd_error(adapter->dev_id, "Too small New IRK event");
@@ -7215,11 +12170,16 @@ static void new_irk_callback(uint16_t index, uint16_t length,
 
        if (bacmp(&ev->rpa, BDADDR_ANY)) {
                device = btd_adapter_get_device(adapter, &ev->rpa,
-                                                       BDADDR_LE_RANDOM);
+                               BDADDR_LE_RANDOM);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                duplicate = btd_adapter_find_device(adapter, &addr->bdaddr,
-                                                               addr->type);
+                               addr->type);
                if (duplicate == device)
                        duplicate = NULL;
+#else
+               device_set_rpa(device, &ev->rpa);
+               duplicate = NULL;
+#endif
        } else {
                device = btd_adapter_get_device(adapter, &addr->bdaddr,
                                                                addr->type);
@@ -7237,11 +12197,20 @@ static void new_irk_callback(uint16_t index, uint16_t length,
        if (duplicate)
                device_merge_duplicate(device, duplicate);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       device_set_irk_value(device, irk->val);
+#endif
+
        persistent = !!ev->store_hint;
        if (!persistent)
                return;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       device_get_tizen_addr(device, addr->type, &t_addr);
+       store_irk(adapter, &t_addr.bdaddr, t_addr.bdaddr_type, irk->val);
+#else
        store_irk(adapter, &addr->bdaddr, addr->type, irk->val);
+#endif
 
        btd_device_set_temporary(device, false);
 }
@@ -7294,7 +12263,9 @@ static void new_conn_param(uint16_t index, uint16_t length,
        uint16_t min, max, latency, timeout;
        struct btd_device *dev;
        char dst[18];
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct device_addr_type t_addr;
+#endif
 
        if (length < sizeof(*ev)) {
                btd_error(adapter->dev_id,
@@ -7322,19 +12293,71 @@ static void new_conn_param(uint16_t index, uint16_t length,
        if (!ev->store_hint)
                return;
 
-       store_conn_param(adapter, &ev->addr.bdaddr, ev->addr.type,
-                                       ev->min_interval, ev->max_interval,
-                                       ev->latency, ev->timeout);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       device_get_tizen_addr(dev, ev->addr.type, &t_addr);
+       store_conn_param(adapter, &t_addr.bdaddr, t_addr.bdaddr_type,
+                                       ev->min_interval, ev->max_interval,
+                                       ev->latency, ev->timeout);
+#else
+       store_conn_param(adapter, &ev->addr.bdaddr, ev->addr.type,
+                                       ev->min_interval, ev->max_interval,
+                                       ev->latency, ev->timeout);
+#endif
+}
+
+int adapter_set_io_capability(struct btd_adapter *adapter, uint8_t io_cap)
+{
+       struct mgmt_cp_set_io_capability cp;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.io_capability = io_cap;
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_SET_IO_CAPABILITY,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               NULL, NULL, NULL) > 0)
+               return 0;
+
+       return -EIO;
+}
+
+int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
+                                       const bdaddr_t *bdaddr,
+                                       uint8_t *hash, uint8_t *randomizer)
+{
+       struct mgmt_cp_add_remote_oob_data cp;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d bdaddr %s", adapter->dev_id, addr);
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.addr.bdaddr, bdaddr);
+       memcpy(cp.hash192, hash, 16);
+
+       if (randomizer)
+               memcpy(cp.rand192, randomizer, 16);
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_ADD_REMOTE_OOB_DATA,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               NULL, NULL, NULL) > 0)
+               return 0;
+
+       return -EIO;
 }
 
-int adapter_set_io_capability(struct btd_adapter *adapter, uint8_t io_cap)
+int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
+                                                       const bdaddr_t *bdaddr)
 {
-       struct mgmt_cp_set_io_capability cp;
+       struct mgmt_cp_remove_remote_oob_data cp;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d bdaddr %s", adapter->dev_id, addr);
 
        memset(&cp, 0, sizeof(cp));
-       cp.io_capability = io_cap;
+       bacpy(&cp.addr.bdaddr, bdaddr);
 
-       if (mgmt_send(adapter->mgmt, MGMT_OP_SET_IO_CAPABILITY,
+       if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
                                adapter->dev_id, sizeof(cp), &cp,
                                NULL, NULL, NULL) > 0)
                return 0;
@@ -7342,22 +12365,31 @@ int adapter_set_io_capability(struct btd_adapter *adapter, uint8_t io_cap)
        return -EIO;
 }
 
-int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
-                                       const bdaddr_t *bdaddr,
-                                       uint8_t *hash, uint8_t *randomizer)
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int btd_adapter_add_remote_oob_ext_data(struct btd_adapter *adapter,
+                               const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+                               uint8_t *hash192, uint8_t *randomizer192,
+                               uint8_t *hash256, uint8_t *randomizer256)
 {
        struct mgmt_cp_add_remote_oob_data cp;
        char addr[18];
 
        ba2str(bdaddr, addr);
-       DBG("hci%d bdaddr %s", adapter->dev_id, addr);
+       DBG("hci%d bdaddr %s type %d", adapter->dev_id, addr, bdaddr_type);
 
        memset(&cp, 0, sizeof(cp));
        bacpy(&cp.addr.bdaddr, bdaddr);
-       memcpy(cp.hash192, hash, 16);
+       cp.addr.type = bdaddr_type;
 
-       if (randomizer)
-               memcpy(cp.rand192, randomizer, 16);
+       if (hash192 && randomizer192) {
+               memcpy(cp.hash192, hash192, 16);
+               memcpy(cp.rand192, randomizer192, 16);
+       }
+
+       if (hash256 && randomizer256) {
+               memcpy(cp.hash256, hash256, 16);
+               memcpy(cp.rand256, randomizer256, 16);
+       }
 
        if (mgmt_send(adapter->mgmt, MGMT_OP_ADD_REMOTE_OOB_DATA,
                                adapter->dev_id, sizeof(cp), &cp,
@@ -7367,17 +12399,18 @@ int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
        return -EIO;
 }
 
-int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
-                                                       const bdaddr_t *bdaddr)
+int btd_adapter_remove_remote_oob_ext_data(struct btd_adapter *adapter,
+                               const bdaddr_t *bdaddr, uint8_t bdaddr_type)
 {
        struct mgmt_cp_remove_remote_oob_data cp;
        char addr[18];
 
        ba2str(bdaddr, addr);
-       DBG("hci%d bdaddr %s", adapter->dev_id, addr);
+       DBG("hci%d bdaddr %s type %d", adapter->dev_id, addr, bdaddr_type);
 
        memset(&cp, 0, sizeof(cp));
        bacpy(&cp.addr.bdaddr, bdaddr);
+       cp.addr.type = bdaddr_type;
 
        if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
                                adapter->dev_id, sizeof(cp), &cp,
@@ -7386,6 +12419,7 @@ int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
 
        return -EIO;
 }
+#endif
 
 bool btd_adapter_ssp_enabled(struct btd_adapter *adapter)
 {
@@ -7412,6 +12446,10 @@ static void read_local_oob_data_complete(uint8_t status, uint16_t length,
        const struct mgmt_rp_read_local_oob_data *rp = param;
        struct btd_adapter *adapter = user_data;
        const uint8_t *hash, *randomizer;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       const uint8_t *hash256 = NULL;
+       const uint8_t *randomizer256 = NULL;
+#endif
 
        if (status != MGMT_STATUS_SUCCESS) {
                btd_error(adapter->dev_id,
@@ -7419,20 +12457,36 @@ static void read_local_oob_data_complete(uint8_t status, uint16_t length,
                                                mgmt_errstr(status), status);
                hash = NULL;
                randomizer = NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       } else if (length < 32) {
+#else
        } else if (length < sizeof(*rp)) {
+#endif
                btd_error(adapter->dev_id,
                                "Too small read local OOB data response");
                return;
        } else {
                hash = rp->hash192;
                randomizer = rp->rand192;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               if (length > 32) {
+                       hash256 = rp->hash256;
+                       randomizer256 = rp->rand256;
+               }
+#endif
        }
 
        if (!adapter->oob_handler || !adapter->oob_handler->read_local_cb)
                return;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       adapter->oob_handler->read_local_cb(adapter, hash, randomizer,
+                                       hash256, randomizer256,
+                                       adapter->oob_handler->user_data);
+#else
        adapter->oob_handler->read_local_cb(adapter, hash, randomizer,
                                        adapter->oob_handler->user_data);
+#endif
 
        g_free(adapter->oob_handler);
        adapter->oob_handler = NULL;
@@ -7544,7 +12598,11 @@ static int adapter_register(struct btd_adapter *adapter)
 
        if (!g_dbus_register_interface(dbus_conn,
                                        adapter->path, ADAPTER_INTERFACE,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                                       adapter_methods, adapter_signals,
+#else
                                        adapter_methods, NULL,
+#endif
                                        adapter_properties, adapter,
                                        adapter_free)) {
                btd_error(adapter->dev_id,
@@ -7598,6 +12656,30 @@ static int adapter_register(struct btd_adapter *adapter)
        clear_blocked(adapter);
        load_devices(adapter);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       adapter_print_devices(adapter);
+
+       if (load_local_irk(adapter)) {
+               if (!(adapter->supported_settings & MGMT_SETTING_PRIVACY))
+                       main_opts.le_privacy = false;
+
+               /*
+                * Some Android devices don't consider the device as LE one,
+                * if the device doesn't distribute IRK when pairing.
+                * Because of this compatibility issue, set IRK
+                * even though privacy feature is disabled.
+                */
+               set_local_irk(adapter);
+
+               if (main_opts.le_privacy) {
+                       DBG("Enable LE Privacy feature");
+                       set_privacy(adapter, true);
+               } else {
+                       DBG("Disable LE Privacy feature");
+               }
+       }
+#endif
+
        /* retrieve the active connections: address the scenario where
         * the are active connections before the daemon've started */
        if (adapter->current_settings & MGMT_SETTING_POWERED)
@@ -7825,11 +12907,13 @@ static void connect_failed_callback(uint16_t index, uint16_t length,
                device_bonding_failed(device, ev->status);
        }
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        /* In the case the bonding was canceled or did exists, remove the device
         * when it is temporary. */
        if (device && !device_is_bonding(device, NULL)
                                                && device_is_temporary(device))
                btd_adapter_remove_device(adapter, device);
+#endif
 }
 
 static void remove_keys(struct btd_adapter *adapter,
@@ -7845,6 +12929,11 @@ static void remove_keys(struct btd_adapter *adapter,
        ba2str(btd_adapter_get_address(adapter), adapter_addr);
        ba2str(device_get_address(device), device_addr);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (device_get_rpa_exist(device) == true)
+               ba2str(device_get_rpa(device), device_addr);
+#endif
+
        snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
                                                                device_addr);
        key_file = g_key_file_new();
@@ -7895,6 +12984,264 @@ static void unpaired_callback(uint16_t index, uint16_t length,
        device_set_unpaired(device, ev->addr.type);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static uint8_t *generate_irk(void)
+{
+       int fd;
+       uint8_t *irk;
+
+       DBG("Generate IRK");
+
+       fd = open("/dev/urandom", O_RDONLY);
+       if (fd < 0)
+               return NULL;
+
+       irk = g_malloc0(MGMT_IRK_SIZE);
+       if (read(fd, irk, MGMT_IRK_SIZE) != MGMT_IRK_SIZE) {
+               error("Cannot read random bytes");
+               g_free(irk);
+               close(fd);
+               return NULL;
+       }
+       close(fd);
+
+       return irk;
+}
+
+#define LOCAL_IRK_DIRNAME      "/csa/bluetooth"
+#define LOCAL_IRK_FILENAME     ".local_irk"
+
+static bool store_local_irk(struct btd_adapter *adapter)
+{
+       int fd;
+       int ret;
+
+       if (adapter->local_irk == NULL) {
+               error("Local IRK is not proper");
+               return false;
+       }
+
+       if (access(LOCAL_IRK_DIRNAME, F_OK) < 0) {
+               if (mkdir(LOCAL_IRK_DIRNAME, 0755) < 0) {
+                       error("Cannot create a directory for local IRK : %s",
+                                       strerror(errno));
+                       return false;
+               }
+       }
+
+       fd = open(LOCAL_IRK_DIRNAME"/"LOCAL_IRK_FILENAME,
+                       O_WRONLY | O_CREAT | O_TRUNC, 0644);
+       if (fd < 0) {
+               error("Cannot open a file for local IRK : %s", strerror(errno));
+               return false;
+       }
+
+       ret = write(fd, adapter->local_irk, MGMT_IRK_SIZE);
+       if (ret != MGMT_IRK_SIZE) {
+               error("Cannot write local IRK [%d] : %s", ret, strerror(errno));
+
+               close(fd);
+               unlink(LOCAL_IRK_DIRNAME"/"LOCAL_IRK_FILENAME);
+               return false;
+       }
+
+       ret = fdatasync(fd);
+       if (ret < 0)
+               error("sync failed : %s", strerror(errno));
+
+       close(fd);
+       return true;
+}
+
+static bool load_local_irk(struct btd_adapter *adapter)
+{
+       int fd;
+       int ret;
+
+       if (access(LOCAL_IRK_DIRNAME"/"LOCAL_IRK_FILENAME, F_OK) < 0) {
+               adapter->local_irk = generate_irk();
+               if (store_local_irk(adapter) == false) {
+                       error("Cannot store Local IRK");
+                       g_free(adapter->local_irk);
+                       return false;
+               }
+
+               return true;
+       }
+
+       if (adapter->local_irk) {
+               DBG("Local IRK is already loaded");
+               return true;
+       }
+
+       fd = open(LOCAL_IRK_DIRNAME"/"LOCAL_IRK_FILENAME, O_RDONLY);
+       if (fd < 0) {
+               error("Cannot open local IRK file : %s", strerror(errno));
+               return false;
+       }
+
+       adapter->local_irk = g_malloc0(MGMT_IRK_SIZE);
+
+       ret = read(fd, adapter->local_irk, MGMT_IRK_SIZE);
+       if (ret != MGMT_IRK_SIZE) {
+               error("Cannot read local IRK [%d] : %s", ret, strerror(errno));
+               g_free(adapter->local_irk);
+               close(fd);
+               return false;
+       }
+
+       close(fd);
+       return true;
+}
+
+static void set_privacy_complete(uint8_t status, uint16_t length,
+       const void *param, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       if (status != MGMT_STATUS_SUCCESS)
+               error("Setting privacy failed for hci%u: %s (0x%02x)",
+                       adapter->dev_id, mgmt_errstr(status), status);
+       else
+               DBG("Privacy feature is set/unset successfully for hci%u",
+                       adapter->dev_id);
+}
+
+static bool set_privacy(struct btd_adapter *adapter, bool privacy)
+{
+       struct mgmt_cp_set_privacy cp;
+
+       if (!adapter->local_irk) {
+               error("Local IRK is not available");
+               return false;
+       }
+
+       memset(&cp, 0, sizeof(cp));
+       memcpy(cp.irk, adapter->local_irk, MGMT_IRK_SIZE);
+
+       if (privacy)
+               cp.privacy = 0x01;
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_SET_PRIVACY,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               set_privacy_complete, adapter, NULL) > 0)
+               return true;
+
+       error("Failed to set privacy and load local irk for index %u",
+                       adapter->dev_id);
+       return false;
+}
+
+static void set_irk_complete(uint8_t status, uint16_t length,
+               const void *param, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       if (status != MGMT_STATUS_SUCCESS)
+               error("Setting IRK is failed for hci%u: %s (0x%02x)",
+                       adapter->dev_id, mgmt_errstr(status), status);
+       else
+               DBG("Setting IRK is succeed for hci%u", adapter->dev_id);
+}
+
+static bool set_local_irk(struct btd_adapter *adapter)
+{
+       struct mgmt_cp_set_irk cp;
+
+       if (!adapter->local_irk) {
+               error("Local IRK is not available");
+               return false;
+       }
+
+       memcpy(cp.irk, adapter->local_irk, MGMT_IRK_SIZE);
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_SET_IRK,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               set_irk_complete, adapter, NULL) > 0)
+               return true;
+
+       error("Failed to set irk %u", adapter->dev_id);
+       return false;
+}
+
+int btd_adapter_connect_ipsp(struct btd_adapter *adapter,
+                                               const bdaddr_t *bdaddr,
+                                               uint8_t bdaddr_type)
+
+{
+       struct mgmt_cp_connect_6lowpan cp;
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.addr.bdaddr, bdaddr);
+       cp.addr.type = bdaddr_type;
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_CONNECT_6LOWPAN,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               NULL, NULL, NULL) > 0)
+               return 0;
+
+       return -EIO;
+}
+
+int btd_adapter_disconnect_ipsp(struct btd_adapter *adapter,
+                                               const bdaddr_t *bdaddr,
+                                               uint8_t bdaddr_type)
+
+{
+       struct mgmt_cp_disconnect_6lowpan cp;
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.addr.bdaddr, bdaddr);
+       cp.addr.type = bdaddr_type;
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_DISCONNECT_6LOWPAN,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               NULL, NULL, NULL) > 0)
+               return 0;
+
+       return -EIO;
+}
+
+uint8_t btd_adapter_get_rpa_res_support_value(
+               struct btd_adapter *adapter)
+{
+       return adapter->central_rpa_res_support;
+}
+
+static void set_dev_rpa_res_support_complete(uint8_t status,
+                                       uint16_t length, const void *param,
+                                       void *user_data)
+{
+       if (status != MGMT_STATUS_SUCCESS)
+               error("Failed to set RPA resolution support of device : %s (0x%02x)",
+                                               mgmt_errstr(status), status);
+       else
+               DBG("Set RPA resolution support successful");
+}
+
+int btd_adapter_set_dev_rpa_res_support(struct btd_adapter *adapter,
+                                       struct btd_device *device)
+
+{
+       struct mgmt_cp_set_dev_rpa_res_support cp;
+
+       DBG("btd_adapter_set_dev_rpa_res_support called");
+
+       memset(&cp, 0, sizeof(cp));
+
+       bacpy(&cp.addr.bdaddr, device_get_address(device));
+       cp.addr.type = btd_device_get_bdaddr_type(device);
+       cp.res_support = device_get_rpa_res_char_value(device);
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_SET_DEV_RPA_RES_SUPPORT,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               set_dev_rpa_res_support_complete, NULL, NULL) > 0)
+               return 0;
+
+       return -EIO;
+}
+#endif
+
 static void clear_devices_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
@@ -7927,6 +13274,17 @@ static int clear_devices(struct btd_adapter *adapter)
        return -EIO;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean adapter_start_idle_cb(gpointer user_data)
+{
+       struct btd_adapter *adapter = (struct btd_adapter*)user_data;
+
+       adapter_start(adapter);
+
+       return FALSE;
+}
+#endif
+
 static void read_info_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
@@ -7973,6 +13331,10 @@ static void read_info_complete(uint8_t status, uint16_t length,
        adapter->supported_settings = btohl(rp->supported_settings);
        adapter->current_settings = btohl(rp->current_settings);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       adapter_check_version(adapter, rp->version);
+#endif
+
        clear_uuids(adapter);
        clear_devices(adapter);
 
@@ -8026,6 +13388,13 @@ static void read_info_complete(uint8_t status, uint16_t length,
                        (missing_settings & MGMT_SETTING_FAST_CONNECTABLE))
                set_mode(adapter, MGMT_OP_SET_FAST_CONNECTABLE, 0x01);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* Set the RPA resolution value to '1' if privacy is supported */
+       if (main_opts.le_privacy &&
+                       adapter->supported_settings & MGMT_SETTING_PRIVACY)
+               adapter->central_rpa_res_support = 0x01;
+#endif
+
        err = adapter_register(adapter);
        if (err < 0) {
                btd_error(adapter->dev_id, "Unable to register new adapter");
@@ -8061,6 +13430,13 @@ static void read_info_complete(uint8_t status, uint16_t length,
                                                device_found_callback,
                                                adapter, NULL);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       mgmt_register(adapter->mgmt, MGMT_EV_LE_DEVICE_FOUND,
+                                               adapter->dev_id,
+                                               le_device_found_callback,
+                                               adapter, NULL);
+#endif
+
        mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_DISCONNECTED,
                                                adapter->dev_id,
                                                disconnected_callback,
@@ -8140,6 +13516,63 @@ static void read_info_complete(uint8_t status, uint16_t length,
                                                user_passkey_notify_callback,
                                                adapter, NULL);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       mgmt_register(adapter->mgmt, MGMT_EV_RSSI_ALERT,
+                                               adapter->dev_id,
+                                               rssi_alert_callback,
+                                               adapter, NULL);
+
+       mgmt_register(adapter->mgmt, MGMT_EV_RAW_RSSI,
+                                               adapter->dev_id,
+                                               get_raw_rssi_callback,
+                                               adapter, NULL);
+
+       mgmt_register(adapter->mgmt, MGMT_EV_RSSI_ENABLED,
+                                                       adapter->dev_id,
+                                                       rssi_enabled_callback,
+                                                       adapter, NULL);
+
+       mgmt_register(adapter->mgmt, MGMT_EV_RSSI_DISABLED,
+                                                       adapter->dev_id,
+                                                       rssi_disabled_callback,
+                                                       adapter, NULL);
+
+       mgmt_register(adapter->mgmt, MGMT_EV_HARDWARE_ERROR,
+                                               adapter->dev_id,
+                                               hardware_error_callback,
+                                               adapter, NULL);
+
+       mgmt_register(adapter->mgmt, MGMT_EV_TX_TIMEOUT_ERROR,
+                                               adapter->dev_id,
+                                               tx_timeout_error_callback,
+                                               adapter, NULL);
+
+       mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_NAME_UPDATE,
+                                       adapter->dev_id,
+                                       device_name_update_callback,
+                                       adapter, NULL);
+
+       mgmt_register(adapter->mgmt, MGMT_EV_MULTI_ADV_STATE_CHANGED,
+                                       adapter->dev_id,
+                                       multi_adv_state_change_callback,
+                                       adapter, NULL);
+
+       mgmt_register(adapter->mgmt, MGMT_EV_6LOWPAN_CONN_STATE_CHANGED,
+                                               adapter->dev_id,
+                                               bt_6lowpan_conn_state_change_callback,
+                                               adapter, NULL);
+
+       mgmt_register(adapter->mgmt, MGMT_EV_LE_DATA_LENGTH_CHANGED,
+                                               adapter->dev_id,
+                                               bt_le_data_length_changed_callback,
+                                               adapter, NULL);
+
+       mgmt_register(adapter->mgmt, MGMT_EV_CONN_UPDATED,
+                                       adapter->dev_id,
+                                       le_conn_update_completed_callback,
+                                       adapter, NULL);
+#endif
+
        set_dev_class(adapter);
 
        set_name(adapter, btd_adapter_get_name(adapter));
@@ -8156,7 +13589,15 @@ static void read_info_complete(uint8_t status, uint16_t length,
                set_discoverable(adapter, 0x01, 0);
 
        if (adapter->current_settings & MGMT_SETTING_POWERED)
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               g_idle_add(adapter_start_idle_cb, adapter);
+#else
                adapter_start(adapter);
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       else
+               set_mode(adapter, MGMT_OP_SET_POWERED, 0x01);
+#endif
 
        return;
 
index f2947fe..985e28e 100755 (executable)
 #include <stdbool.h>
 #include <dbus/dbus.h>
 #include <glib.h>
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#endif
 
 #define MAX_NAME_LENGTH                248
 
 /* Invalid SSP passkey value used to indicate negative replies */
 #define INVALID_PASSKEY                0xffffffff
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define BT_DISC_TYPE_DEFAULT    0
+#define BT_DISC_TYPE_BREDR_ONLY 1
+#define BT_DISC_TYPE_LE_ONLY    2
+#define BT_DISC_TYPE_LE_BREDR   3
+
+#define TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+
+#define COMPANY_ID_APPLE 0x004C
+/* Deafult value set to 16(same as vendor specific value)
+ * and value shall be changed when required */
+#define SCAN_FILTER_SLOTS_MAX 16
+#define ADV_TYPE_SCAN_RESPONSE 04
+
+typedef enum {
+       NONE_REPORT,
+       SCAN_REPORT,
+       IBEACON_REPORT,
+} adapter_le_scan_report_type;
+
+typedef enum {
+       LE_PASSIVE_SCAN,
+       LE_ACTIVE_SCAN,
+} adapter_le_scan_type;
+#endif /* TIZEN_FEATURE_PLATFROM_SCAN_FILTER */
+
+#endif
+
 struct btd_adapter;
 struct btd_device;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+typedef enum {
+       BLUETOOTH_A2DP_SOURCE_ROLE = 0,
+       BLUETOOTH_A2DP_SINK_ROLE,
+} bluetooth_a2dp_role_t;
+#endif
+
 struct btd_adapter *btd_adapter_get_default(void);
 bool btd_adapter_is_default(struct btd_adapter *adapter);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+bluetooth_a2dp_role_t btd_adapter_get_a2dp_role(struct btd_adapter *adapter);
+void btd_adapter_set_a2dp_role(struct btd_adapter *adapter, bluetooth_a2dp_role_t role);
+#endif
 uint16_t btd_adapter_get_index(struct btd_adapter *adapter);
 
 typedef void (*adapter_cb) (struct btd_adapter *adapter, gpointer user_data);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+typedef void (*oob_ext_read_local_cb_t) (struct btd_adapter *adapter,
+                                       const uint8_t *hash192,
+                                       const uint8_t *randomizer192,
+                                       const uint8_t *hash256,
+                                       const uint8_t *randomizer256,
+                                       void *user_data);
+#endif
 typedef void (*oob_read_local_cb_t) (struct btd_adapter *adapter,
                                        const uint8_t *hash,
                                        const uint8_t *randomizer,
@@ -49,12 +103,26 @@ typedef void (*oob_bonding_cb_t) (struct btd_adapter *adapter,
                                        void *user_data);
 
 struct oob_handler {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       oob_ext_read_local_cb_t read_local_cb;
+#else
        oob_read_local_cb_t read_local_cb;
+#endif
        oob_bonding_cb_t bonding_cb;
        bdaddr_t remote_addr;
        void *user_data;
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+typedef enum {
+       NONE_CHARGING,
+       WIRE_CHARGING,
+       WIRELESS_CHARGING,
+} charging_state_e;
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
+
 int adapter_init(void);
 void adapter_cleanup(void);
 void adapter_shutdown(void);
@@ -92,6 +160,10 @@ struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter,
 
 const char *adapter_get_path(struct btd_adapter *adapter);
 const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+const bdaddr_t *btd_adapter_get_le_address(struct btd_adapter *adapter);
+uint8_t btd_adapter_get_le_address_type(struct btd_adapter * adapter);
+#endif
 int adapter_set_name(struct btd_adapter *adapter, const char *name);
 
 int adapter_service_add(struct btd_adapter *adapter, sdp_record_t *rec);
@@ -198,6 +270,16 @@ int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
 int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
                                                        const bdaddr_t *bdaddr);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int btd_adapter_add_remote_oob_ext_data(struct btd_adapter *adapter,
+                               const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+                               uint8_t *hash192, uint8_t *randomizer192,
+                               uint8_t *hash256, uint8_t *randomizer256);
+
+int btd_adapter_remove_remote_oob_ext_data(struct btd_adapter *adapter,
+                               const bdaddr_t *bdaddr, uint8_t bdaddr_type);
+#endif
+
 int btd_adapter_gatt_server_start(struct btd_adapter *adapter);
 void btd_adapter_gatt_server_stop(struct btd_adapter *adapter);
 
@@ -226,3 +308,48 @@ void btd_adapter_for_each_device(struct btd_adapter *adapter,
 
 bool btd_le_connect_before_pairing(void);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int btd_adapter_connect_ipsp(struct btd_adapter *adapter,
+                                               const bdaddr_t *bdaddr,
+                                               uint8_t bdaddr_type);
+int btd_adapter_disconnect_ipsp(struct btd_adapter *adapter,
+                                               const bdaddr_t *bdaddr,
+                                               uint8_t bdaddr_type);
+
+uint8_t btd_adapter_get_rpa_res_support_value(
+               struct btd_adapter *adapter);
+
+int btd_adapter_set_dev_rpa_res_support(struct btd_adapter *adapter,
+                                               struct btd_device *device);
+#endif
+
+typedef void (*read_max_data_length_cb_t) (struct btd_adapter *adapter,
+                                       const uint16_t max_txOctects,
+                                       const uint16_t max_txTime,
+                                       const uint16_t max_rxOctects,
+                                       const uint16_t max_rxTime,
+                                       void *user_data);
+
+struct le_data_length_read_handler {
+       read_max_data_length_cb_t read_callback;
+       void *user_data;
+};
+
+typedef void (*read_host_suggested_default_data_length_cb_t) (struct btd_adapter *adapter,
+                                       const uint16_t def_txOctects,
+                                       const uint16_t def_txTime,
+                                       void *user_data);
+
+struct le_data_length_read_default_data_length_handler {
+       read_host_suggested_default_data_length_cb_t read_callback;
+       void *user_data;
+};
+
+int btd_adapter_le_set_data_length(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                       uint16_t max_tx_octets, uint16_t max_tx_time);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void adapter_check_version(struct btd_adapter *adapter, uint8_t hci_ver);
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+charging_state_e get_charging_state(struct btd_adapter *adapter);
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
diff --git a/src/adapter_le_vsc_features.c b/src/adapter_le_vsc_features.c
new file mode 100755 (executable)
index 0000000..6c586eb
--- /dev/null
@@ -0,0 +1,769 @@
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+
+#include <errno.h>
+
+#include "log.h"
+#include "adapter.h"
+#include "eir.h"
+
+#include "adapter_le_vsc_features.h"
+
+
+static apater_le_vsc_rp_get_vendor_cap ble_vsc_cb = { -1, };
+
+static int update_le_address(const bdaddr_t *le_addr)
+{
+       int hdev = 0;
+       le_set_random_address_cp cp;
+
+       hdev = hci_open_dev(0);
+       if (hdev < 0) {
+               error("Cannot open hdev");
+               return -1;
+       }
+
+       bacpy(&cp.bdaddr, le_addr);
+
+       if (hci_send_cmd(hdev, OGF_LE_CTL, OCF_LE_SET_RANDOM_ADDRESS,
+                               LE_SET_RANDOM_ADDRESS_CP_SIZE, &cp) < 0) {
+               error("hci_send_cmd is failed");
+               hci_close_dev(hdev);
+               return -1;
+       }
+
+       hci_close_dev(hdev);
+
+       return 0;
+}
+
+static int send_vsc_command(uint16_t ocf, uint8_t *cp, uint8_t cp_len,
+                                               uint8_t *rp, uint8_t rp_len)
+{
+       int dd;
+       struct hci_request rq;
+
+       dd = hci_open_dev(0);
+       if (dd < 0) {
+               error("hci_open_dev is failed");
+               return -1;
+       }
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = ocf;
+       rq.cparam = cp;
+       rq.clen   = cp_len;
+       rq.rparam = rp;
+       rq.rlen   = rp_len;
+
+       if (hci_send_req(dd, &rq, 5000) < 0) {
+               error("Fail to send VSC");
+               hci_close_dev(dd);
+               return -1;
+       }
+
+       hci_close_dev(dd);
+       return 0;
+}
+
+gboolean adapter_le_read_ble_feature_info(void)
+{
+       int ret;
+
+       DBG("");
+
+       ret = send_vsc_command(OCF_BCM_LE_GET_VENDOR_CAP, (uint8_t *) NULL, 0,
+                                               (uint8_t *) &ble_vsc_cb, sizeof(ble_vsc_cb));
+       if (ret < 0)
+               return FALSE;
+
+       if (HCI_SUCCESS != ble_vsc_cb.status) {
+               error("Fail to read ble feature info");
+               return FALSE;
+       }
+
+       DBG("======== BLE support info ========");
+       DBG("adv_inst_max [%d]", ble_vsc_cb.adv_inst_max);
+       DBG("rpa_offloading [%d]", ble_vsc_cb.rpa_offloading);
+       DBG("tot_scan_results_strg [%d]", ble_vsc_cb.tot_scan_results_strg);
+       DBG("max_irk_list_sz [%d]", ble_vsc_cb.max_irk_list_sz);
+       DBG("filter_support [%d]", ble_vsc_cb.filter_support);
+       DBG("max_filter [%d]", ble_vsc_cb.max_filter);
+       DBG("energy_support [%d]", ble_vsc_cb.energy_support);
+       DBG("onlost_follow [%d]", ble_vsc_cb.onlost_follow);
+       DBG("=================================");
+
+       return TRUE;
+}
+
+int adapter_le_get_max_adv_instance(void)
+{
+       if (HCI_SUCCESS != ble_vsc_cb.status) {
+               error("not yet acquired chipset info");
+               return 0;
+       }
+
+       /* GearS does not support multi advertising.
+       but its official firmware returns adv_inst_max vaule to 5.
+       So here check rpa_offloading support and filter_support */
+       if (!ble_vsc_cb.rpa_offloading || !ble_vsc_cb.max_filter)
+               return 0;
+
+       return ble_vsc_cb.adv_inst_max;
+}
+
+gboolean adapter_le_is_supported_multi_advertising(void)
+{
+       if (HCI_SUCCESS != ble_vsc_cb.status) {
+               error("not yet acquired chipset info");
+               return FALSE;
+       }
+
+       /* GearS does not support multi advertising.
+       but its official firmware returns adv_inst_max vaule to 5.
+       So here check rpa_offloading support and filter_support */
+       if (!ble_vsc_cb.rpa_offloading || !ble_vsc_cb.max_filter)
+               return FALSE;
+
+       if (ble_vsc_cb.adv_inst_max >= 5)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+gboolean adapter_le_is_supported_offloading(void)
+{
+       if (HCI_SUCCESS != ble_vsc_cb.status) {
+               error("not yet acquired chipset info");
+               return FALSE;
+       }
+
+       return ble_vsc_cb.rpa_offloading ? TRUE : FALSE;
+}
+
+int adapter_le_get_scan_filter_size(void)
+{
+       if (HCI_SUCCESS != ble_vsc_cb.status) {
+               error("not yet acquired chipset info");
+               return 0;
+       }
+#if 0
+       if (!ble_vsc_cb.filter_support) {
+               error("filter_support is not supported");
+               return 0;
+       }
+#endif
+       return ble_vsc_cb.max_filter;
+}
+
+gboolean adapter_le_set_multi_adv_params (adapter_le_adv_inst_info_t *p_inst,
+                                       adapter_le_adv_param_t *p_params)
+{
+       int ret;
+       adapter_le_vsc_cp_set_multi_adv_params cp;
+       apater_le_vsc_rp_multi_adv rp;
+
+       DBG("");
+
+       memset(&cp, 0, sizeof(cp));
+       cp.subcode = SUB_CMD_LE_MULTI_ADV_SET_PARAM;
+       cp.adv_int_min = p_params->adv_int_min;
+       cp.adv_int_max = p_params->adv_int_max;
+       cp.adv_type = p_params->adv_type;
+       cp.bdaddr_type = p_inst->bdaddr_type;
+       bacpy(&cp.bdaddr, &p_inst->bdaddr);
+       cp.direct_bdaddr_type = 0;
+       bacpy(&cp.direct_bdaddr, BDADDR_ANY);
+
+       cp.channel_map = p_params->channel_map;
+       cp.adv_filter_policy = p_params->adv_filter_policy;
+       cp.inst_id = p_inst->inst_id;
+       cp.tx_power = p_params->tx_power;
+
+       ret = send_vsc_command(OCF_BCM_LE_MULTI_ADV, (uint8_t *) &cp, sizeof(cp),
+                                       (uint8_t *) &rp, sizeof(rp));
+
+       if (ret < 0)
+               return FALSE;
+
+       if (HCI_SUCCESS != rp.status) {
+               DBG("Fail to send VSC :: sub[%x] - status [0x%02x]", rp.subcode, rp.status);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+gboolean adapter_le_set_multi_adv_data(uint8_t inst_id, gboolean is_scan_rsp,
+                                       uint8_t data_len, uint8_t *p_data)
+{
+       int ret;
+       adapter_le_vsc_cp_set_multi_adv_data cp;
+       apater_le_vsc_rp_multi_adv rp;
+
+       DBG("");
+
+       memset(&cp, 0, sizeof(cp));
+       cp.subcode = (is_scan_rsp) ?
+                           SUB_CMD_LE_MULTI_ADV_WRITE_SCAN_RSP_DATA :
+                           SUB_CMD_LE_MULTI_ADV_WRITE_ADV_DATA;
+       cp.data_len = data_len;
+       memcpy(&cp.data, p_data, data_len);
+       cp.inst_id = inst_id;
+
+       ret = send_vsc_command(OCF_BCM_LE_MULTI_ADV, (uint8_t *) &cp, sizeof(cp),
+                                       (uint8_t *) &rp, sizeof(rp));
+
+       if (ret < 0)
+               return FALSE;
+
+       if (HCI_SUCCESS != rp.status) {
+               DBG("Fail to send VSC :: sub[%x] - status [0x%02x]", rp.subcode, rp.status);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+gboolean adapter_le_enable_multi_adv (struct btd_adapter *adapter,
+                                       gboolean enable, uint8_t inst_id)
+{
+       int ret;
+       adapter_le_vsc_cp_enable_multi_adv cp;
+       apater_le_vsc_rp_multi_adv rp;
+       uint8_t bdaddr_type;
+       const bdaddr_t *bdaddr;
+
+       DBG("");
+       if (enable) {
+               bdaddr_type = btd_adapter_get_le_address_type(adapter);
+               if (bdaddr_type == BDADDR_LE_RANDOM) {
+                       bdaddr = btd_adapter_get_le_address(adapter);
+                       update_le_address(bdaddr);
+               }
+       }
+
+       memset(&cp, 0, sizeof(cp));
+       cp.subcode = SUB_CMD_LE_MULTI_ADV_ENB;
+       cp.enable = enable;
+       cp.inst_id = inst_id;
+
+       ret = send_vsc_command(OCF_BCM_LE_MULTI_ADV, (uint8_t *) &cp, sizeof(cp),
+                                       (uint8_t *) &rp, sizeof(rp));
+
+       if (ret < 0)
+               return FALSE;
+
+       if (HCI_SUCCESS != rp.status) {
+               DBG("Fail to send VSC :: sub[%x] - status [0x%02x]", rp.subcode, rp.status);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+gboolean adapter_le_enable_scan_filtering (gboolean enable)
+{
+       int ret;
+       adapter_le_vsc_cp_enable_scan_filter cp;
+       apater_le_vsc_rp_enable_scan_filter rp;
+
+       DBG(" enable[%d]", enable);
+
+       memset(&cp, 0, sizeof(cp));
+       cp.subcode = SUB_CMD_LE_META_PF_ENABLE;
+       cp.enable = enable;
+
+       ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, sizeof(cp),
+                                               (uint8_t *) &rp, sizeof(rp));
+
+       if (ret < 0)
+               return FALSE;
+
+       if (HCI_SUCCESS != rp.status) {
+               DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+gboolean adapter_le_set_scan_filter_params(adapter_le_scan_filter_param_t *params)
+{
+       int ret;
+       adapter_le_vsc_cp_apcf_set_filter_params cp;
+       adapter_le_vsc_rp_apcf_set_scan_filter rp;
+
+       DBG("filter_index [%d]", params->index);
+
+       memset(&cp, 0, sizeof(cp));
+       cp.subcode = SUB_CMD_LE_META_PF_FEAT_SEL;
+       cp.action = params->action;
+       cp.filter_index= params->index;
+       cp.feature= params->feature;
+       cp.feature_list_logic = params->filter_logic_type;
+       cp.filter_logic = params->filter_logic_type;
+       cp.rssi_high_threshold = params->rssi_high_threshold;
+       cp.rssi_low_thresh = params->rssi_low_threshold;
+       cp.delivery_mode = params->delivery_mode;
+       cp.onfound_timeout = params->onfound_timeout;
+       cp.onfound_timeout_cnt = params->onfound_timeout_cnt;
+       cp.onlost_timeout = params->onlost_timeout;
+
+       ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, sizeof(cp),
+                                               (uint8_t *) &rp, sizeof(rp));
+
+       if (ret < 0)
+               return FALSE;
+
+       if (HCI_SUCCESS != rp.status) {
+               DBG("Fail to send VSC :: sub[%x] - status [%x] Available space [%x]",
+                                       rp.subcode, rp.status, rp.available_space);
+               return FALSE;
+       }
+
+       DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+                                       rp.subcode, rp.status, rp.action, rp.available_space);
+       return TRUE;
+}
+
+gboolean adapter_le_service_address_scan_filtering(adapter_le_address_filter_params_t *params)
+{
+       int ret;
+       adapter_le_vsc_cp_address_scan_filter cp;
+       adapter_le_vsc_rp_apcf_set_scan_filter rp;
+
+       DBG("");
+
+       memset(&cp, 0, sizeof(cp));
+       cp.subcode = SUB_CMD_LE_META_PF_ADDR;
+       cp.action= params ->action;
+       cp.filter_index = params->filter_index;
+
+       bacpy(&cp.bdaddr, &params->broadcaster_addr);
+       cp.bdaddr_type = params->bdaddr_type;
+
+       ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, sizeof(cp),
+                                               (uint8_t *) &rp, sizeof(rp));
+
+       if (ret < 0)
+               return FALSE;
+
+       if (HCI_SUCCESS != rp.status) {
+               DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+               return FALSE;
+       }
+
+       DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+                                       rp.subcode, rp.status, rp.action, rp.available_space);
+
+       return TRUE;
+}
+
+gboolean adapter_le_service_uuid_scan_filtering(gboolean is_solicited,
+                                       adapter_le_uuid_params_t *params)
+{
+       int ret;
+       adapter_le_vsc_cp_service_uuid_scan_filter cp;
+       adapter_le_vsc_rp_apcf_set_scan_filter rp;
+       uint8_t *p = cp.data;
+       int cp_len = UUID_SCAN_FILTER_HEADER_SIZE;
+
+       DBG("");
+
+       memset(&cp, 0, sizeof(cp));
+       cp.subcode = (is_solicited) ? SUB_CMD_LE_META_PF_SOL_UUID :
+                                       SUB_CMD_LE_META_PF_UUID;
+
+       cp.action= params ->action;
+       cp.filter_index = params->filter_index;
+
+       memcpy(&cp.data, params->uuid, params->uuid_len);
+       cp_len += params->uuid_len;
+
+       memcpy(p + params->uuid_len, params->uuid_mask, params->uuid_len);
+       cp_len += params->uuid_len;
+
+       ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, cp_len,
+                                               (uint8_t *) &rp, sizeof(rp));
+
+       if (ret < 0)
+               return FALSE;
+
+       if (HCI_SUCCESS != rp.status) {
+               DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+               return FALSE;
+       }
+
+       DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+                                       rp.subcode, rp.status, rp.action, rp.available_space);
+
+       return TRUE;
+}
+
+gboolean adapter_le_local_name_scan_filtering(adapter_le_local_name_params_t *params)
+{
+       int ret;
+       adapter_le_vsc_cp_local_name_scan_filter cp;
+       adapter_le_vsc_rp_apcf_set_scan_filter rp;
+       int cp_len = NAME_SCAN_FILTER_HEADER_SIZE;
+       int name_len;
+
+       DBG("");
+
+       memset(&cp, 0, sizeof(cp));
+       cp.subcode = SUB_CMD_LE_META_PF_LOCAL_NAME;
+       cp.action= params->action;
+       cp.filter_index = params->filter_index;
+
+       name_len = params->name_len;
+       DBG("name [%s], len [%d]",params->local_name, name_len);
+
+       if (name_len > SCAN_FILTER_DATA_MAX_LEN)
+               name_len = SCAN_FILTER_DATA_MAX_LEN;
+
+       if (name_len > 0) {
+               memcpy(&cp.name, params->local_name, name_len);
+               cp_len += name_len;
+       }
+
+       ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, cp_len,
+                                               (uint8_t *) &rp, sizeof(rp));
+
+       if (ret < 0)
+               return FALSE;
+
+       if (HCI_SUCCESS != rp.status) {
+               DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+               return FALSE;
+       }
+
+       DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+                                       rp.subcode, rp.status, rp.action, rp.available_space);
+
+       return TRUE;
+}
+
+gboolean adapter_le_manf_data_scan_filtering (adapter_le_manf_data_params_t *params)
+{
+       int ret;
+       adapter_le_vsc_cp_manf_data_scan_filter cp;
+       adapter_le_vsc_rp_apcf_set_scan_filter rp;
+       uint8_t *p = cp.data;
+       int data_len = 0;
+       DBG("");
+
+       memset(&cp, 0, sizeof(cp));
+       cp.subcode = SUB_CMD_LE_META_PF_MANU_DATA;
+       cp.action= params->action;
+       cp.filter_index = params->filter_index;
+
+       /* add company_id and data */
+       cp.data[data_len++] = (uint8_t) params->company_id;
+       cp.data[data_len++] = (uint8_t) (params->company_id >> 8);
+       DBG("");
+       memcpy(p + data_len, params->man_data, params->man_data_len);
+       data_len += params->man_data_len;
+
+       /* add company_id mask and data mask */
+       cp.data[data_len++] = (uint8_t) params->company_id_mask;
+       cp.data[data_len++] = (uint8_t) (params->company_id_mask >> 8);
+       memcpy(p + data_len, params->man_data_mask, params->man_data_len);
+       data_len += params->man_data_len;
+
+       ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp,
+                                               MANF_DATA_SCAN_FILTER_HEADER_SIZE + data_len,
+                                               (uint8_t *) &rp, sizeof(rp));
+
+       if (ret < 0)
+               return FALSE;
+
+       if (HCI_SUCCESS != rp.status) {
+               DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+               return FALSE;
+       }
+
+       DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+                                       rp.subcode, rp.status, rp.action, rp.available_space);
+
+       return TRUE;
+}
+
+gboolean adapter_le_service_data_scan_filtering (adapter_le_service_data_params_t *params)
+{
+       int ret;
+       adapter_le_vsc_cp_service_data_scan_filter cp;
+       adapter_le_vsc_rp_apcf_set_scan_filter rp;
+       uint8_t *p = cp.data;
+       int cp_len = SERVICE_DATA_SCAN_FILTER_HEADER_SIZE;
+
+       DBG("");
+
+       memset(&cp, 0, sizeof(cp));
+       cp.subcode = SUB_CMD_LE_META_PF_SRVC_DATA;
+       cp.action= params->action;
+       cp.filter_index = params->filter_index;
+
+       memcpy(&cp.data, params->service_data, params->service_data_len);
+       cp_len += params->service_data_len;
+
+       memcpy(p+params->service_data_len, params->service_data_mask,
+                                               params->service_data_len);
+       cp_len += params->service_data_len;
+
+       ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, cp_len,
+                                               (uint8_t *) &rp, sizeof(rp));
+
+       if (ret < 0)
+               return FALSE;
+
+       if (HCI_SUCCESS != rp.status) {
+               DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+               return FALSE;
+       }
+
+       DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+                                       rp.subcode, rp.status, rp.action, rp.available_space);
+
+       return TRUE;
+}
+
+gboolean adapter_le_set_scan_filter_data(int client_if, int action,
+                                                       int filt_type, int filter_index,
+                                                       int company_id,
+                                                       int company_id_mask,
+                                                       int uuid_len, uint8_t *p_uuid,
+                                                       int uuid_mask_len, uint8_t *p_uuid_mask,
+                                                       gchar *string, int addr_type,
+                                                       int data_len, uint8_t *p_data,
+                                                       int mask_len, uint8_t *p_mask)
+{
+       gboolean ret;
+
+       DBG("");
+
+       switch (filt_type) {
+       case TYPE_DEVICE_ADDRESS: {
+               /* TYPE_DEVICE_ADDRESS */
+               adapter_le_address_filter_params_t params;
+               bdaddr_t bd_addr;
+
+               str2ba(string, &bd_addr);
+
+               params.action = action;
+               params.filter_index = filter_index;
+               bacpy(&params.broadcaster_addr, &bd_addr);
+               params.bdaddr_type = addr_type;
+
+               ret = adapter_le_service_address_scan_filtering(&params);
+               break;
+       }
+
+       case TYPE_SERVICE_UUID:
+       case TYPE_SOLICIT_UUID: {
+               adapter_le_uuid_params_t params;
+               gboolean is_solicited = (filt_type == TYPE_SOLICIT_UUID) ? TRUE : FALSE;
+
+               if (uuid_len != UUID_16_LEN && uuid_len != UUID_32_LEN
+                       && uuid_len != UUID_128_LEN) {
+                       DBG("UUID length error");
+                       return FALSE;
+               }
+
+               if (uuid_len != uuid_mask_len) {
+                       DBG("Both UUID and UUID_MASK length shoule be samed");
+                       return FALSE;
+               }
+
+               params.action = action;
+               params.filter_index = filter_index;
+               params.uuid =  p_uuid;
+               params.uuid_mask =  p_uuid_mask;
+               params.uuid_len = uuid_len;
+
+               ret = adapter_le_service_uuid_scan_filtering(is_solicited, &params);
+               break;
+       }
+
+       case TYPE_LOCAL_NAME: {
+               adapter_le_local_name_params_t params;
+
+               params.action = action;
+               params.filter_index = filter_index;
+               params.local_name = string;
+               params.name_len = strlen(string);
+               ret = adapter_le_local_name_scan_filtering(&params);
+               break;
+       }
+
+       case TYPE_MANUFACTURER_DATA: {
+               adapter_le_manf_data_params_t params;
+
+               if (data_len == 0 || (data_len != mask_len)) {
+                       DBG("parameter length error");
+                       return FALSE;
+               }
+
+               params.action = action;
+               params.filter_index = filter_index;
+               params.company_id = company_id;
+               params.company_id_mask = company_id_mask;
+               params.man_data = p_data;
+               params.man_data_mask = p_mask;
+               params.man_data_len = data_len;
+
+               ret = adapter_le_manf_data_scan_filtering(&params);
+               break;
+       }
+
+       case TYPE_SERVICE_DATA: {
+               adapter_le_service_data_params_t params;
+
+               if (data_len == 0 || (data_len != mask_len)) {
+                       DBG("parameter length error");
+                       return FALSE;
+               }
+
+               params.action = action;
+               params.filter_index = filter_index;
+               params.service_data = p_data;
+               params.service_data_mask = p_mask;
+               params.service_data_len = data_len;
+
+               ret = adapter_le_service_data_scan_filtering(&params);
+               break;
+       }
+
+       default:
+               DBG("filter_type error");
+               ret = FALSE;
+       }
+
+       return ret;
+}
+
+gboolean adapter_le_clear_scan_filter_data(int client_if, int filter_index)
+{
+       int ret;
+       adapter_le_vsc_cp_service_data_scan_filter cp;
+       adapter_le_vsc_rp_apcf_set_scan_filter rp;
+
+       DBG("");
+
+       memset(&cp, 0, sizeof(cp));
+       cp.subcode = SUB_CMD_LE_META_PF_FEAT_SEL;
+       cp.action= 0x02; // (Add - 0x00, Delete - 0x01, Clear - 0x02)
+       cp.filter_index = filter_index;
+
+       ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, sizeof(cp),
+                                               (uint8_t *) &rp, sizeof(rp));
+
+       if (ret < 0)
+               return FALSE;
+
+       if (HCI_SUCCESS != rp.status) {
+               DBG("Fail to send VSC :: sub[%x] - status [%x] Available space [%x]",
+                                       rp.subcode, rp.status, rp.available_space);
+               return FALSE;
+       }
+
+       DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+                                       rp.subcode, rp.status, rp.action, rp.available_space);
+       return TRUE;
+}
+
+gboolean adapter_le_enable_offloading(gboolean enable)
+{
+       int ret;
+       adapter_le_vsc_cp_enable_rpa_offload cp;
+       adapter_le_vsc_rp_enable_rpa_offload rp;
+
+       DBG("");
+
+       memset(&cp, 0, sizeof(cp));
+       cp.subcode = SUB_CMD_LE_ENABLE_OFFLOADING;
+       cp.enable = enable;
+
+       ret = send_vsc_command(OCF_BCM_LE_RPA_OFFLOAD, (uint8_t *) &cp, sizeof(cp),
+                                       (uint8_t *) &rp, sizeof(rp));
+
+       if (ret < 0)
+               return FALSE;
+
+       if (HCI_SUCCESS != rp.status) {
+               DBG("Fail to send VSC :: sub[%x] - status [0x%02x]", rp.subcode, rp.status);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+gboolean adapter_le_add_irk_to_list(const uint8_t *le_irk, const bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+       int ret;
+       adapter_le_vsc_cp_add_irk_to_list cp;
+       adapter_le_vsc_rp_irk_to_list rp;
+
+       DBG("addr_type %d, irk %x %x %x...", bdaddr_type, le_irk[0], le_irk[1], le_irk[2]);
+
+       memset(&cp, 0, sizeof(cp));
+       cp.subcode = SUB_CMD_LE_ADD_IRK_TO_LIST;
+       memcpy(&cp.le_irk, le_irk, sizeof(cp.le_irk));
+       bacpy(&cp.bdaddr, bdaddr);
+
+       if (bdaddr_type == BDADDR_LE_PUBLIC)
+               cp.bdaddr_type = 0x0;
+       else
+               cp.bdaddr_type = 0x1;
+
+       ret = send_vsc_command(OCF_BCM_LE_RPA_OFFLOAD, (uint8_t *) &cp, sizeof(cp),
+                                       (uint8_t *) &rp, sizeof(rp));
+
+       if (ret < 0)
+               return FALSE;
+
+       if (HCI_SUCCESS != rp.status) {
+               DBG("Fail to send VSC :: sub[%x] - status [0x%02x]", rp.subcode, rp.status);
+               return FALSE;
+       }
+
+       DBG("Add IRK to VCS :: available space[%d]", rp.available_space);
+
+       return TRUE;
+}
+
+gboolean adapter_le_remove_irk_to_list(const bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+       int ret;
+       adapter_le_vsc_cp_remove_irk_to_list cp;
+       adapter_le_vsc_rp_irk_to_list rp;
+
+       DBG("");
+
+       memset(&cp, 0, sizeof(cp));
+       cp.subcode = SUB_CMD_LE_REMOVE_IRK_TO_LIST;
+       bacpy(&cp.bdaddr, bdaddr);
+
+       if (bdaddr_type == BDADDR_LE_PUBLIC)
+               cp.bdaddr_type = 0x0;
+       else
+               cp.bdaddr_type = 0x1;
+
+       ret = send_vsc_command(OCF_BCM_LE_RPA_OFFLOAD, (uint8_t *) &cp, sizeof(cp),
+                                       (uint8_t *) &rp, sizeof(rp));
+
+       if (ret < 0)
+               return FALSE;
+
+       if (HCI_SUCCESS != rp.status) {
+               DBG("Fail to send VSC :: sub[%x] - status [0x%02x]", rp.subcode, rp.status);
+               return FALSE;
+       }
+
+       DBG("Remove IRK to VCS :: available space[%d]", rp.available_space);
+
+       return TRUE;
+}
+
+
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
diff --git a/src/adapter_le_vsc_features.h b/src/adapter_le_vsc_features.h
new file mode 100755 (executable)
index 0000000..44a4bcc
--- /dev/null
@@ -0,0 +1,503 @@
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+
+typedef enum {
+       BLE_ADV_TX_POWER_MIN = 0x00,
+       BLE_ADV_TX_POWER_LOW = 0x01,
+       BLE_ADV_TX_POWER_MID = 0x02,
+       BLE_ADV_TX_POWER_UPPER = 0x03,
+       BLE_ADV_TX_POWER_MAX = 0x04,
+} adapter_le_tx_power_t;
+
+typedef struct {
+       uint8_t inst_id;
+       uint8_t bdaddr_type;
+       bdaddr_t bdaddr;
+} adapter_le_adv_inst_info_t;
+
+typedef struct {
+       uint16_t adv_int_min;         /* minimum adv interval */
+       uint16_t adv_int_max;         /* maximum adv interval */
+       uint8_t adv_type;             /* adv event type (0x00 ~ 0x04) */
+       uint8_t channel_map;          /* adv channel map (all channel = 0x07)  */
+       uint8_t adv_filter_policy;    /* advertising filter policy (0x00 ~ 0x04) */
+       adapter_le_tx_power_t tx_power;    /* adv tx power */
+} adapter_le_adv_param_t;
+
+typedef enum {
+       ADD,
+       DELETE,
+       CLEAR,
+} adapter_le_scan_filter_action_type;
+
+typedef enum {
+       ADDR_LE_PUBLIC,
+       ADDR_LE_RANDOM,
+} adapter_vsc_le_addr_type;
+
+typedef enum {
+       TYPE_DEVICE_ADDRESS = 0x01,
+       TYPE_SERVICE_DATA_CHANGED = 0x02,
+       TYPE_SERVICE_UUID = 0x04,
+       TYPE_SOLICIT_UUID = 0x08,
+       TYPE_LOCAL_NAME = 0x10,
+       TYPE_MANUFACTURER_DATA = 0x20,
+       TYPE_SERVICE_DATA = 0x40,
+} adapter_le_scan_filter_type;
+
+#define BDADDR_BREDR           0x00
+#define BDADDR_LE_PUBLIC       0x01
+
+#define BROADCAST_ADDR_FILTER  0x01
+#define SERVICE_DATA_CHANGE_FILTER     0x02
+#define SERVICE_UUID_CHECK             0x04
+#define SERVICE_SOLICITATION_UUID_CHECK        0x08
+#define LOCAL_NAME_CHECK                       0x10
+#define MANUFACTURE_DATA_CHECK         0x20
+#define SERVICE_DATA_CHECK                     0x40
+
+typedef uint16_t adapter_le_scan_filter_feature_t;
+
+typedef enum {
+       OR,
+       AND,
+} adapter_le_scan_filter_logic_type;
+
+typedef enum {
+       IMMEDIATE,
+       ON_FOUND,
+       BATCHED,
+} adapter_le_scan_filter_delivery_mode;
+
+typedef enum {
+       UUID_16_LEN=2,
+       UUID_32_LEN=4,
+       UUID_128_LEN =16,
+} adapter_le_uuid_len;
+
+typedef struct {
+       adapter_le_scan_filter_action_type action;
+       uint8_t index;
+       adapter_le_scan_filter_feature_t feature;
+       adapter_le_scan_filter_logic_type list_logic_type;
+       adapter_le_scan_filter_logic_type filter_logic_type;
+       uint8_t rssi_high_threshold;
+       adapter_le_scan_filter_delivery_mode delivery_mode;
+       uint16_t onfound_timeout;
+       uint8_t onfound_timeout_cnt;
+       uint8_t rssi_low_threshold;
+       uint16_t onlost_timeout;
+}adapter_le_scan_filter_param_t;
+
+typedef struct {
+       adapter_le_scan_filter_action_type action;
+       uint8_t filter_index;
+       bdaddr_t broadcaster_addr;
+       adapter_vsc_le_addr_type bdaddr_type;
+} adapter_le_address_filter_params_t;
+
+typedef struct {
+       adapter_le_scan_filter_action_type action;
+       uint8_t filter_index;
+       uint8_t *uuid;
+       uint8_t *uuid_mask;
+       adapter_le_uuid_len     uuid_len;
+}adapter_le_uuid_params_t;
+
+typedef struct {
+       adapter_le_scan_filter_action_type action;
+       uint8_t filter_index;
+       const char *local_name;
+       uint8_t name_len;
+}adapter_le_local_name_params_t;
+
+typedef struct {
+       adapter_le_scan_filter_action_type action;
+       uint8_t filter_index;
+       uint16_t company_id;
+       uint16_t company_id_mask;
+       uint8_t *man_data;
+       uint8_t *man_data_mask;
+       uint8_t man_data_len;
+}adapter_le_manf_data_params_t;
+
+typedef struct {
+       adapter_le_scan_filter_action_type action;
+       uint8_t filter_index;
+       uint8_t *service_data;
+       uint8_t *service_data_mask;
+       uint8_t service_data_len;
+}adapter_le_service_data_params_t;
+
+/*****************************************************************************
+**  Defentions for HCI Error Codes that are past in the events
+*/
+#define HCI_SUCCESS                                     0x00
+
+
+
+/*****************************************************************************
+**                          Vendor Specific Commands
+**
+*/
+
+#define OCF_BCM_LE_GET_VENDOR_CAP    0x0153     /* LE Get Vendor Capabilities */
+
+#define OCF_BCM_LE_MULTI_ADV     0x0154     /* Multi adv OCF */
+
+/* subcode for multi adv feature */
+#define SUB_CMD_LE_MULTI_ADV_SET_PARAM                     0x01
+#define SUB_CMD_LE_MULTI_ADV_WRITE_ADV_DATA                0x02
+#define SUB_CMD_LE_MULTI_ADV_WRITE_SCAN_RSP_DATA           0x03
+#define SUB_CMD_LE_MULTI_ADV_SET_RANDOM_ADDR               0x04
+#define SUB_CMD_LE_MULTI_ADV_ENB                           0x05
+
+/* APCF : Advertising Packet Content Filter feature */
+#define OCF_BCM_LE_SCAN_FILTER     0x0157     /* Advertising filter OCF */
+
+/* Sub codes for APCF */
+#define SUB_CMD_LE_META_PF_ENABLE          0x00
+#define SUB_CMD_LE_META_PF_FEAT_SEL        0x01
+#define SUB_CMD_LE_META_PF_ADDR            0x02
+#define SUB_CMD_LE_META_PF_UUID            0x03
+#define SUB_CMD_LE_META_PF_SOL_UUID        0x04
+#define SUB_CMD_LE_META_PF_LOCAL_NAME      0x05
+#define SUB_CMD_LE_META_PF_MANU_DATA       0x06
+#define SUB_CMD_LE_META_PF_SRVC_DATA       0x07
+#define SUB_CMD_LE_META_PF_ALL             0x08
+
+/* Offloaded resolution of private address */
+#define OCF_BCM_LE_RPA_OFFLOAD     0x0155     /* RPA Offload OCF */
+
+/* subcode for rpa offloading feature */
+#define SUB_CMD_LE_ENABLE_OFFLOADING       0x01
+#define SUB_CMD_LE_ADD_IRK_TO_LIST         0x02
+#define SUB_CMD_LE_REMOVE_IRK_TO_LIST      0x03
+#define SUB_CMD_LE_CLEAR_IRK_TO_LIST       0x04
+#define SUB_CMD_LE_READ_IRK_TO_LIST        0x05
+
+/*****************************************************************************
+**                          CP & RP for OCF_BCM_LE_GET_VENDOR_CAP
+**
+*/
+
+/**
+*
+* RP
+*
+* (1 octet) status        : Command complete status
+* (1 octet) adv_inst_max  : Num of advertisement instances supported
+* (1 octet) rpa_offloading: BT chip capability of RPA
+*                           (value 0 not capable, value 1 capable)
+*                           If supported by chip, it needs enablement by host
+* (2 octet) tot_scan_results_strg : Storage for scan results in bytes
+* (1 octet) max_irk_list_sz : Num of IRK entries supported in f/w
+* (1 octet) filter_support  : Support Filtering in controller.
+*                             0 = Not supported / 1 = supported
+* (1 octet) max_filter      : Number of filters supported
+* (1 octet) energy_support  : Supports reporting of activity and energy info
+*                             0 = not capable, 1 = capable
+* (1 octet) onlost_follow   : Number of advertisers that can be analysed
+*                             for onlost per filter
+*/
+typedef struct {
+       uint8_t status;
+       uint8_t adv_inst_max;
+       uint8_t rpa_offloading;
+       uint16_t tot_scan_results_strg;
+       uint8_t max_irk_list_sz;
+       uint8_t filter_support;
+       uint8_t max_filter;
+       uint8_t energy_support;
+       uint8_t onlost_follow;
+} __attribute__ ((packed)) apater_le_vsc_rp_get_vendor_cap;
+
+
+
+/*****************************************************************************
+**                          CP & RP for OCF_BCM_LE_MULTI_ADV
+**
+*/
+
+/**
+*
+* CP for  OCF_BCM_LE_MULTI_ADV & SUB_CMD_LE_MULTI_ADV_SET_PARAM
+*
+* (1 octet) subcode            : SUB_CMD_LE_MULTI_ADV_SET_PARAM
+* (2 octet) adv_int_min        : per spec
+* (2 octet) adv_int_max        : per spec
+* (1 octet) adv_type           : per spec
+* (1 octet) bdaddr_type        : per spec
+* (6 octet) bdaddr             : per spec
+* (1 octet) direct_bdaddr_type : per spec
+* (6 octet) direct_bdaddr      : per spec
+* (1 octet) channel_map        : per spec
+* (1 octet) adv_filter_policy  : per spec
+* (1 octet) inst_id            : Specifies the applicability of the above parameters to an instance
+* (1 octet) tx_power           : Transmit_Power Unit - in dBm (signed integer) Range (70 to +20)
+*/
+typedef struct {
+       uint8_t subcode;
+       uint16_t adv_int_min;
+       uint16_t adv_int_max;
+       uint8_t adv_type;
+       uint8_t bdaddr_type;
+       bdaddr_t bdaddr;
+       uint8_t direct_bdaddr_type;
+       bdaddr_t direct_bdaddr;
+       uint8_t channel_map;
+       uint8_t adv_filter_policy;
+       uint8_t inst_id;
+       uint8_t tx_power;
+} __attribute__ ((packed)) adapter_le_vsc_cp_set_multi_adv_params;
+
+/**
+*
+* CP for SUB_CMD_LE_MULTI_ADV_WRITE_ADV_DATA
+* CP for SUB_CMD_LE_MULTI_ADV_WRITE_SCAN_RSP_DATA
+*
+* ( 1 octet) subcode     : SUB_CMD_LE_MULTI_ADV_WRITE_ADV_DATA
+*                          or SUB_CMD_LE_MULTI_ADV_WRITE_SCAN_RSP_DATA
+* ( 1 octet) data_len    : per spec
+* (31 octet) data        : per spec
+* ( 1 octet) inst_id     : Specifies the applicability of the above parameters to an instance.
+*/
+typedef struct {
+       uint8_t subcode;
+       uint8_t data_len;
+       uint8_t data[31];
+       uint8_t inst_id;
+} __attribute__ ((packed)) adapter_le_vsc_cp_set_multi_adv_data;
+
+/**
+*
+* CP for SUB_CMD_LE_MULTI_ADV_ENB
+*
+* (1 octet) subcode     : SUB_CMD_LE_MULTI_ADV_ENB
+* (1 octet) enable      : When set to 1, it means enable, otherwise disable.
+* (1 octet) inst_id     : Specifies the applicability of the above parameters
+*                         to an instance. Instance 0 has special meaning this
+*                         refers to std HCI instance.
+*/
+typedef struct {
+       uint8_t subcode;
+       uint8_t enable;
+       uint8_t inst_id;
+} __attribute__ ((packed)) adapter_le_vsc_cp_enable_multi_adv;
+
+/**
+*
+* RP
+*
+* (1 octet) status      : Command complete status
+* (1 octet) subcode     : subcode of OCF_BCM_LE_MULTI_ADV
+*/
+typedef struct {
+       uint8_t status;
+       uint8_t subcode;
+} __attribute__ ((packed)) apater_le_vsc_rp_multi_adv;
+
+
+
+/*****************************************************************************
+**                          CP & RP for OCF_BCM_LE_SCAN_FILTER
+**
+*/
+
+
+/* CP for SUB_CMD_LE_META_PF_ENABLE */
+typedef struct {
+       uint8_t subcode;
+       uint8_t enable;
+} __attribute__ ((packed)) adapter_le_vsc_cp_enable_scan_filter;
+
+/* RP for SUB_CMD_LE_META_PF_ENABLE */
+typedef struct {
+       uint8_t status;
+       uint8_t subcode;
+       uint8_t enable;
+} __attribute__ ((packed)) apater_le_vsc_rp_enable_scan_filter;
+
+/* CP for SUB_CMD_LE_META_PF_FEAT_SEL */
+typedef struct {
+       uint8_t  subcode;
+       uint8_t  action;
+       uint8_t  filter_index;
+       uint16_t feature;
+       uint16_t feature_list_logic;
+       uint8_t  filter_logic;
+       int8_t   rssi_high_threshold;
+       uint8_t  delivery_mode;
+       uint16_t onfound_timeout;
+       uint8_t  onfound_timeout_cnt;
+       uint8_t  rssi_low_thresh;
+       uint16_t onlost_timeout;
+} __attribute__ ((packed)) adapter_le_vsc_cp_apcf_set_filter_params;
+
+/* CP for SUB_CMD_LE_META_PF_ADDR */
+typedef struct {
+       uint8_t subcode;
+       uint8_t action;
+       uint8_t filter_index;
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+} __attribute__ ((packed))adapter_le_vsc_cp_address_scan_filter;
+
+/* CP for SUB_CMD_LE_META_PF_UUID & SUB_CMD_LE_META_PF_SOL_UUID */
+typedef struct {
+       uint8_t subcode;
+       uint8_t action;
+       uint8_t filter_index;
+       uint8_t data[40];               /* UUID + UUID_MASK */
+} __attribute__ ((packed))adapter_le_vsc_cp_service_uuid_scan_filter;
+#define UUID_SCAN_FILTER_HEADER_SIZE 3
+
+#define SCAN_FILTER_DATA_MAX_LEN 29
+
+/* CP for SUB_CMD_LE_META_PF_LOCAL_NAME*/
+typedef struct {
+       uint8_t subcode;
+       uint8_t action;
+       uint8_t filter_index;
+       uint8_t name[SCAN_FILTER_DATA_MAX_LEN];
+} __attribute__ ((packed)) adapter_le_vsc_cp_local_name_scan_filter;
+#define NAME_SCAN_FILTER_HEADER_SIZE 3
+
+/* CP for SUB_CMD_LE_META_PF_MANU_DATA*/
+typedef struct {
+       uint8_t subcode;
+       uint8_t action;
+       uint8_t filter_index;
+       uint8_t data[SCAN_FILTER_DATA_MAX_LEN * 2];     /* data + mask filed */
+} __attribute__ ((packed)) adapter_le_vsc_cp_manf_data_scan_filter;
+#define MANF_DATA_SCAN_FILTER_HEADER_SIZE 3
+
+/* CP for SUB_CMD_LE_META_PF_SRVC_DATA*/
+typedef struct {
+       uint8_t subcode;
+       uint8_t action;
+       uint8_t filter_index;
+       uint8_t data[SCAN_FILTER_DATA_MAX_LEN * 2];     /* data + mask filed */
+} __attribute__ ((packed)) adapter_le_vsc_cp_service_data_scan_filter;
+#define SERVICE_DATA_SCAN_FILTER_HEADER_SIZE 3
+
+/* RP for SUB_CMD_LE_META_PF_ADDR & SUB_CMD_LE_META_PF_FEAT_SEL &
+               SUB_CMD_LE_META_PF_UUID & SUB_CMD_LE_META_PF_SOL_UUID &
+               SUB_CMD_LE_META_PF_LOCAL_NAME & SUB_CMD_LE_META_PF_MANU_DATA &
+               SUB_CMD_LE_META_PF_SRVC_DATA */
+typedef struct {
+       uint8_t status;
+       uint8_t subcode;
+       uint8_t         action;
+       uint8_t         available_space;
+} __attribute__ ((packed)) adapter_le_vsc_rp_apcf_set_scan_filter;
+
+
+/*****************************************************************************
+**                          CP & RP for OCF_BCM_LE_RPA_OFFLOAD
+**
+*/
+
+/**
+*
+* CP for  SUB_CMD_ENABLE_RPA_OFFLOAD
+*
+* (1 octet) subcode    : SUB_CMD_ENABLE_RPA_OFFLOAD (0x01)
+* (2 octet) enable      : When set to 1, it means enable, otherwise disable.
+*/
+typedef struct {
+       uint8_t subcode;
+       uint8_t enable;
+} __attribute__ ((packed)) adapter_le_vsc_cp_enable_rpa_offload;
+
+/* RP for SUB_CMD_ENABLE_RPA_OFFLOAD */
+typedef struct {
+       uint8_t status;
+       uint8_t subcode;
+} __attribute__ ((packed)) adapter_le_vsc_rp_enable_rpa_offload;
+
+/**
+*
+* CP for  SUB_CMD_ADD_IRK_TO_LIST
+*
+* (1 octet) subcode    : SUB_CMD_ADD_IRK_TO_LIST (0x02)
+* (16 octet) le_irk      : LE IRK (1st byte LSB)
+* (1 octet) bdaddr_type : per spec
+* (6 octet) bdaddr : per spec
+*/
+typedef struct {
+       uint8_t subcode;
+       uint8_t le_irk[16];
+       uint8_t bdaddr_type;
+       bdaddr_t bdaddr;
+} __attribute__ ((packed)) adapter_le_vsc_cp_add_irk_to_list;
+
+/**
+*
+* CP for  SUB_CMD_REMOVE_IRK_TO_LIST
+*
+* (1 octet) subcode    : SUB_CMD_REMOVE_IRK_TO_LIST (0x03)
+* (16 octet) le_irk      : LE IRK (1st byte LSB)
+* (1 octet) bdaddr_type : per spec
+* (6 octet) bdaddr : per spec
+*/
+typedef struct {
+       uint8_t subcode;
+       uint8_t bdaddr_type;
+       bdaddr_t bdaddr;
+} __attribute__ ((packed)) adapter_le_vsc_cp_remove_irk_to_list;
+
+/* RP for SUB_CMD_ADD_IRK_TO_LIST & SUB_CMD_REMOVE_IRK_TO_LIST */
+typedef struct {
+       uint8_t status;
+       uint8_t subcode;
+       uint8_t available_space;
+} __attribute__ ((packed)) adapter_le_vsc_rp_irk_to_list;
+
+
+/*****************************************************************************
+**                          Functions
+**
+*/
+
+/* Read supported BLE feature info from chipset */
+gboolean adapter_le_read_ble_feature_info(void);
+
+gboolean adapter_le_is_supported_multi_advertising(void);
+
+gboolean adapter_le_is_supported_offloading(void);
+
+int adapter_le_get_max_adv_instance(void);
+
+int adapter_le_get_scan_filter_size(void);
+
+gboolean adapter_le_set_multi_adv_params (adapter_le_adv_inst_info_t *p_inst,
+                                       adapter_le_adv_param_t *p_params);
+
+gboolean adapter_le_set_multi_adv_data(uint8_t inst_id, gboolean is_scan_rsp,
+                                       uint8_t data_len, uint8_t *p_data);
+
+gboolean adapter_le_enable_multi_adv (struct btd_adapter *adapter,
+                                       gboolean enable, uint8_t inst_id);
+
+gboolean adapter_le_enable_scan_filtering (gboolean enable);
+
+gboolean adapter_le_set_scan_filter_params(adapter_le_scan_filter_param_t *params);
+
+gboolean adapter_le_set_scan_filter_data(int client_if, int action,
+                                                       int filt_type, int filter_index,
+                                                       int company_id,
+                                                       int company_id_mask,
+                                                       int uuid_len, uint8_t *p_uuid,
+                                                       int uuid_mask_len, uint8_t *p_uuid_mask,
+                                                       gchar *string, int addr_type,
+                                                       int data_len, uint8_t *p_data,
+                                                       int mask_len, uint8_t *p_mask);
+gboolean adapter_le_clear_scan_filter_data(int client_if, int filter_index);
+
+gboolean adapter_le_enable_offloading(gboolean enable);
+
+gboolean adapter_le_add_irk_to_list(const uint8_t *le_irk, const bdaddr_t *bdaddr, uint8_t bdaddr_type);
+
+gboolean adapter_le_remove_irk_to_list(const bdaddr_t *bdaddr, uint8_t bdaddr_type);
+
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
diff --git a/src/attio.h b/src/attio.h
new file mode 100755 (executable)
index 0000000..16e2873
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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
+ *
+ */
+
+typedef void (*attio_connect_cb) (GAttrib *attrib, gpointer user_data);
+typedef void (*attio_disconnect_cb) (gpointer user_data);
+
+guint btd_device_add_attio_callback(struct btd_device *device,
+                                               attio_connect_cb cfunc,
+                                               attio_disconnect_cb dcfunc,
+                                               gpointer user_data);
+
+gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id);
index 4439c27..de6919d 100755 (executable)
@@ -255,6 +255,37 @@ static int attribute_cmp(gconstpointer a1, gconstpointer a2)
        return attrib1->handle - attrib2->handle;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static int attribute_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct attribute *attrib1 = a;
+       const bt_uuid_t *uuid = b;
+
+       return bt_uuid_cmp(&attrib1->uuid, uuid);
+}
+
+struct attribute *attribute_find(struct btd_adapter *adapter, const bt_uuid_t *uuid)
+{
+       GSList *l;
+       GList *ldata;
+       struct gatt_server *server;
+
+       /* Find the attrib server database for the given adapter */
+       l = g_slist_find_custom(servers, adapter, adapter_cmp);
+       if (!l)
+               return NULL;
+
+       server = l->data;
+
+       ldata = g_list_find_custom(server->database, GUINT_TO_POINTER(uuid),
+                                                       attribute_uuid_cmp);
+       if (!ldata)
+               return NULL;
+
+       return ldata->data;
+}
+#endif
+
 static struct attribute *find_svc_range(struct gatt_server *server,
                                        uint16_t start, uint16_t *end)
 {
@@ -306,6 +337,10 @@ static uint32_t attrib_create_sdp_new(struct gatt_server *server,
 
        if (a->len == 2)
                sdp_uuid16_create(&svc, get_le16(a->data));
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       else if (a->len == 4)
+               sdp_uuid32_create(&svc, get_le32(a->data));
+#endif
        else if (a->len == 16) {
                uint8_t be128[16];
 
@@ -320,14 +355,20 @@ static uint32_t attrib_create_sdp_new(struct gatt_server *server,
                return 0;
 
        if (name != NULL)
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               sdp_set_info_attr(record, name, "Samsung", NULL);
+#else
                sdp_set_info_attr(record, name, "BlueZ", NULL);
+#endif
 
        sdp_uuid16_create(&gap_uuid, GENERIC_ACCESS_PROFILE_ID);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) {
                sdp_set_url_attr(record, "http://www.bluez.org/",
                                "http://www.bluez.org/",
                                "http://www.bluez.org/");
        }
+#endif
 
        if (adapter_service_add(server->adapter, record) == 0)
                return record->handle;
@@ -498,6 +539,10 @@ static uint16_t read_by_group(struct gatt_channel *channel, uint16_t start,
 
        if (dl == NULL)
                cur->end = a->handle;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       else if (a->handle == end && end == 0xffff)
+               cur->end = a->handle;
+#endif
        else
                cur->end = last_handle;
 
@@ -936,6 +981,35 @@ static uint16_t write_value(struct gatt_channel *channel, uint16_t handle,
                        g_file_set_contents(filename, data, length, NULL);
                }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               g_free(filename);
+               filename = btd_device_get_storage_path(channel->device, "ccc_sc");
+               if (!filename) {
+                       warn("Unable to get ccc storage path for device");
+                       g_free(data);
+                       return enc_error_resp(ATT_OP_WRITE_REQ, handle,
+                                               ATT_ECODE_WRITE_NOT_PERM,
+                                               pdu, len);
+               }
+
+               g_key_file_free(key_file);
+               key_file = g_key_file_new();
+               g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+               memset(&group, 0x00, 6);
+               memset(&value, 0x00, 5);
+               sprintf(group, "%hu", handle);
+               sprintf(value, "%hX", cccval);
+               g_key_file_set_string(key_file, group, "Value", value);
+
+               g_free(data);
+               data = g_key_file_to_data(key_file, &length, NULL);
+               if (length > 0) {
+                       create_file(filename, S_IRUSR | S_IWUSR);
+                       g_file_set_contents(filename, data, length, NULL);
+               }
+#endif
+
                g_free(data);
                g_free(filename);
                g_key_file_free(key_file);
@@ -1066,6 +1140,12 @@ static void channel_handler(const uint8_t *ipdu, uint16_t len,
                }
 
                length = find_info(channel, start, end, opdu, channel->mtu);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               if (length == 0 && start == end) {
+                       status = ATT_ECODE_ATTR_NOT_FOUND;
+                       goto done;
+               }
+#endif
                break;
        case ATT_OP_WRITE_REQ:
                length = dec_write_req(ipdu, len, &start, value, &vlen);
@@ -1289,6 +1369,9 @@ static gboolean register_core_services(struct gatt_server *server)
        uint8_t atval[256];
        bt_uuid_t uuid;
        uint16_t appearance = 0x0000;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint16_t service_changed_handle;
+#endif
 
        /* GAP service: primary service definition */
        bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
@@ -1337,6 +1420,29 @@ static gboolean register_core_services(struct gatt_server *server)
        attrib_db_add_new(server, 0x0010, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
                                                                atval, 2);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* GATT service: service changed characteristic */
+       service_changed_handle = 0x0012;
+       bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+
+       atval[0] = GATT_CHR_PROP_INDICATE;
+       put_le16(service_changed_handle, &atval[1]);
+       put_le16(GATT_CHARAC_SERVICE_CHANGED, &atval[3]);
+
+       attrib_db_add_new(server, 0x0011, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                       atval, 5);
+
+       /* GATT service: service changed attribute */
+       bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
+       attrib_db_add_new(server, service_changed_handle, &uuid, ATT_NOT_PERMITTED,
+                                               ATT_NOT_PERMITTED, NULL, 0);
+
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+       atval[0] = GATT_CHR_PROP_READ | GATT_CHR_PROP_WRITE;
+       atval[1] = 0;
+       attrib_db_add_new(server, 0x0013, &uuid, ATT_NONE, ATT_NONE, atval, 2);
+#endif
+
        server->gatt_sdp_handle = attrib_create_sdp_new(server, 0x0010,
                                                "Generic Attribute Profile");
        if (server->gatt_sdp_handle == 0) {
@@ -1623,6 +1729,97 @@ int attrib_db_del(struct btd_adapter *adapter, uint16_t handle)
        return 0;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+uint16_t send_sc_indication(uint16_t start_handle, uint16_t end_handle, size_t vlen,
+                                               uint8_t *pdu, size_t len)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (len < (vlen + min_len))
+               return 0;
+
+       pdu[0] = ATT_OP_HANDLE_IND;
+/*     API replaced by put_le16 in bluez 5.25
+       att_put_u16(start_handle, &pdu[1]);
+       att_put_u16(end_handle, &pdu[3]);*/
+       put_le16(start_handle, &pdu[1]);
+       put_le16(end_handle, &pdu[3]);
+
+       return vlen + min_len;
+}
+
+static uint8_t attrib_get_ccc_info(struct btd_device *device, uint16_t handle)
+{
+       uint16_t cccval = 0;
+       char *filename;
+       GKeyFile *key_file;
+       char group[6];
+       char *value;
+
+       filename = btd_device_get_storage_path(device, "ccc");
+       if (!filename) {
+               warn("Unable to get ccc storage path for device");
+               return 0;
+       }
+
+       key_file = g_key_file_new();
+       g_key_file_load_from_file(key_file, filename, 0, NULL);
+       sprintf(group, "%hu", handle);
+
+       /* Get the CCC value */
+       value = g_key_file_get_string(key_file, group, "Value", NULL);
+       if (!value)
+               return 0;
+
+       sscanf(value, "%hX", &cccval);
+
+       g_free(value);
+       g_free(filename);
+       g_key_file_free(key_file);
+
+       return cccval;
+}
+
+void attrib_send_sc_ind(struct btd_device *device, GAttrib *attrib,
+                               uint16_t start_handle, uint16_t end_handle,
+                               size_t vlen)
+{
+       size_t length = 0;
+       uint8_t *pdu;
+       size_t mtu;
+
+       pdu = g_attrib_get_buffer(attrib, &mtu);
+       length = send_sc_indication(start_handle, end_handle, vlen, pdu, mtu);
+       g_attrib_send(attrib, 0, pdu, length, NULL, NULL, NULL);
+}
+
+void attrib_send_noty_ind(struct btd_device *device, GAttrib *attrib,
+                               uint16_t handle, uint16_t desc_handle,
+                               uint8_t *value, size_t vlen)
+{
+       size_t length = 0;
+       uint16_t cccval;
+       uint8_t *pdu;
+       size_t mtu;
+
+       cccval = attrib_get_ccc_info(device, desc_handle);
+       if (!cccval)
+               return;
+
+       pdu = g_attrib_get_buffer(attrib, &mtu);
+       if (cccval == GATT_CLIENT_CHARAC_CFG_NOTIF_BIT) {
+               length = enc_notification(handle, value, vlen, pdu, mtu);
+               g_attrib_send(attrib, 0, pdu, length, NULL, NULL, NULL);
+       } else if (cccval == GATT_CLIENT_CHARAC_CFG_IND_BIT) {
+               length = enc_indication(handle, value, vlen, pdu, mtu);
+               g_attrib_send(attrib, 0, pdu, length, NULL, NULL, NULL);
+       }
+}
+#endif
+
 int attrib_gap_set(struct btd_adapter *adapter, uint16_t uuid,
                                        const uint8_t *value, size_t len)
 {
index 063cb66..2a951e4 100755 (executable)
@@ -40,3 +40,15 @@ void attrib_free_sdp(struct btd_adapter *adapter, uint32_t sdp_handle);
 GAttrib *attrib_from_device(struct btd_device *device);
 guint attrib_channel_attach(GAttrib *attrib);
 gboolean attrib_channel_detach(GAttrib *attrib, guint id);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct attribute *attribute_find(struct btd_adapter *adapter, const bt_uuid_t *uuid);
+void attrib_send_noty_ind(struct btd_device *device, GAttrib *attrib,
+                               uint16_t handle, uint16_t desc_handle,
+                               uint8_t *value, size_t vlen);
+uint16_t send_sc_indication(uint16_t handle, uint16_t end_handle, size_t vlen,
+                        uint8_t *pdu, size_t len);
+
+void attrib_send_sc_ind(struct btd_device *device, GAttrib *attrib,
+                               uint16_t start_handle, uint16_t end_handle,
+                               size_t vlen);
+#endif
index 10d2d36..2add5d7 100755 (executable)
   <policy user="root">
     <allow own="org.bluez"/>
     <allow send_destination="org.bluez"/>
+    <allow own="org.projectx.bluetooth"/>
+    <allow send_interface="org.projectx.bluetooth"/>
+    <allow send_destination="org.projectx.bluetooth"/>
+    <allow send_interface="org.projectx.bt_event"/>
+    <allow send_destination="org.projectx.bt_event"/>
+    <allow own="org.bluez.frwk_agent"/>
+    <allow send_interface="org.bluez.frwk_agent"/>
+    <allow send_destination="org.bluez.frwk_agent"/>
+    <allow own="org.bluez.Agent1"/>
     <allow send_interface="org.bluez.Agent1"/>
+    <allow send_destination="org.bluez.Agent1"/>
+    <allow own="org.bluez.Adapter1"/>
+    <allow send_interface="org.bluez.Adapter1"/>
+    <allow send_destination="org.bluez.Adapter1"/>
+    <allow own="org.bluez.Manager"/>
+    <allow send_interface="org.bluez.Manager"/>
+    <allow send_destination="org.bluez.Manager"/>
+    <allow own="org.bluez.Device1"/>
+    <allow send_interface="org.bluez.Device1"/>
+    <allow send_destination="org.bluez.Device1"/>
+    <allow own="org.bluez.MediaEndpoint1"/>
     <allow send_interface="org.bluez.MediaEndpoint1"/>
+    <allow send_destination="org.bluez.MediaEndpoint1"/>
+    <allow own="org.bluez.MediaPlayer1"/>
     <allow send_interface="org.bluez.MediaPlayer1"/>
     <allow send_interface="org.bluez.ThermometerWatcher1"/>
     <allow send_interface="org.bluez.AlertAgent1"/>
+    <allow send_destination="org.bluez.MediaPlayer1"/>
+    <allow own="org.bluez.MediaTransport1"/>
+    <allow send_interface="org.bluez.MediaTransport1"/>
+    <allow send_destination="org.bluez.MediaTransport1"/>
+    <allow own="org.bluez.Profile1"/>
     <allow send_interface="org.bluez.Profile1"/>
+    <allow send_destination="org.bluez.Profile1"/>
     <allow send_interface="org.bluez.HeartRateWatcher1"/>
     <allow send_interface="org.bluez.CyclingSpeedWatcher1"/>
     <allow send_interface="org.bluez.GattCharacteristic1"/>
     <allow send_destination="org.bluez"/>
   </policy>
 
-  <!-- allow users of lp group (printing subsystem) to 
+  <!-- allow users of bt_use group (Tizen BT group) to
+       communicate with bluetoothd -->
+  <policy group="bt_use">
+    <allow send_interface="org.freedesktop.DBus.ObjectManager"/>
+    <allow send_destination="org.bluez"/>
+    <allow send_interface="org.projectx.bluetooth"/>
+    <allow send_destination="org.projectx.bluetooth"/>
+    <allow send_interface="org.projectx.bt_event"/>
+    <allow send_destination="org.projectx.bt_event"/>
+    <allow send_interface="org.bluez.frwk_agent"/>
+    <allow send_destination="org.bluez.frwk_agent"/>
+    <allow send_interface="org.bluez.Agent1"/>
+    <allow send_destination="org.bluez.Agent1"/>
+    <allow send_interface="org.bluez.Adapter1"/>
+    <allow send_destination="org.bluez.Adapter1"/>
+    <allow send_interface="org.bluez.Manager"/>
+    <allow send_destination="org.bluez.Manager"/>
+    <allow send_interface="org.bluez.Device1"/>
+    <allow send_destination="org.bluez.Device1"/>
+    <allow send_interface="org.bluez.MediaEndpoint1"/>
+    <allow send_destination="org.bluez.MediaEndpoint1"/>
+    <allow send_interface="org.bluez.MediaTransport1"/>
+    <allow send_destination="org.bluez.MediaTransport1"/>
+    <allow send_interface="org.bluez.MediaPlayer1"/>
+    <allow send_destination="org.bluez.MediaPlayer1"/>
+    <allow send_interface="org.bluez.Profile1"/>
+    <allow send_destination="org.bluez.Profile1"/>
+  </policy>
+
+  <!-- allow users of system group (Tizen BT group) to
+       communicate with bluetoothd -->
+  <policy group="users">
+    <allow send_interface="org.freedesktop.DBus.ObjectManager"/>
+    <allow send_destination="org.bluez"/>
+    <allow send_interface="org.projectx.bluetooth"/>
+    <allow send_destination="org.projectx.bluetooth"/>
+    <allow send_interface="org.projectx.bt_event"/>
+    <allow send_destination="org.projectx.bt_event"/>
+    <allow send_interface="org.bluez.frwk_agent"/>
+    <allow send_destination="org.bluez.frwk_agent"/>
+    <allow send_interface="org.bluez.Agent1"/>
+    <allow send_destination="org.bluez.Agent1"/>
+    <allow send_interface="org.bluez.Adapter1"/>
+    <allow send_destination="org.bluez.Adapter1"/>
+    <allow send_interface="org.bluez.Manager"/>
+    <allow send_destination="org.bluez.Manager"/>
+    <allow send_interface="org.bluez.Device1"/>
+    <allow send_destination="org.bluez.Device1"/>
+    <allow send_interface="org.bluez.MediaEndpoint1"/>
+    <allow send_destination="org.bluez.MediaEndpoint1"/>
+    <allow send_interface="org.bluez.MediaTransport1"/>
+    <allow send_destination="org.bluez.MediaTransport1"/>
+    <allow send_interface="org.bluez.MediaPlayer1"/>
+    <allow send_destination="org.bluez.MediaPlayer1"/>
+    <allow send_interface="org.bluez.Profile1"/>
+    <allow send_destination="org.bluez.Profile1"/>
+  </policy>
+
+  <!-- allow users of lp group (printing subsystem) to
        communicate with bluetoothd -->
   <policy group="lp">
     <allow send_destination="org.bluez"/>
   </policy>
 
   <policy context="default">
-    <deny send_destination="org.bluez"/>
+    <deny send_interface="org.projectx.bluetooth"/>
+    <deny send_destination="org.projectx.bluetooth"/>
+    <deny send_interface="org.bluez.frwk_agent"/>
+    <deny send_destination="org.bluez.frwk_agent"/>
+    <deny send_interface="org.bluez.Agent1"/>
+    <deny send_destination="org.bluez.Agent1"/>
+    <deny send_interface="org.bluez.Adapter1"/>
+    <deny send_destination="org.bluez.Adapter1"/>
+    <deny send_interface="org.bluez.Manager"/>
+    <deny send_destination="org.bluez.Manager"/>
+    <deny send_interface="org.bluez.Device1"/>
+    <deny send_destination="org.bluez.Device1"/>
+    <deny send_interface="org.bluez.MediaEndpoint1"/>
+    <deny send_destination="org.bluez.MediaEndpoint1"/>
+    <deny send_interface="org.bluez.MediaTransport1"/>
+    <deny send_destination="org.bluez.MediaTransport1"/>
+    <deny send_interface="org.bluez.MediaPlayer1"/>
+    <deny send_destination="org.bluez.MediaPlayer1"/>
+    <deny send_interface="org.bluez.Profile1"/>
+    <deny send_destination="org.bluez.Profile1"/>
   </policy>
 
 </busconfig>
index f799f65..e41c2da 100755 (executable)
@@ -1,20 +1,17 @@
 [Unit]
 Description=Bluetooth service
 Documentation=man:bluetoothd(8)
-ConditionPathIsDirectory=/sys/class/bluetooth
 
 [Service]
 Type=dbus
 BusName=org.bluez
-ExecStart=@libexecdir@/bluetoothd
+ExecStart=/usr/libexec/bluetooth/bluetoothd -d -C
 NotifyAccess=main
-#WatchdogSec=10
-#Restart=on-failure
 CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
 LimitNPROC=1
 ProtectHome=true
 ProtectSystem=full
 
 [Install]
-WantedBy=bluetooth.target
+WantedBy=multi-user.target
 Alias=dbus-org.bluez.service
index 99454a7..217e402 100755 (executable)
 #include "attrib-server.h"
 #include "eir.h"
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "sdp-xml.h"
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+#include <sys/ioctl.h>
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
+
 #define IO_CAPABILITY_NOINPUTNOOUTPUT  0x03
 
 #define DISCONNECT_TIMER       2
 #define GATT_INCLUDE_UUID_STR "2802"
 #define GATT_CHARAC_UUID_STR "2803"
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define DEV_MAX_MANUFACTURER_DATA_LEN  248
+#endif
+
 static DBusConnection *dbus_conn = NULL;
 static unsigned service_state_cb_id;
 
@@ -159,6 +170,14 @@ struct svc_callback {
        void *user_data;
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct le_adv_report_info {
+       uint8_t flags;
+       char manufacturer_data[DEV_MAX_MANUFACTURER_DATA_LEN];
+       uint8_t manufacturer_data_len;
+};
+#endif
+
 /* Per-bearer (LE or BR/EDR) device state */
 struct bearer_state {
        bool paired;
@@ -172,6 +191,33 @@ struct csrk_info {
        uint32_t counter;
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+typedef enum {
+       DEV_PAIRED_NONE = 0,
+       DEV_PAIRED_BREDR = 1,
+       DEV_PAIRED_LE,
+       DEV_PAIRED_BREDR_LE,
+} dev_paired_state;
+
+typedef enum {
+       DEV_CONNECTED_NONE = 0,
+       DEV_CONNECTED_BREDR = 1,
+       DEV_CONNECTED_LE,
+       DEV_CONNECTED_BREDR_LE,
+} dev_connected_state;
+
+struct trusted_profile_t {
+       uint32_t        pbap:2;
+       uint32_t        map:2;
+       uint32_t        sap:2;
+} __packed;
+
+struct restricted_profile_t {
+       uint32_t        hfp_hs;
+       uint32_t        a2dp;
+};
+#endif
+
 struct btd_device {
        int ref_count;
 
@@ -239,6 +285,10 @@ struct btd_device {
        time_t          le_seen;
 
        gboolean        trusted;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct trusted_profile_t trusted_profiles;
+       struct restricted_profile_t restricted_profiles;
+#endif
        gboolean        blocked;
        gboolean        auto_connect;
        gboolean        disable_auto_connect;
@@ -250,6 +300,30 @@ struct btd_device {
 
        GIOChannel      *att_io;
        guint           store_id;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       bool    legacy_pairing;
+       char            *manufacturer_data;
+       int             manufacturer_data_len;
+       struct le_adv_report_info le_adv_data;
+       int     remote_feature_flags;
+       guint   attio_id;
+       gboolean        gatt_connected;
+       uint16_t        auth_payload_timeout;
+       uint8_t disc_reason;
+       uint8_t         last_bdaddr_type;
+       uint8_t         auth_bdaddr_type;
+       gboolean        ipsp_connected; /* IPSP Connection state */
+       char            if_name[16 + 1]; /* BT interface UP after IPSP connection */
+       uint8_t         rpa_res_support; /* RPA Resolution capability of device */
+       uint16_t        max_tx_octets;
+       uint16_t        max_tx_time;
+       uint16_t        max_rx_octets;
+       uint16_t        max_rx_time;
+       bdaddr_t        *rpa;
+       DBusMessage *req_att_mtu;       /* Attribute MTU request message */
+       uint8_t         irk_val[16];
+       bool pending_conn_update;
+#endif
 };
 
 static const uint16_t uuid_list[] = {
@@ -259,6 +333,29 @@ static const uint16_t uuid_list[] = {
        0
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+typedef enum {
+       SHOW_AUTHORIZATION = 0x0, /* 0b00 */
+       SUPPORTED_BLOCKED = 0x1, /* 0b01 */
+       SUPPORTED_TRUSTED= 0x2, /* 0b10 */
+} bt_profile_trusted_states;
+
+#define PBAP_SHIFT_OFFSET 0
+#define MAP_SHIFT_OFFSET 2
+#define SAP_SHIFT_OFFSET 4
+
+#define PROFILE_SUPPORTED 0x3 /* This corresponds to binary 0b11*/
+
+typedef enum {
+       CONNECTION_PERMITTED = 0x0, /* 0b00 */
+       CONNECTION_RESTRICTED = 0x1, /* 0b01 */
+} bt_profile_restricted_states;
+
+#define HFP_HS_SHIFT_OFFSET 0
+#define A2DP_SHIFT_OFFSET 2
+
+#endif
+
 static int device_browse_gatt(struct btd_device *device, DBusMessage *msg);
 static int device_browse_sdp(struct btd_device *device, DBusMessage *msg);
 
@@ -353,6 +450,42 @@ static void store_csrk(struct csrk_info *csrk, GKeyFile *key_file,
        g_key_file_set_integer(key_file, group, "Counter", csrk->counter);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static char *manufacturer_data2str(char *data, int size)
+{
+       char str[DEV_MAX_MANUFACTURER_DATA_LEN * 3 + 1];
+       char tmp[5];
+       int i;
+
+       str[0] = '\0';
+       for(i = 0; i < size; i++) {
+               snprintf(tmp, sizeof(tmp), "%d ", data[i]);
+               g_strlcat(str, tmp, sizeof(str));
+       }
+
+       return g_strdup(str);
+}
+
+static void load_manufacturer_data_2digit(char *data, int len, char *buf)
+{
+       int i;
+       char **split;
+
+       split = g_strsplit(data, " ", 0);
+
+       for (i = 0; i < len; i++) {
+               if (split[i] == NULL)
+                       break;
+
+               buf[i] = (char)g_ascii_strtoull(split[i], NULL, 10);
+       }
+
+       g_strfreev(split);
+
+       return;
+}
+#endif
+
 static gboolean store_device_info_cb(gpointer user_data)
 {
        struct btd_device *device = user_data;
@@ -364,11 +497,19 @@ static gboolean store_device_info_cb(gpointer user_data)
        char class[9];
        char **uuids = NULL;
        gsize length = 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       gboolean svc_change_regd = false;
+#endif
 
        device->store_id = 0;
 
        ba2str(btd_adapter_get_address(device->adapter), adapter_addr);
        ba2str(&device->bdaddr, device_addr);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (device->rpa)
+               ba2str(device->rpa, device_addr);
+#endif
        snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
                        device_addr);
 
@@ -397,11 +538,36 @@ static gboolean store_device_info_cb(gpointer user_data)
                g_key_file_remove_key(key_file, "General", "Appearance", NULL);
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (device->rpa_res_support) {
+               g_key_file_set_integer(key_file, "General", "RPAResSupport",
+                                       device->rpa_res_support);
+       } else {
+               g_key_file_remove_key(key_file, "General", "RPAResSupport", NULL);
+       }
+#endif
+
        update_technologies(key_file, device);
 
        g_key_file_set_boolean(key_file, "General", "Trusted",
                                                        device->trusted);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct trusted_profile_t trust_profile = device->trusted_profiles;
+       int trusted_profiles = (trust_profile.pbap << PBAP_SHIFT_OFFSET) |
+                       (trust_profile.map << MAP_SHIFT_OFFSET) |
+                       (trust_profile.sap << SAP_SHIFT_OFFSET);
+       DBG("Storing TrustedProfiles %d", trusted_profiles);
+       g_key_file_set_integer(key_file, "General", "TrustedProfiles",
+                                                       trusted_profiles);
+
+       struct restricted_profile_t restrict_profile = device->restricted_profiles;
+       int restricted_profiles = (restrict_profile.hfp_hs << HFP_HS_SHIFT_OFFSET) |
+                       (restrict_profile.a2dp << A2DP_SHIFT_OFFSET);
+       DBG("Storing RestrictedProfiles %d", restricted_profiles);
+       g_key_file_set_integer(key_file, "General", "RestrictedProfiles",
+                                                       restricted_profiles);
 
+#endif
        g_key_file_set_boolean(key_file, "General", "Blocked",
                                                        device->blocked);
 
@@ -418,6 +584,43 @@ static gboolean store_device_info_cb(gpointer user_data)
                g_key_file_remove_key(key_file, "General", "Services", NULL);
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (device->le_adv_data.flags) {
+               g_key_file_set_integer(key_file, "General", "Flags",
+                                       device->le_adv_data.flags);
+       } else {
+               g_key_file_remove_key(key_file, "General", "Flags", NULL);
+       }
+
+       if (device->manufacturer_data) {
+               str = manufacturer_data2str(device->manufacturer_data,
+                                       device->manufacturer_data_len);
+               g_key_file_set_string(key_file, "General",
+                               "ManufacturerData",
+                               str);
+               g_free(str);
+               g_key_file_set_integer(key_file, "General",
+                               "ManufacturerDataLen",
+                               device->manufacturer_data_len);
+       } else {
+               g_key_file_remove_key(key_file, "General",
+                                               "ManufacturerData", NULL);
+               g_key_file_remove_key(key_file, "General",
+                                               "ManufacturerDataLen", NULL);
+       }
+
+       if (device->rpa) {
+               char irk_addr[18];
+
+               ba2str(&device->bdaddr, irk_addr);
+               g_key_file_set_string(key_file, "General", "IdentityAddress",
+                                                               irk_addr);
+       } else {
+               g_key_file_remove_key(key_file, "General", "IdentityAddress",
+                                                               NULL);
+       }
+#endif
+
        if (device->vendor_src) {
                g_key_file_set_integer(key_file, "DeviceID", "Source",
                                        device->vendor_src);
@@ -431,6 +634,12 @@ static gboolean store_device_info_cb(gpointer user_data)
                g_key_file_remove_group(key_file, "DeviceID", NULL);
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       svc_change_regd = bt_att_get_svc_changed_indication_registered(device->att);
+       g_key_file_set_boolean(key_file, "Att", "SvcChangeRegd",
+                                               svc_change_regd);
+#endif
+
        if (device->local_csrk)
                store_csrk(device->local_csrk, key_file, "LocalSignatureKey");
 
@@ -493,6 +702,12 @@ void device_store_cached_name(struct btd_device *dev, const char *name)
 
        ba2str(btd_adapter_get_address(dev->adapter), s_addr);
        ba2str(&dev->bdaddr, d_addr);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (dev->rpa)
+               ba2str(dev->rpa, d_addr);
+#endif
+
        snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", s_addr, d_addr);
        create_file(filename, S_IRUSR | S_IWUSR);
 
@@ -509,6 +724,9 @@ void device_store_cached_name(struct btd_device *dev, const char *name)
 
 static void browse_request_free(struct browse_req *req)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       DBG("");
+#endif
        if (req->listener_id)
                g_dbus_remove_watch(dbus_conn, req->listener_id);
        if (req->msg)
@@ -644,6 +862,9 @@ static void device_free(gpointer user_data)
        if (device->eir_uuids)
                g_slist_free_full(device->eir_uuids, g_free);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       g_free(device->rpa);
+#endif
        g_free(device->local_csrk);
        g_free(device->remote_csrk);
        g_free(device->path);
@@ -652,6 +873,18 @@ static void device_free(gpointer user_data)
        g_free(device);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void device_set_remote_feature_flag(struct btd_device *device, int flags)
+{
+       device->remote_feature_flags = flags;
+}
+
+gboolean device_is_bredrle(struct btd_device *device)
+{
+       return (device->remote_feature_flags & (EIR_CONTROLLER | EIR_SIM_HOST));
+}
+#endif
+
 bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type)
 {
        struct bearer_state *state = get_state(device, bdaddr_type);
@@ -671,6 +904,53 @@ gboolean device_is_trusted(struct btd_device *device)
        return device->trusted;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+gboolean device_is_profile_trusted(struct btd_device *device,
+               const char *uuid)
+{
+       if (g_strcmp0(uuid, OBEX_PSE_UUID) == 0) {
+               if (device->trusted_profiles.pbap == SUPPORTED_TRUSTED)
+                       return TRUE;
+       } else if (g_strcmp0(uuid, OBEX_MAS_UUID) == 0) {
+               if (device->trusted_profiles.map == SUPPORTED_TRUSTED)
+                       return TRUE;
+       } else if (g_strcmp0(uuid, SAP_UUID) == 0) {
+               if (device->trusted_profiles.sap == SUPPORTED_TRUSTED)
+                       return TRUE;
+       }
+       return FALSE;
+}
+
+gboolean device_is_profile_restricted(struct btd_device *device,
+               const char *uuid)
+{
+       if (g_strcmp0(uuid, HFP_HS_UUID) == 0) {
+               if (device->restricted_profiles.hfp_hs == CONNECTION_RESTRICTED)
+                       return TRUE;
+       } else if (g_strcmp0(uuid, A2DP_SINK_UUID) == 0) {
+               if (device->restricted_profiles.a2dp == CONNECTION_RESTRICTED)
+                       return TRUE;
+       }
+       return FALSE;
+}
+
+gboolean device_is_profile_blocked(struct btd_device *device,
+               const char *uuid)
+{
+       if (g_strcmp0(uuid, OBEX_PSE_UUID) == 0) {
+               if (device->trusted_profiles.pbap == SUPPORTED_BLOCKED)
+                       return TRUE;
+       } else if (g_strcmp0(uuid, OBEX_MAS_UUID) == 0) {
+               if (device->trusted_profiles.map == SUPPORTED_BLOCKED)
+                       return TRUE;
+       } else if (g_strcmp0(uuid, SAP_UUID) == 0) {
+               if (device->trusted_profiles.sap == SUPPORTED_BLOCKED)
+                       return TRUE;
+       }
+       return FALSE;
+}
+#endif
+
 static gboolean dev_property_get_address(const GDBusPropertyTable *property,
                                        DBusMessageIter *iter, void *data)
 {
@@ -678,7 +958,12 @@ static gboolean dev_property_get_address(const GDBusPropertyTable *property,
        char dstaddr[18];
        const char *ptr = dstaddr;
 
-       ba2str(&device->bdaddr, dstaddr);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (device->rpa)
+               ba2str(device->rpa, dstaddr);
+       else
+#endif
+               ba2str(&device->bdaddr, dstaddr);
        dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
 
        return TRUE;
@@ -717,7 +1002,9 @@ static gboolean dev_property_get_alias(const GDBusPropertyTable *property,
                ptr = device->name;
        } else {
                ba2str(&device->bdaddr, dstaddr);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                g_strdelimit(dstaddr, ":", '-');
+#endif
                ptr = dstaddr;
        }
 
@@ -726,6 +1013,24 @@ static gboolean dev_property_get_alias(const GDBusPropertyTable *property,
        return TRUE;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean dev_property_get_alias_set(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct btd_device *device = data;
+       dbus_bool_t val;
+
+       if (device->alias != NULL)
+               val = TRUE;
+       else
+               val = FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+       return TRUE;
+}
+#endif
+
 static void set_alias(GDBusPendingPropertySet id, const char *alias,
                                                                void *data)
 {
@@ -1005,6 +1310,40 @@ static void dev_property_set_trusted(const GDBusPropertyTable *property,
        set_trust(id, b, data);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean dev_property_get_trusted_profiles(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct btd_device *device = data;
+       uint32_t pbap = device->trusted_profiles.pbap;
+       uint32_t map = device->trusted_profiles.map;
+       uint32_t sap = device->trusted_profiles.sap;
+
+       unsigned int val = (pbap << PBAP_SHIFT_OFFSET) |
+                       (map << MAP_SHIFT_OFFSET) |
+                       (sap << SAP_SHIFT_OFFSET);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &val);
+
+       return TRUE;
+}
+
+static gboolean dev_property_get_restricted_profiles(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct btd_device *device = data;
+       uint32_t hfp_hs = device->restricted_profiles.hfp_hs;
+       uint32_t a2dp = device->restricted_profiles.a2dp;
+
+       unsigned int val = (hfp_hs << HFP_HS_SHIFT_OFFSET) |
+                       (a2dp << A2DP_SHIFT_OFFSET);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &val);
+
+       return TRUE;
+}
+#endif
+
 static gboolean dev_property_get_blocked(const GDBusPropertyTable *property,
                                        DBusMessageIter *iter, void *data)
 {
@@ -1060,10 +1399,112 @@ static void dev_property_set_blocked(const GDBusPropertyTable *property,
        set_blocked(id, b, data);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static uint8_t device_get_connected_state(struct btd_device *device)
+{
+       if (device->bredr_state.connected && device->le_state.connected)
+               return DEV_CONNECTED_BREDR_LE;
+       else if (device->bredr_state.connected)
+               return DEV_CONNECTED_BREDR;
+       else if (device->le_state.connected)
+               return DEV_CONNECTED_LE;
+       else
+               return DEV_CONNECTED_NONE;
+}
+
+static gboolean dev_property_get_payload(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct btd_device *dev = data;
+       dbus_uint16_t payload_timeout = dev->auth_payload_timeout;
+
+       dbus_message_iter_append_basic(iter,
+                       DBUS_TYPE_UINT16, &payload_timeout);
+
+       return TRUE;
+}
+
+static gboolean dev_property_get_last_addr_type(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct btd_device *dev = data;
+       uint8_t last_addr_type = dev->last_bdaddr_type;
+
+       dbus_message_iter_append_basic(iter,
+                       DBUS_TYPE_BYTE, &last_addr_type);
+
+       return TRUE;
+}
+
+static gboolean dev_property_get_att_mtu(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct btd_device *device = data;
+       dbus_uint16_t mtu = bt_gatt_client_get_mtu(device->client);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &mtu);
+
+       return TRUE;
+}
+
+static gboolean dev_property_get_gatt_connected(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct btd_device *device = data;
+       dbus_bool_t gatt_connected;
+
+       if (device->gatt_connected)
+               gatt_connected = TRUE;
+       else
+               gatt_connected = FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+                                       &gatt_connected);
+
+       return TRUE;
+}
+
+static gboolean dev_property_get_ipsp_conn_state(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct btd_device *dev = data;
+       dbus_bool_t ipsp_connected;
+
+       if (dev->ipsp_connected)
+               ipsp_connected = TRUE;
+       else
+               ipsp_connected = FALSE;
+
+       dbus_message_iter_append_basic(iter,
+                       DBUS_TYPE_BOOLEAN, &ipsp_connected);
+
+       return TRUE;
+}
+
+static gboolean dev_property_get_ipsp_conn_bt_iface_name(const GDBusPropertyTable *property,
+                               DBusMessageIter *iter, void *data)
+{
+       struct btd_device *dev = data;
+       const char *ptr = g_strdup(dev->if_name);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
+
+       g_free(ptr);
+
+       return TRUE;
+}
+#endif
+
 static gboolean dev_property_get_connected(const GDBusPropertyTable *property,
                                        DBusMessageIter *iter, void *data)
 {
        struct btd_device *dev = data;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint8_t connected = device_get_connected_state(dev);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &connected);
+#else
        dbus_bool_t connected;
 
        if (dev->bredr_state.connected || dev->le_state.connected)
@@ -1072,6 +1513,7 @@ static gboolean dev_property_get_connected(const GDBusPropertyTable *property,
                connected = FALSE;
 
        dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &connected);
+#endif
 
        return TRUE;
 }
@@ -1135,6 +1577,48 @@ static gboolean dev_property_get_adapter(const GDBusPropertyTable *property,
        return TRUE;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean property_get_manufacturer_data_len(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       struct btd_device *device = user_data;
+       dbus_uint16_t val = device->manufacturer_data_len;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &val);
+
+       return TRUE;
+}
+
+static gboolean property_get_manufacturer_data(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       struct btd_device *device = user_data;
+       char str[DEV_MAX_MANUFACTURER_DATA_LEN] = {0};
+       DBusMessageIter array;
+
+       memset(str, 0, DEV_MAX_MANUFACTURER_DATA_LEN);
+       if (device->manufacturer_data_len)
+               memcpy(str, device->manufacturer_data,
+                                       device->manufacturer_data_len);
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                                       DBUS_TYPE_BYTE_AS_STRING, &array);
+
+       dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+                                               &device->manufacturer_data,
+                                               device->manufacturer_data_len);
+
+       dbus_message_iter_close_container(iter, &array);
+
+       return TRUE;
+}
+
+gboolean device_get_gatt_connected(const struct btd_device *device)
+{
+       return device->gatt_connected;
+}
+#endif
+
 static void append_manufacturer_data(void *data, void *user_data)
 {
        struct bt_ad_manufacturer_data *md = data;
@@ -1483,8 +1967,10 @@ static void device_profile_connected(struct btd_device *dev,
        if (!err)
                btd_device_set_temporary(dev, false);
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (dev->pending == NULL)
                goto done;
+#endif
 
        if (!btd_device_is_connected(dev)) {
                switch (-err) {
@@ -1495,6 +1981,10 @@ static void device_profile_connected(struct btd_device *dev,
                }
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (dev->pending == NULL)
+               return;
+#endif
 
        pending = dev->pending->data;
        l = find_service_with_profile(dev->pending, profile);
@@ -1530,10 +2020,28 @@ done:
                g_dbus_send_message(dbus_conn,
                                btd_error_failed(dev->connect, strerror(-err)));
        else {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               /* SDP is not required for Samsung TV Power on */
+               if (g_strcmp0(profile->name, "hid-device") == 0) {
+                       DBG("Skip SDP discovery.");
+               } else {
+#endif
                /* Start passive SDP discovery to update known services */
                if (dev->bredr && !dev->svc_refreshed)
                        device_browse_sdp(dev, NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               }
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               if (err)
+                       g_dbus_send_message(dbus_conn,
+                                       btd_error_failed(dev->connect, strerror(-err)));
+               else
+                       g_dbus_send_reply(dbus_conn, dev->connect, DBUS_TYPE_INVALID);
+#else
                g_dbus_send_reply(dbus_conn, dev->connect, DBUS_TYPE_INVALID);
+#endif
        }
 
        dbus_message_unref(dev->connect);
@@ -1604,7 +2112,9 @@ static struct btd_service *find_connectable_service(struct btd_device *dev,
                                                        const char *uuid)
 {
        GSList *l;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct btd_service *s = NULL;
+#endif
        for (l = dev->services; l != NULL; l = g_slist_next(l)) {
                struct btd_service *service = l->data;
                struct btd_profile *p = btd_service_get_profile(service);
@@ -1612,9 +2122,24 @@ static struct btd_service *find_connectable_service(struct btd_device *dev,
                if (!p->connect || !p->remote_uuid)
                        continue;
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                if (strcasecmp(uuid, p->remote_uuid) == 0)
                        return service;
+#else
+               if (strcasecmp(uuid, p->remote_uuid) == 0) {
+                       s = service;
+                       if (ext_profile_is_registered_as_client_role(p) == TRUE) {
+                               return service;
+                       } else {
+                               continue;
+                       }
+               }
+#endif
        }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (s)
+               return s;
+#endif
 
        return NULL;
 }
@@ -1632,12 +2157,29 @@ static GSList *create_pending_list(struct btd_device *dev, const char *uuid)
        struct btd_service *service;
        struct btd_profile *p;
        GSList *l;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       bool hs_hf_verify = FALSE;
+#endif
 
        if (uuid) {
                service = find_connectable_service(dev, uuid);
                if (service)
                        return g_slist_prepend(dev->pending, service);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               else if ((service == NULL) &&
+                                       (g_strcmp0(uuid, HFP_HS_UUID) == 0)) {
+                       DBG("HFP service not found check for HSP service");
+                       service = find_connectable_service(dev, HSP_HS_UUID);
+                       if (service)
+                               return g_slist_prepend(dev->pending, service);
+               } else if (g_strcmp0(uuid, HID_UUID) == 0) {
+                       DBG("HID service not found, add HID service");
+                       btd_device_add_uuid(dev, uuid);
+                       service = find_connectable_service(dev, HID_UUID);
+                       if (service)
+                               return g_slist_prepend(dev->pending, service);
+               }
+#endif
                return dev->pending;
        }
 
@@ -1645,6 +2187,28 @@ static GSList *create_pending_list(struct btd_device *dev, const char *uuid)
                service = l->data;
                p = btd_service_get_profile(service);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               DBG("profile uuid %s", p->remote_uuid);
+               if (g_strcmp0(p->remote_uuid, HSP_HS_UUID) == 0) {
+                       DBG("HSP service is found check for HFP service");
+                       struct btd_service *service;
+                       struct btd_profile *p;
+                       GSList *h;
+
+                       for (h = dev->services; h != NULL; h = g_slist_next(h)) {
+                               service = h->data;
+                               p = btd_service_get_profile(service);
+
+                               if (g_strcmp0(p->remote_uuid, HFP_HS_UUID) == 0) {
+                                       DBG("HFP found,ignore HSP ");
+                                       hs_hf_verify = TRUE;
+                                       break;
+                               }
+                       }
+                       if (hs_hf_verify)
+                               continue;
+               }
+#endif
                if (!p->auto_connect)
                        continue;
 
@@ -1693,8 +2257,13 @@ static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type
        DBG("%s %s, client %s", dev->path, uuid ? uuid : "(all)",
                                                dbus_message_get_sender(msg));
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (dev->pending || dev->connect)
+               return btd_error_in_progress(msg);
+#else
        if (dev->pending || dev->connect || dev->browse)
                return btd_error_in_progress(msg);
+#endif
 
        if (!btd_adapter_get_powered(dev->adapter))
                return btd_error_not_ready(msg);
@@ -1707,8 +2276,13 @@ static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type
        dev->pending = create_pending_list(dev, uuid);
        if (!dev->pending) {
                if (dev->svc_refreshed) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                       if (!uuid && find_service_with_state(dev->services,
+                                               BTD_SERVICE_STATE_CONNECTED))
+#else
                        if (find_service_with_state(dev->services,
                                                BTD_SERVICE_STATE_CONNECTED))
+#endif
                                return dbus_message_new_method_return(msg);
                        else
                                return btd_error_not_available(msg);
@@ -1892,6 +2466,12 @@ static DBusMessage *disconnect_profile(DBusConnection *conn, DBusMessage *msg,
                return btd_error_invalid_args(msg);
 
        service = find_connectable_service(dev, uuid);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if ((service == NULL) && (g_strcmp0(uuid, HFP_HS_UUID) == 0)) {
+               DBG("HFP service is not found check for HSP service");
+               service = find_connectable_service(dev, HSP_HS_UUID);
+       }
+#endif
        free(uuid);
 
        if (!service)
@@ -1906,6 +2486,9 @@ static DBusMessage *disconnect_profile(DBusConnection *conn, DBusMessage *msg,
        if (err == 0)
                return NULL;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (dev->disconnect)
+#endif
        dbus_message_unref(dev->disconnect);
        dev->disconnect = NULL;
 
@@ -1941,6 +2524,11 @@ static void store_services(struct btd_device *device)
        ba2str(btd_adapter_get_address(adapter), src_addr);
        ba2str(&device->bdaddr, dst_addr);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (device->rpa)
+               ba2str(device->rpa, dst_addr);
+#endif
+
        snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/attributes", src_addr,
                                                                dst_addr);
        key_file = g_key_file_new();
@@ -2211,9 +2799,18 @@ static void device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type,
        struct bearer_state *state = get_state(dev, bdaddr_type);
        struct browse_req *req = dev->browse;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       DBG("%s bdaddr_type %d err %d", dev->path, bdaddr_type, err);
+#else
        DBG("%s err %d", dev->path, err);
+#endif
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        state->svc_resolved = true;
+#else
+       if (err == 0)
+               state->svc_resolved = true;
+#endif
 
        /* Disconnection notification can happen before this function
         * gets called, so don't set svc_refreshed for a disconnected
@@ -2253,6 +2850,14 @@ static void device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type,
        if (!req)
                return;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* If bdaddr_type is LE but req is for SDP, don't complete browse req. */
+       if (bdaddr_type != BDADDR_BREDR && req->search_uuid) {
+               DBG("Discover comp. is for LE but browse req. is for SDP.");
+               return;
+       }
+#endif
+
        dev->browse = NULL;
        browse_request_complete(req, bdaddr_type, err);
 }
@@ -2376,22 +2981,52 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
 {
        struct btd_device *device = data;
        struct btd_adapter *adapter = device->adapter;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        struct bearer_state *state;
+#endif
        uint8_t bdaddr_type;
        const char *sender;
        struct agent *agent;
        struct bonding_req *bonding;
        uint8_t io_cap;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint8_t conn_type;
+       bool connect_le = FALSE;
+#endif
        int err;
 
        btd_device_set_temporary(device, false);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &conn_type,
+                                       DBUS_TYPE_INVALID) == FALSE)
+#else
        if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID))
+#endif
                return btd_error_invalid_args(msg);
 
        if (device->bonding)
                return btd_error_in_progress(msg);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (conn_type == DEV_CONN_DEFAULT) {
+               if (device_is_bonded(device, DEV_CONN_BREDR))
+                       return btd_error_already_exists(msg);
+               else if (device_is_bonded(device, DEV_CONN_LE))
+                       return btd_error_already_exists(msg);
+
+               if (device->bredr)
+                       conn_type = DEV_CONN_BREDR;
+               else if (device->le)
+                       conn_type = DEV_CONN_LE;
+               else
+                       conn_type = DEV_CONN_BREDR;
+       } else {
+               if (device_is_bonded(device, conn_type))
+                       return btd_error_already_exists(msg);
+       }
+       bdaddr_type = device->bdaddr_type;
+#else
        if (device->bredr_state.bonded)
                bdaddr_type = device->bdaddr_type;
        else if (device->le_state.bonded)
@@ -2403,6 +3038,15 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
 
        if (state->bonded)
                return btd_error_already_exists(msg);
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (conn_type == DEV_CONN_LE &&
+           (device_is_bredrle(device) || bdaddr_type != BDADDR_BREDR)) {
+               DBG("LE Connect request");
+               connect_le = TRUE;
+       }
+#endif
 
        sender = dbus_message_get_sender(msg);
 
@@ -2412,7 +3056,15 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
        else
                io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if ((conn_type == DEV_CONN_LE && bdaddr_type != BDADDR_BREDR) ||
+                       connect_le)
+               bonding = bonding_request_new(msg, device, bdaddr_type, agent);
+       else
+               bonding = bonding_request_new(msg, device, BDADDR_BREDR, agent);
+#else
        bonding = bonding_request_new(msg, device, bdaddr_type, agent);
+#endif
 
        if (agent)
                agent_unref(agent);
@@ -2429,6 +3081,17 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
         * channel first and only then start pairing (there's code for
         * this in the ATT connect callback)
         */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (((conn_type == DEV_CONN_LE && bdaddr_type != BDADDR_BREDR) ||
+               (connect_le)) && !device->le_state.connected)
+               err = device_connect_le(device);
+       else if (connect_le) /* Send bonding request if LE is already connected*/
+               err = adapter_create_bonding(adapter, &device->bdaddr,
+                                                       bdaddr_type, io_cap);
+       else
+               err = adapter_create_bonding(adapter, &device->bdaddr,
+                                                       BDADDR_BREDR, io_cap);
+#else
        if (bdaddr_type != BDADDR_BREDR) {
                if (!state->connected && btd_le_connect_before_pairing())
                        err = device_connect_le(device);
@@ -2440,6 +3103,7 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
                err = adapter_create_bonding(adapter, &device->bdaddr,
                                                        BDADDR_BREDR, io_cap);
        }
+#endif
 
        if (err < 0) {
                bonding_request_free(device->bonding);
@@ -2470,7 +3134,9 @@ static DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status)
                                "Authentication Rejected");
        case MGMT_STATUS_CANCELLED:
        case MGMT_STATUS_NO_RESOURCES:
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        case MGMT_STATUS_DISCONNECTED:
+#endif
                return dbus_message_new_error(msg,
                                ERROR_INTERFACE ".AuthenticationCanceled",
                                "Authentication Canceled");
@@ -2500,28 +3166,665 @@ static void device_cancel_bonding(struct btd_device *device, uint8_t status)
        if (device->authr)
                device_cancel_authentication(device, FALSE);
 
-       reply = new_authentication_return(bonding->msg, status);
-       g_dbus_send_message(dbus_conn, reply);
+       reply = new_authentication_return(bonding->msg, status);
+       g_dbus_send_message(dbus_conn, reply);
+
+       bonding_request_cancel(bonding);
+       bonding_request_free(bonding);
+}
+
+static DBusMessage *cancel_pairing(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct btd_device *device = data;
+       struct bonding_req *req = device->bonding;
+
+       DBG("");
+
+       if (!req)
+               return btd_error_does_not_exist(msg);
+
+       device_cancel_bonding(device, MGMT_STATUS_CANCELLED);
+
+       return dbus_message_new_method_return(msg);
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *discover_services(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct btd_device *device = user_data;
+       const char *pattern;
+       int err;
+
+       if (device->browse)
+               return btd_error_in_progress(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               return btd_error_invalid_args(msg);
+
+       err = device_browse_sdp(device, msg);
+       if (err < 0)
+               goto fail;
+
+       return NULL;
+
+fail:
+       return btd_error_failed(msg,
+                       "Unable to search the SDP services");
+}
+
+static const char *browse_request_get_requestor(struct browse_req *req)
+{
+       if (!req->msg)
+               return NULL;
+
+       return dbus_message_get_sender(req->msg);
+}
+
+static void iter_append_record(DBusMessageIter *dict, uint32_t handle,
+                                                       const char *record)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                       NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_UINT32, &handle);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &record);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static void discover_services_reply(struct browse_req *req, int err,
+                                                       sdp_list_t *recs)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter, dict;
+       sdp_list_t *seq;
+
+       if (!req->msg)
+               return;
+
+       if (err) {
+               const char *err_if;
+
+               if (err == -EHOSTDOWN)
+                       err_if = ERROR_INTERFACE ".ConnectionAttemptFailed";
+               else
+                       err_if = ERROR_INTERFACE ".Failed";
+
+               reply = dbus_message_new_error(req->msg, err_if,
+                                                       strerror(-err));
+               g_dbus_send_message(dbus_conn, reply);
+               return;
+       }
+
+       reply = dbus_message_new_method_return(req->msg);
+       if (!reply)
+               return;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_UINT32_AS_STRING DBUS_TYPE_STRING_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       for (seq = recs; seq; seq = seq->next) {
+               sdp_record_t *rec = (sdp_record_t *) seq->data;
+               GString *result;
+
+               if (!rec)
+                       break;
+
+               result = g_string_new(NULL);
+
+               convert_sdp_record_to_xml(rec, result,
+                               (void *) g_string_append);
+
+               if (result->len)
+                       iter_append_record(&dict, rec->handle, result->str);
+
+               g_string_free(result, TRUE);
+       }
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       g_dbus_send_message(dbus_conn, reply);
+}
+
+static DBusMessage *cancel_discover(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct btd_device *device = user_data;
+       const char *sender = dbus_message_get_sender(msg);
+       const char *requestor;
+
+       if (!device->browse)
+               return btd_error_does_not_exist(msg);
+
+       if (!dbus_message_is_method_call(device->browse->msg, DEVICE_INTERFACE,
+                                       "DiscoverServices"))
+               return btd_error_not_authorized(msg);
+
+       requestor = browse_request_get_requestor(device->browse);
+
+       /* only the discover requestor can cancel the inquiry process */
+       if (!requestor || !g_str_equal(requestor, sender))
+               return btd_error_not_authorized(msg);
+
+       discover_services_reply(device->browse, -ECANCELED, NULL);
+
+       if (device->browse)
+               browse_request_cancel(device->browse);
+
+       return dbus_message_new_method_return(msg);
+}
+
+void device_set_gatt_connected(struct btd_device *device, gboolean connected)
+{
+       if (device == NULL) {
+               error("device is NULL");
+               return;
+       }
+
+       if (device->gatt_connected == connected) {
+               error("same state change for gatt_connected : %d", connected);
+               return;
+       }
+       DBG("GattConnected %d", connected);
+
+       device->gatt_connected = connected;
+       g_dbus_emit_property_changed(dbus_conn, device->path,
+                       DEVICE_INTERFACE, "GattConnected");
+}
+
+static DBusMessage *connect_le(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct btd_device *dev = user_data;
+       dbus_bool_t auto_connect = FALSE;
+       int err;
+
+       if (!dev->le) {
+               /*
+                * If a LE connection is requested without device discovery,
+                * we try to get device object. Here, technology can be updated
+                * if there is matched device object. Or, a new device object
+                * will be created.
+                */
+               dev = btd_adapter_get_device(dev->adapter, &dev->bdaddr,
+                               BDADDR_LE_PUBLIC);
+               if (dev == NULL) {
+                       error("Unable to get device object");
+                       return btd_error_not_supported(msg);
+               }
+       }
+
+       if (dev->le_state.connected)
+               return dbus_message_new_method_return(msg);
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &auto_connect,
+                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       btd_device_set_temporary(dev, false);
+
+       if (auto_connect) {
+               DBG("Start BLE auto connection");
+               dev->disable_auto_connect = FALSE;
+               device_set_auto_connect(dev, TRUE);
+
+               return dbus_message_new_method_return(msg);
+       }
+
+       err = device_connect_le(dev);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       dev->connect = dbus_message_ref(msg);
+
+       return NULL;
+}
+
+static DBusMessage *disconnect_le(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct btd_device *dev = user_data;
+
+       if (!dev->le)
+               return btd_error_not_supported(msg);
+
+       /*
+        * Disable connections through passive sccanning
+        */
+       if (dev->auto_connect) {
+               DBG("Stop BLE auto connection");
+               dev->disable_auto_connect = FALSE;
+               device_set_auto_connect(dev, FALSE);
+
+               if (!dev->le_state.connected) {
+                       g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID);
+                       return NULL;
+               }
+       } else if (!dev->le_state.connected) {
+               return btd_error_not_connected(msg);
+       }
+
+       dev->disconnects = g_slist_append(dev->disconnects,
+                       dbus_message_ref(msg));
+
+       btd_adapter_disconnect_device(dev->adapter, &dev->bdaddr,
+                       dev->bdaddr_type);
+
+       return NULL;
+}
+
+static DBusMessage *connect_ipsp(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct btd_device *device = user_data;
+
+       DBG("bdaddr_type  %d", device->bdaddr_type);
+
+       if (device->bdaddr_type == BDADDR_BREDR) {
+               if(device->le)
+                       device->bdaddr_type = BDADDR_LE_PUBLIC;
+               else {
+                       device = btd_adapter_get_device(device->adapter,
+                                       &device->bdaddr, BDADDR_LE_PUBLIC);
+                       if (device == NULL)
+                               return btd_error_no_such_adapter(msg);
+               }
+       }
+
+       if (device->ipsp_connected)
+               return btd_error_already_connected(msg);
+
+       /* Initiate Connection for 6Lowan*/
+       if (btd_adapter_connect_ipsp(device->adapter, &device->bdaddr,
+                                       device->bdaddr_type) != 0)
+               return btd_error_failed(msg, "ConnectFailed");
+
+       return dbus_message_new_method_return(msg);;
+}
+
+static DBusMessage *disconnect_ipsp(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct btd_device *device = user_data;
+       DBG("bdaddr_type  %d", device->bdaddr_type);
+
+       if (device->bdaddr_type == BDADDR_BREDR)
+               return btd_error_not_supported(msg);
+
+       if (!device->ipsp_connected)
+               return btd_error_not_connected(msg);
+
+       /* Disconnect the 6Lowpan connection */
+       if (btd_adapter_disconnect_ipsp(device->adapter, &device->bdaddr,
+                                       device->bdaddr_type) != 0)
+               return btd_error_failed(msg, "DisconnectFailed");
+
+       /* TODO: Handle disconnection of GATT connection, If the connection
+        * is established as part of IPSP connection. */
+
+       return dbus_message_new_method_return(msg);;
+}
+
+static DBusMessage *le_set_data_length(
+                       DBusConnection *conn, DBusMessage *msg,
+                       void *user_data)
+{
+       dbus_uint16_t max_tx_octets;
+       dbus_uint16_t max_tx_time;
+       struct btd_device *device = user_data;
+       int status;
+       char addr[18];
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_UINT16, &max_tx_octets,
+                               DBUS_TYPE_UINT16, &max_tx_time,
+                               DBUS_TYPE_INVALID)) {
+               DBG("error in retrieving values");
+               return btd_error_invalid_args(msg);
+       }
+
+       if (device->bdaddr_type == BDADDR_BREDR)
+               return btd_error_not_supported(msg);
+
+       ba2str(&device->bdaddr, addr);
+
+       DBG("Remote device address: %s", addr);
+       DBG("Max tx octets: %u, Max tx time: %u",
+                               max_tx_octets, max_tx_time);
+
+       status = btd_adapter_le_set_data_length(device->adapter,
+                               &device->bdaddr, max_tx_octets,
+                               max_tx_time);
+
+       if (status != 0)
+               return btd_error_failed(msg, "Unable to set le data length values");
+       else
+               return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_trusted_profile(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_device *dev = data;
+       dbus_bool_t profile_trusted;
+       const char *pattern;
+       char *uuid;
+       uint32_t pbap = dev->trusted_profiles.pbap;
+       uint32_t map = dev->trusted_profiles.map;
+       uint32_t sap = dev->trusted_profiles.sap;
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_STRING, &pattern,
+                               DBUS_TYPE_BOOLEAN, &profile_trusted,
+                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       DBG("Pattern : %s", pattern);
+       uuid = bt_name2string(pattern);
+       DBG("UUID : %s", uuid);
+       DBG("profile Trusted : %d %d %d", dev->trusted_profiles.pbap,
+                       dev->trusted_profiles.map, dev->trusted_profiles.sap);
+       if (g_strcmp0(uuid, OBEX_PBAP_UUID) == 0) {
+               if (profile_trusted)
+                       pbap = SUPPORTED_TRUSTED;
+               else
+                       pbap = SUPPORTED_BLOCKED;
+       } else if (g_strcmp0(uuid, OBEX_MAP_UUID) == 0) {
+               if (profile_trusted)
+                       map = SUPPORTED_TRUSTED;
+               else
+                       map = SUPPORTED_BLOCKED;
+       } else if (g_strcmp0(uuid, SAP_UUID) == 0) {
+               if (profile_trusted)
+                       sap = SUPPORTED_TRUSTED;
+               else
+                       sap = SUPPORTED_BLOCKED;
+       } else {
+               return btd_error_invalid_args(msg);
+       }
+
+       btd_device_set_trusted_profiles(dev, pbap, map, sap);
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_restricted_profile(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_device *dev = data;
+       dbus_bool_t profile_restricted;
+       const char *pattern;
+       char *uuid;
+       uint32_t hfp_hs = dev->restricted_profiles.hfp_hs;
+       uint32_t a2dp = dev->restricted_profiles.a2dp;
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_STRING, &pattern,
+                               DBUS_TYPE_BOOLEAN, &profile_restricted,
+                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       DBG("Pattern : %s", pattern);
+       uuid = bt_name2string(pattern);
+       DBG("UUID : %s", uuid);
+       DBG("profile Restricted : %d %d", dev->restricted_profiles.hfp_hs,
+                       dev->restricted_profiles.a2dp);
+       if (g_strcmp0(uuid, HFP_HS_UUID) == 0) {
+               if (profile_restricted)
+                       hfp_hs = CONNECTION_RESTRICTED;
+               else
+                       hfp_hs = CONNECTION_PERMITTED;
+       } else if (g_strcmp0(uuid, A2DP_SINK_UUID) == 0) {
+               if (profile_restricted)
+                       a2dp = CONNECTION_RESTRICTED;
+               else
+                       a2dp = CONNECTION_PERMITTED;
+       } else {
+               return btd_error_invalid_args(msg);
+       }
+
+       btd_device_set_restricted_profiles(dev, hfp_hs, a2dp);
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *is_connected_profile(DBusConnection *conn, DBusMessage *msg,
+                                                                       void *user_data)
+{
+       struct btd_device *dev = user_data;
+       struct btd_service *service;
+       btd_service_state_t state;
+       const char *pattern;
+       char *uuid;
+       DBusMessage *reply;
+       dbus_bool_t val;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return btd_error_invalid_args(reply);
+
+       uuid = bt_name2string(pattern);
+       DBG("is_connected_profile_uuid : %s", uuid);
+       service = btd_device_get_service(dev, uuid);
+
+       if ((service == NULL) && (g_strcmp0(uuid, HFP_HS_UUID) == 0)) {
+               DBG("HFP  service is not found check for HSP service");
+               service = btd_device_get_service(dev, HSP_HS_UUID);
+       }
+       if (uuid)
+               free(uuid);
+
+       if (!service)
+               return btd_error_not_connected(msg);
+
+       state = btd_service_get_state(service);
+       DBG("Connected State : %d", state);
+
+       if (state == BTD_SERVICE_STATE_CONNECTED)
+               val = TRUE;
+       else
+               val = FALSE;
+
+       dbus_message_append_args(reply,
+                       DBUS_TYPE_BOOLEAN, &val,
+                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static DBusMessage *update_le_conn_parm(DBusConnection *conn, DBusMessage *msg,
+                                       void *user_data)
+{
+       struct btd_device *device = user_data;
+       GIOChannel *io;
+       int fd;
+       struct le_conn_param param = {0, 0, 0, 0};
+
+       DBG("");
+
+       if (device == NULL) {
+               error("device is NULL");
+               return btd_error_invalid_args(msg);
+       }
+
+       if (!device->le) {
+               error("le is not supported");
+               return btd_error_not_supported(msg);
+       }
+
+       if (!device->gatt_connected || !device->attrib)
+               return btd_error_not_connected(msg);
+
+       io = g_attrib_get_channel(device->attrib);
+       if (!io)
+               return btd_error_not_connected(msg);
+
+       fd = g_io_channel_unix_get_fd(io);
+       if (fd < 0)
+               return btd_error_not_connected(msg);
+
+       if (device_get_conn_update_state(device))
+               return btd_error_in_progress(msg);
+       else
+               device_set_conn_update_state(device, true);
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &param.min,
+                                       DBUS_TYPE_UINT32, &param.max,
+                                       DBUS_TYPE_UINT32, &param.latency,
+                                       DBUS_TYPE_UINT32, &param.to_multiplier,
+                                       DBUS_TYPE_INVALID)) {
+               error("Invalid args");
+               return btd_error_invalid_args(msg);
+       }
+
+       if (setsockopt(fd, SOL_BLUETOOTH, BT_LE_CONN_PARAM,
+                               &param, sizeof(param)) < 0) {
+               error("Can't Update LE conn param : %s (%d)",
+                               strerror(errno), errno);
+               return btd_error_failed(msg, strerror(errno));
+       }
+
+       return dbus_message_new_method_return(msg);
+}
+
+static void device_request_att_mtu_reponse_cb(bool success, uint8_t att_ecode,
+                                       void *user_data)
+{
+       struct btd_device *device = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       uint16_t mtu;
+
+       if (!device->req_att_mtu)
+               return;
+
+       mtu = bt_gatt_client_get_mtu(device->client);
+
+       if (!success) {
+               const char *err_if;
+               err_if = ERROR_INTERFACE ".Failed";
+
+               reply = dbus_message_new_error(device->req_att_mtu, err_if,
+                                                       "Request Att MTU failed");
+               g_dbus_send_message(dbus_conn, reply);
+               return;
+       }
+
+       DBG("MTU exchange complete, with MTU: %u", mtu);
+
+       reply = dbus_message_new_method_return(device->req_att_mtu);
+       if (!reply)
+               return;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16,
+                                                       &mtu);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_BYTE,
+                                                       &att_ecode);
+       g_dbus_send_message(dbus_conn, reply);
+
+       dbus_message_unref(device->req_att_mtu);
+       device->req_att_mtu = NULL;
+}
+
+static DBusMessage *request_att_mtu(DBusConnection *conn, DBusMessage *msg,
+                                       void *user_data)
+{
+       struct btd_device *device = user_data;
+       uint16_t mtu;
+
+       DBG("");
+
+       if (device == NULL) {
+               error("device is NULL");
+               return btd_error_invalid_args(msg);
+       }
+
+       if (!device->le) {
+               error("le is not supported");
+               return btd_error_not_supported(msg);
+       }
+
+       if (!device->gatt_connected || !device->attrib)
+               return btd_error_not_connected(msg);
+
+       if (!dbus_message_get_args(msg, NULL,
+                                               DBUS_TYPE_UINT16, &mtu,
+                                               DBUS_TYPE_INVALID)) {
+               error("Invalid args");
+               return btd_error_invalid_args(msg);
+       }
+
+       DBG("MTU %d", mtu);
+
+       if (!bt_gatt_request_att_mtu(device->client, mtu,
+                               device_request_att_mtu_reponse_cb, device))
+               return btd_error_failed(msg, "Unable to Request MTU");
+
+       device->req_att_mtu = dbus_message_ref(msg);
+       return NULL;
+}
+
+static DBusMessage *device_get_ida(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct btd_device *device = user_data;
+       char device_idaddr[18] = { 0 };
+       DBusMessage *reply;
+       const gchar *id_address = device_idaddr;
+
+       DBG("");
+
+       if (device == NULL)
+               return btd_error_invalid_args(msg);
+
+       if (!device->le)
+               return btd_error_not_supported(msg);
+
+       if (device->rpa) {
+               if (device->bredr)
+                       ba2str(device->rpa, device_idaddr);
+               else
+                       ba2str(&device->bdaddr, device_idaddr);
+       } else
+               return btd_error_does_not_exist(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_append_args(reply, DBUS_TYPE_STRING, &id_address,
+                                                       DBUS_TYPE_INVALID);
 
-       bonding_request_cancel(bonding);
-       bonding_request_free(bonding);
+       return reply;
 }
 
-static DBusMessage *cancel_pairing(DBusConnection *conn, DBusMessage *msg,
-                                                               void *data)
+void device_set_conn_update_state(struct btd_device *device, bool state)
 {
-       struct btd_device *device = data;
-       struct bonding_req *req = device->bonding;
-
-       DBG("");
-
-       if (!req)
-               return btd_error_does_not_exist(msg);
+       if (!device)
+               return;
 
-       device_cancel_bonding(device, MGMT_STATUS_CANCELLED);
+       device->pending_conn_update = state;
+}
 
-       return dbus_message_new_method_return(msg);
+bool device_get_conn_update_state(struct btd_device *device)
+{
+       return device->pending_conn_update;
 }
+#endif
 
 static const GDBusMethodTable device_methods[] = {
        { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, dev_disconnect) },
@@ -2530,8 +3833,45 @@ static const GDBusMethodTable device_methods[] = {
                                                NULL, connect_profile) },
        { GDBUS_ASYNC_METHOD("DisconnectProfile", GDBUS_ARGS({ "UUID", "s" }),
                                                NULL, disconnect_profile) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { GDBUS_ASYNC_METHOD("Pair", GDBUS_ARGS({ "conn_type", "y" }), NULL,
+                       pair_device) },
+#else
        { GDBUS_ASYNC_METHOD("Pair", NULL, NULL, pair_device) },
+#endif
        { GDBUS_METHOD("CancelPairing", NULL, NULL, cancel_pairing) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { GDBUS_ASYNC_METHOD("ConnectLE", GDBUS_ARGS({ "auto_connect", "b"}),
+                                               NULL, connect_le) },
+       { GDBUS_ASYNC_METHOD("DisconnectLE", NULL, NULL, disconnect_le) },
+       { GDBUS_METHOD("IsConnectedProfile", GDBUS_ARGS({ "UUID", "s" }),
+                       GDBUS_ARGS({ "IsConnected", "b" }),
+                       is_connected_profile)},
+       { GDBUS_METHOD("LeConnUpdate", GDBUS_ARGS({ "interval_min", "u" },
+                               { "interval_max", "u" }, { "latency", "u" },
+                               { "time_out", "u" }), NULL,
+                       update_le_conn_parm) },
+       { GDBUS_ASYNC_METHOD("DiscoverServices", GDBUS_ARGS({ "pattern", "s" }),
+                       NULL, discover_services) },
+       { GDBUS_METHOD("CancelDiscovery", NULL, NULL, cancel_discover) },
+       { GDBUS_ASYNC_METHOD("ConnectIpsp", NULL, NULL, connect_ipsp) },
+       { GDBUS_ASYNC_METHOD("DisconnectIpsp", NULL, NULL, disconnect_ipsp) },
+       { GDBUS_ASYNC_METHOD("LESetDataLength",
+                       GDBUS_ARGS({"max_tx_octets", "q" },
+                       { "max_tx_time", "q" }), NULL,
+                       le_set_data_length)},
+       { GDBUS_ASYNC_METHOD("RequestAttMtu", GDBUS_ARGS({ "mtu", "q" }),
+                       GDBUS_ARGS({ "mtu", "q" }, { "status", "y"}),
+                       request_att_mtu) },
+       { GDBUS_METHOD("GetIDAddress", NULL, GDBUS_ARGS({ "IDAdress", "s" }),
+                       device_get_ida) },
+       { GDBUS_METHOD("SetTrustedProfile",
+                       GDBUS_ARGS({ "uuid", "s"}, { "trusted", "b"}), NULL,
+                       set_trusted_profile) },
+       { GDBUS_METHOD("SetRestrictedProfile",
+                       GDBUS_ARGS({ "uuid", "s"}, { "restricted", "b"}), NULL,
+                       set_restricted_profile) },
+#endif
        { }
 };
 
@@ -2550,11 +3890,30 @@ static const GDBusPropertyTable device_properties[] = {
        { "Blocked", "b", dev_property_get_blocked, dev_property_set_blocked },
        { "LegacyPairing", "b", dev_property_get_legacy },
        { "RSSI", "n", dev_property_get_rssi, NULL, dev_property_exists_rssi },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       {"IsAliasSet", "b", dev_property_get_alias_set },
+       { "Connected", "y", dev_property_get_connected },
+#else
        { "Connected", "b", dev_property_get_connected },
+#endif
        { "UUIDs", "as", dev_property_get_uuids },
        { "Modalias", "s", dev_property_get_modalias, NULL,
                                                dev_property_exists_modalias },
        { "Adapter", "o", dev_property_get_adapter },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+        /* To handle Failed Legacy Pairing when initiated from Remote device*/
+       { "LegacyPaired", "b", dev_property_get_paired },
+       { "ManufacturerDataLen", "q", property_get_manufacturer_data_len },
+       { "ManufacturerData", "ay", property_get_manufacturer_data },
+       { "GattConnected", "b", dev_property_get_gatt_connected },
+       { "PayloadTimeout", "q", dev_property_get_payload},
+       { "LastAddrType", "y", dev_property_get_last_addr_type},
+       { "IpspConnected", "b", dev_property_get_ipsp_conn_state },
+       { "IpspBtInterfaceInfo", "s", dev_property_get_ipsp_conn_bt_iface_name },
+       { "AttMtu", "q", dev_property_get_att_mtu },
+       { "TrustedProfiles", "u", dev_property_get_trusted_profiles},
+       { "RestrictedProfiles", "u", dev_property_get_restricted_profiles},
+#endif
        { "ManufacturerData", "a{qv}", dev_property_get_manufacturer_data,
                                NULL, dev_property_manufacturer_data_exist },
        { "ServiceData", "a{sv}", dev_property_get_service_data,
@@ -2569,6 +3928,28 @@ static const GDBusPropertyTable device_properties[] = {
        { }
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static const GDBusSignalTable device_signals[] = {
+       { GDBUS_SIGNAL("Disconnected",
+                       GDBUS_ARGS({ "bdaddr_type", "y" }, { "reason", "y" },
+                               { "name", "s" })) },
+       { GDBUS_SIGNAL("DeviceConnected", GDBUS_ARGS({ "bdaddr_type", "y"})) },
+       { GDBUS_SIGNAL("ProfileStateChanged",
+                       GDBUS_ARGS({ "profile", "s"}, {"state", "i"})) },
+       { GDBUS_SIGNAL("AdvReport",
+                       GDBUS_ARGS({"Address","s"}, { "Address Type", "y" },
+                               { "Adv Type", "y"}, { "RSSI", "i"},
+                               { "AdvDataLen", "i"}, { "AdvData", "ay"})) },
+       { GDBUS_SIGNAL("LEDataLengthChanged",
+                       GDBUS_ARGS({"max_tx_octets","q"},
+                               { "max_tx_time", "q" },
+                               { "max_rx_octets", "q"},
+                               { "max_rx_time", "q"})) },
+       { GDBUS_SIGNAL("IpspStateChanged",
+                       GDBUS_ARGS({"connected","b"},{"if_name","s"}))},
+};
+#endif
+
 uint8_t btd_device_get_bdaddr_type(struct btd_device *dev)
 {
        return dev->bdaddr_type;
@@ -2600,16 +3981,38 @@ void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type)
 
        state->connected = true;
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (dev->le_state.connected && dev->bredr_state.connected)
                return;
 
        g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE,
                                                                "Connected");
+#else
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+       if (bdaddr_type == BDADDR_BREDR &&
+           get_charging_state(dev->adapter) == WIRELESS_CHARGING) {
+               int br_pkt_type = ACL_PTYPE_MASK |
+                       HCI_2DH1 | HCI_2DH3 | HCI_2DH5 |
+                       HCI_3DH1 | HCI_3DH3 | HCI_3DH5;
+
+               DBG("During wireless charging... Change packet type");
+               device_change_pkt_type(dev, (gpointer)br_pkt_type);
+       }
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+
+       g_dbus_emit_signal(dbus_conn, dev->path,
+               DEVICE_INTERFACE, "DeviceConnected",
+               DBUS_TYPE_BYTE, &bdaddr_type,
+               DBUS_TYPE_INVALID);
+#endif
 }
 
 void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type)
 {
        struct bearer_state *state = get_state(device, bdaddr_type);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char *dev_name = device->name;
+#endif
 
        if (!state->connected)
                return;
@@ -2624,6 +4027,18 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type)
                device->disconn_timer = 0;
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (device->browse) {
+               struct browse_req *req = device->browse;
+
+               if ((bdaddr_type == BDADDR_BREDR && req->search_uuid != 0) ||
+                   (bdaddr_type != BDADDR_BREDR && req->search_uuid == 0))
+                       device->browse = NULL;
+               else
+                       DBG("device->browse is for other link");
+       }
+#endif
+
        while (device->disconnects) {
                DBusMessage *msg = device->disconnects->data;
 
@@ -2636,11 +4051,20 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type)
                btd_adapter_remove_bonding(device->adapter, &device->bdaddr,
                                                                bdaddr_type);
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (device->bredr_state.connected || device->le_state.connected)
                return;
 
        g_dbus_emit_property_changed(dbus_conn, device->path,
                                                DEVICE_INTERFACE, "Connected");
+#else
+       g_dbus_emit_signal(dbus_conn, device->path,
+               DEVICE_INTERFACE, "Disconnected",
+               DBUS_TYPE_BYTE, &bdaddr_type,
+               DBUS_TYPE_BYTE, &device->disc_reason,
+               DBUS_TYPE_STRING, &dev_name,
+               DBUS_TYPE_INVALID);
+#endif
 }
 
 guint device_add_disconnect_watch(struct btd_device *device,
@@ -2808,7 +4232,10 @@ static void load_info(struct btd_device *device, const char *local,
        char **uuids;
        int source, vendor, product, version;
        char **techno, **t;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       gboolean svc_change_regd;
+       char buf[DEV_MAX_MANUFACTURER_DATA_LEN] = { 0, };
+#endif
        /* Load device name from storage info file, if that fails fall back to
         * the cache.
         */
@@ -2845,6 +4272,37 @@ static void load_info(struct btd_device *device, const char *local,
                g_free(str);
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* Load RPA Resolution Support value */
+       device->rpa_res_support = g_key_file_get_integer(key_file,
+                                                       "General", "RPAResSupport", NULL);
+
+       str = g_key_file_get_string(key_file, "General", "ManufacturerDataLen", NULL);
+       if (str) {
+               device->manufacturer_data_len = strtol(str, NULL, 10);
+               g_free(str);
+
+               str = g_key_file_get_string(key_file, "General", "ManufacturerData", NULL);
+               if (str) {
+                       load_manufacturer_data_2digit(str,
+                                               device->manufacturer_data_len,  buf);
+                       device->manufacturer_data = g_memdup(buf,
+                                               device->manufacturer_data_len);
+                       g_free(str);
+               }
+       }
+
+       str = g_key_file_get_string(key_file, "General", "IdentityAddress",
+                                                               NULL);
+
+       if (str) {
+               device->rpa = g_malloc0(sizeof(bdaddr_t));
+               bacpy(device->rpa, &device->bdaddr);
+               str2ba(str, &device->bdaddr);
+               g_free(str);
+       }
+#endif
+
        /* Load device technology */
        techno = g_key_file_get_string_list(key_file, "General",
                                        "SupportedTechnologies", NULL, NULL);
@@ -2886,6 +4344,27 @@ next:
        device->trusted = g_key_file_get_boolean(key_file, "General",
                                                        "Trusted", NULL);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* Load Trusted Profiles*/
+       int trusted_profiles = g_key_file_get_integer(key_file, "General",
+                                                       "TrustedProfiles", NULL);
+       DBG("Loading TrustedProfiles %d", trusted_profiles);
+       device->trusted_profiles.pbap = ((trusted_profiles &
+                       (PROFILE_SUPPORTED << PBAP_SHIFT_OFFSET)) >> PBAP_SHIFT_OFFSET);
+       device->trusted_profiles.map = ((trusted_profiles &
+                       (PROFILE_SUPPORTED << MAP_SHIFT_OFFSET)) >> MAP_SHIFT_OFFSET);
+       device->trusted_profiles.sap = ((trusted_profiles &
+                       (PROFILE_SUPPORTED << SAP_SHIFT_OFFSET)) >> SAP_SHIFT_OFFSET);
+
+       /* Load Restricted Profiles*/
+       int restricted_profiles = g_key_file_get_integer(key_file, "General",
+                                                       "RestrictedProfiles", NULL);
+       DBG("Loading RestrictedProfiles %d", restricted_profiles);
+       device->restricted_profiles.hfp_hs = (restricted_profiles >> HFP_HS_SHIFT_OFFSET) & 0x01;
+       device->restricted_profiles.a2dp = (restricted_profiles >> A2DP_SHIFT_OFFSET) & 0x01;
+
+#endif
+
        /* Load device blocked */
        blocked = g_key_file_get_boolean(key_file, "General", "Blocked", NULL);
        if (blocked)
@@ -2916,6 +4395,15 @@ next:
                btd_device_set_pnpid(device, source, vendor, product, version);
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* Load Service changed Registered flag */
+       svc_change_regd = g_key_file_get_boolean(key_file, "Att",
+                                               "SvcChangeRegd", NULL);
+
+       bt_att_set_svc_changed_indication_registered(device->att,
+                                               svc_change_regd);
+#endif
+
        if (store_needed)
                store_device_info(device);
 }
@@ -3342,8 +4830,13 @@ static bool device_match_profile(struct btd_device *device,
                return false;
 
        if (g_slist_find_custom(uuids, profile->remote_uuid,
-                                                       bt_uuid_strcmp) == NULL)
+                                                       bt_uuid_strcmp) == NULL) {
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+               if (strcmp(profile->name, "hid-device") == 0)
+                       return true;
+#endif
                return false;
+       }
 
        return true;
 }
@@ -3404,12 +4897,35 @@ static void device_add_gatt_services(struct btd_device *device)
        gatt_db_foreach_service(device->db, NULL, add_gatt_service, device);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void accept_gatt_service(struct gatt_db_attribute *attr, void *user_data)
+{
+       struct btd_device *device = user_data;
+       GSList *l;
+       bt_uuid_t uuid;
+       char uuid_str[MAX_LEN_UUID_STR];
+
+       gatt_db_attribute_get_service_uuid(attr, &uuid);
+       bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+
+       l = find_service_with_uuid(device->services, uuid_str);
+       if (!l)
+               return;
+
+       service_accept(l->data);
+}
+#endif
+
 static void device_accept_gatt_profiles(struct btd_device *device)
 {
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        GSList *l;
 
        for (l = device->services; l != NULL; l = g_slist_next(l))
                service_accept(l->data);
+#else
+       gatt_db_foreach_service(device->db, NULL, accept_gatt_service, device);
+#endif
 }
 
 static void device_remove_gatt_service(struct btd_device *device,
@@ -3608,7 +5124,11 @@ static struct btd_device *device_new(struct btd_adapter *adapter,
 
        if (g_dbus_register_interface(dbus_conn,
                                        device->path, DEVICE_INTERFACE,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                                       device_methods, device_signals,
+#else
                                        device_methods, NULL,
+#endif
                                        device_properties, device,
                                        device_free) == FALSE) {
                error("Unable to register device interface for %s", address);
@@ -3625,6 +5145,28 @@ static struct btd_device *device_new(struct btd_adapter *adapter,
        return btd_device_ref(device);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void device_print_addr(struct btd_device *dev)
+{
+       char ida[18];
+       char rpa[18];
+
+       ba2str(&dev->bdaddr, ida);
+
+       if (dev->rpa) {
+               ba2str(dev->rpa, rpa);
+
+               DBG("IDA %s [%d] : RPA [%s], BREDR [%d], LE [%d]",
+                               ida, dev->bdaddr_type, rpa,
+                               dev->bredr ? 1 : 0, dev->le ? 1 : 0);
+       } else {
+               DBG("ADDR %s [%d] : BREDR [%d], LE [%d]",
+                               ida, dev->bdaddr_type,
+                               dev->bredr ? 1 : 0, dev->le ? 1 : 0);
+       }
+}
+#endif
+
 struct btd_device *device_create_from_storage(struct btd_adapter *adapter,
                                const char *address, GKeyFile *key_file)
 {
@@ -3697,6 +5239,11 @@ char *btd_device_get_storage_path(struct btd_device *device,
        ba2str(btd_adapter_get_address(device->adapter), srcaddr);
        ba2str(&device->bdaddr, dstaddr);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (device->rpa)
+               ba2str(device->rpa, dstaddr);
+#endif
+
        if (!filename)
                return g_strdup_printf(STORAGEDIR "/%s/%s", srcaddr, dstaddr);
 
@@ -3778,6 +5325,23 @@ void device_update_addr(struct btd_device *device, const bdaddr_t *bdaddr,
 
 void device_set_bredr_support(struct btd_device *device)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char addr_str[18];
+
+       if (device->rpa) {
+               ba2str(device->rpa, addr_str);
+               error("Cannot set bredr support to RPA device [%s]", addr_str);
+               return;
+       }
+
+       if (device->bdaddr_type == BDADDR_LE_RANDOM) {
+               ba2str(&device->bdaddr, addr_str);
+               error("Cannot set bredr support to LE random device [%s]",
+                                                               addr_str);
+               return;
+       }
+#endif
+
        if (device->bredr)
                return;
 
@@ -3785,6 +5349,22 @@ void device_set_bredr_support(struct btd_device *device)
        store_device_info(device);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void device_set_rpa(struct btd_device *device, const bdaddr_t *rpa)
+{
+       if (device->rpa == NULL) {
+               device->rpa = g_malloc0(sizeof(bdaddr_t));
+               bacpy(device->rpa, rpa);
+       } else
+               error("RPA is already set");
+}
+
+void device_set_irk_value(struct btd_device *device, const uint8_t *val)
+{
+       memcpy(&device->irk_val, val, sizeof(device->irk_val));
+}
+#endif
+
 void device_set_le_support(struct btd_device *device, uint8_t bdaddr_type)
 {
        if (device->le)
@@ -3925,6 +5505,11 @@ static void device_remove_stored(struct btd_device *device)
        ba2str(src, adapter_addr);
        ba2str(&device->bdaddr, device_addr);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (device->rpa)
+               ba2str(device->rpa, device_addr);
+#endif
+
        snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s", adapter_addr,
                        device_addr);
        delete_folder_tree(filename);
@@ -3946,6 +5531,102 @@ static void device_remove_stored(struct btd_device *device)
        g_key_file_free(key_file);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void device_unpair(struct btd_device *device, gboolean remove_stored)
+{
+       DBG("+");
+       DBG("Unpairing device %s", device->path);
+
+       if (device->bonding) {
+               uint8_t status;
+
+               if (device->bredr_state.connected)
+                       status = MGMT_STATUS_DISCONNECTED;
+               else
+                       status = MGMT_STATUS_CONNECT_FAILED;
+
+               device_cancel_bonding(device, status);
+       }
+
+       if (device->browse)
+               browse_request_cancel(device->browse);
+
+
+//     while (device->services != NULL) {
+//             struct btd_service *service = device->services->data;
+//
+//             device->services = g_slist_remove(device->services, service);
+//             service_remove(service);
+//     }
+
+       g_slist_free(device->pending);
+       device->pending = NULL;
+
+       if (btd_device_is_connected(device))
+               disconnect_all(device);
+
+       if (device->store_id > 0) {
+               g_source_remove(device->store_id);
+               device->store_id = 0;
+
+               if (!remove_stored)
+                       store_device_info_cb(device);
+       }
+
+       if (remove_stored)
+               device_remove_stored(device);
+
+       gatt_db_clear(device->db);
+
+       if (device->rpa) {
+               bacpy(&device->bdaddr, device->rpa);
+               device->bdaddr_type = BDADDR_LE_RANDOM;
+
+               g_free(device->rpa);
+               device->rpa = NULL;
+       }
+
+       device->bredr_state.paired = 0;
+       device->le_state.paired = 0;
+       device->bredr_state.svc_resolved = false;
+       device->trusted = false;
+       device->trusted_profiles.pbap = SHOW_AUTHORIZATION;
+       device->trusted_profiles.map = SHOW_AUTHORIZATION;
+       device->trusted_profiles.sap = SHOW_AUTHORIZATION;
+       if (device->alias != NULL) {
+               /* Remove alias name because
+                * In UG if we rename and then unpair device and
+                * initiates connection without scanning then paired
+                * list will have alias name as first preference is
+                * given to alias name.
+                */
+               DBG("Freeing device alias name");
+               g_free(device->alias);
+               device->alias = NULL;
+       }
+       g_dbus_emit_property_changed(dbus_conn, device->path,
+                                       DEVICE_INTERFACE, "Paired");
+//     btd_device_unref(device);
+       DBG("-");
+ }
+
+void device_remove_stored_folder(struct btd_device *device)
+{
+       const bdaddr_t *src = btd_adapter_get_address(device->adapter);
+       char adapter_addr[18];
+       char device_addr[18];
+       char filename[PATH_MAX];
+
+       ba2str(src, adapter_addr);
+       ba2str(&device->bdaddr, device_addr);
+
+       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s", adapter_addr,
+                       device_addr);
+
+       delete_folder_tree(filename);
+}
+#endif
+
 void device_remove(struct btd_device *device, gboolean remove_stored)
 {
        DBG("Removing device %s", device->path);
@@ -3993,6 +5674,30 @@ void device_remove(struct btd_device *device, gboolean remove_stored)
        btd_device_unref(device);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int device_rpa_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct btd_device *device = a;
+       const char *address = b;
+       char addr[18];
+
+       if (!device->rpa)
+               return -1;
+
+       ba2str(device->rpa, addr);
+
+       return strcasecmp(addr, address);
+}
+
+int device_addr_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct btd_device *device = a;
+       const bdaddr_t *bdaddr = b;
+
+       return bacmp(&device->bdaddr, bdaddr);
+}
+#endif
+
 int device_address_cmp(gconstpointer a, gconstpointer b)
 {
        const struct btd_device *device = a;
@@ -4019,6 +5724,36 @@ static bool addr_is_public(uint8_t addr_type)
        return false;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int device_addr_type_strict_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct btd_device *dev = a;
+       const struct device_addr_type *addr = b;
+       int cmp;
+
+       cmp = bacmp(&dev->bdaddr, &addr->bdaddr);
+
+       if (addr->bdaddr_type == BDADDR_BREDR) {
+               if (!dev->bredr)
+                       return -1;
+
+               return cmp;
+       }
+
+       if (!dev->le)
+               return -1;
+
+       if (cmp && dev->rpa && addr->bdaddr_type == BDADDR_LE_RANDOM &&
+                       (addr->bdaddr.b[5] >> 6) == 0x01)
+               return bacmp(dev->rpa, &addr->bdaddr);
+
+       if (addr->bdaddr_type != dev->bdaddr_type)
+               return -1;
+
+       return cmp;
+}
+#endif
+
 int device_addr_type_cmp(gconstpointer a, gconstpointer b)
 {
        const struct btd_device *dev = a;
@@ -4034,7 +5769,20 @@ int device_addr_type_cmp(gconstpointer a, gconstpointer b)
         */
        if (!cmp && addr_is_public(addr->bdaddr_type) &&
                                        addr_is_public(dev->bdaddr_type))
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       {
+               if (dev->rpa && addr->bdaddr_type == BDADDR_BREDR) {
+                       char addr_str[18];
+
+                       ba2str(&dev->bdaddr, addr_str);
+                       DBG("Don't match. LE Only device [%s]", addr_str);
+                       return -1;
+               }
+               return 0;
+       }
+#else
                return 0;
+#endif
 
        if (addr->bdaddr_type == BDADDR_BREDR) {
                if (!dev->bredr)
@@ -4052,6 +5800,68 @@ int device_addr_type_cmp(gconstpointer a, gconstpointer b)
        return cmp;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+void device_change_pkt_type(gpointer data, gpointer user_data)
+{
+       int pkt_type = (int)user_data;
+       struct btd_device *device = data;
+       struct hci_conn_info_req *cr;
+       set_conn_ptype_cp cp;
+       char addr[18];
+       int hdev = 0;
+       int err = 0;
+
+       /* Change a packet type only for Phone device */
+       if ((device->class & 0x00001F00) >> 8 != 0x02)
+               return;
+
+       if (!device->bredr_state.connected)
+               return;
+
+       hdev = hci_open_dev(0);
+       if (hdev < 0) {
+               error("Cannot open hdev");
+               return;
+       }
+
+       cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (cr == NULL) {
+               error("Out of memory");
+               return;
+       }
+       cr->type = ACL_LINK;
+       bacpy(&cr->bdaddr, &device->bdaddr);
+
+       err = ioctl(hdev, HCIGETCONNINFO, cr);
+       if (err < 0) {
+               error("Fail to get HCIGETCOINFO");
+               g_free(cr);
+               hci_close_dev(hdev);
+               return;
+       }
+
+       cp.handle = cr->conn_info->handle;
+       g_free(cr);
+       cp.pkt_type = cpu_to_le16((uint16_t)pkt_type);
+
+       ba2str(&device->bdaddr, addr);
+       DBG("Handle %d, Addr %s", cp.handle, addr);
+       DBG("Send Change pkt type request : 0x%X", pkt_type);
+
+       if (hci_send_cmd(hdev, OGF_LINK_CTL, OCF_SET_CONN_PTYPE,
+                               SET_CONN_PTYPE_CP_SIZE, &cp) < 0) {
+               error("hci_send_cmd is failed");
+               hci_close_dev(hdev);
+               return;
+       }
+
+       hci_close_dev(hdev);
+       return;
+}
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
+
 static gboolean record_has_uuid(const sdp_record_t *rec,
                                const char *profile_uuid)
 {
@@ -4108,8 +5918,10 @@ static struct btd_service *probe_service(struct btd_device *device,
        /* Only set auto connect if profile has set the flag and can really
         * accept connections.
         */
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (profile->auto_connect && profile->accept)
                device_set_auto_connect(device, TRUE);
+#endif
 
        return service;
 }
@@ -4119,6 +5931,13 @@ static void dev_probe(struct btd_profile *p, void *user_data)
        struct probe_data *d = user_data;
        struct btd_service *service;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (find_service_with_profile(d->dev->services, p)) {
+               DBG("%s is already probed.(UUID:%s)", p->name, p->remote_uuid);
+               return;
+       }
+#endif
+
        service = probe_service(d->dev, p, d->uuids);
        if (!service)
                return;
@@ -4155,8 +5974,30 @@ void device_remove_profile(gpointer a, gpointer b)
        GSList *l;
 
        l = find_service_with_profile(device->services, profile);
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+       if (l == NULL) {
+               if (g_strcmp0(profile->local_uuid , HID_DEVICE_UUID) == 0) {
+                       l = find_service_with_uuid(device->services,
+                                       HID_DEVICE_UUID);
+                       if (l == NULL)
+                               return;
+
+                       service = l->data;
+
+                       if (btd_service_get_state(service) ==
+                               BTD_SERVICE_STATE_CONNECTED) {
+                               int err;
+                               err = btd_service_disconnect(service);
+                               if (err)
+                                       error("error: %s", strerror(-err));
+                       }
+               }
+               return;
+       }
+#else
        if (l == NULL)
                return;
+#endif
 
        service = l->data;
        device->services = g_slist_delete_link(device->services, l);
@@ -4512,7 +6353,20 @@ static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
        g_dbus_emit_property_changed(dbus_conn, req->device->path,
                                                DEVICE_INTERFACE, "UUIDs");
 
-send_reply:
+send_reply:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (!req->msg)
+               goto done;
+
+       /* since no new services are found, UUID signal is not emitted,
+       ** so send a reply to the framework with the existing services */
+       if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE,
+                               "DiscoverServices"))
+               discover_services_reply(req, err, device->tmp_records);
+
+done:
+#endif
+
        device_svc_resolved(device, BDADDR_BREDR, err);
 }
 
@@ -4598,9 +6452,26 @@ static void att_disconnected_cb(int err, void *user_data)
                adapter_connect_list_add(device->adapter, device);
 
 done:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       device_set_gatt_connected(device, FALSE);
+#endif
        attio_cleanup(device);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void att_mtu_changed(uint16_t mtu, void *user_data)
+{
+       struct btd_device *device = user_data;
+
+       DBG("att mtu changed %d", mtu);
+
+       g_dbus_emit_signal(dbus_conn, device->path,
+               DEVICE_INTERFACE, "AttMtuChanged",
+               DBUS_TYPE_UINT16, &mtu,
+               DBUS_TYPE_INVALID);
+}
+#endif
+
 static void register_gatt_services(struct btd_device *device)
 {
        struct browse_req *req = device->browse;
@@ -4617,8 +6488,17 @@ static void register_gatt_services(struct btd_device *device)
 
        btd_device_set_temporary(device, false);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (req) {
+               if (req->search_uuid)
+                       DBG("browse req. is for SDP. Ignore it.");
+               else
+                       update_gatt_uuids(req, device->primaries, services);
+       }
+#else
        if (req)
                update_gatt_uuids(req, device->primaries, services);
+#endif
 
        g_slist_free_full(device->primaries, g_free);
        device->primaries = NULL;
@@ -4649,6 +6529,15 @@ static void gatt_client_ready_cb(bool success, uint8_t att_ecode,
        device_svc_resolved(device, device->bdaddr_type, 0);
 
        store_gatt_db(device);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (device->name[0] == '\0') {
+               char *name = NULL;
+               name = bt_gatt_client_get_gap_device_name(device->client);
+               if (name)
+                       strncpy(device->name, name, MAX_NAME_LENGTH);
+       }
+#endif
 }
 
 static void gatt_client_service_changed(uint16_t start_handle,
@@ -4715,6 +6604,15 @@ static void gatt_server_init(struct btd_device *device, struct gatt_db *db)
                error("Failed to initialize bt_gatt_server");
 
        bt_gatt_server_set_debug(device->server, gatt_debug, NULL, NULL);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (!bt_gatt_server_set_mtu_changed(device->server,
+                                               att_mtu_changed,
+                                               device, NULL)) {
+               DBG("Failed to set mtu changed handler");
+               return;
+       }
+#endif
 }
 
 static bool local_counter(uint32_t *sign_cnt, void *user_data)
@@ -4745,6 +6643,33 @@ static bool remote_counter(uint32_t *sign_cnt, void *user_data)
        return true;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static bool load_svc_change_indication_status(struct btd_device *device, const char *local,
+                               const char *peer)
+{
+       char filename[PATH_MAX];
+       GKeyFile *key_file;
+       gboolean svc_change_regd = false;
+       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", local, peer);
+
+       key_file = g_key_file_new();
+       if (!g_key_file_load_from_file(key_file, filename, 0, NULL))
+               goto failed;
+
+       /* Load Service changed Registered flag */
+       svc_change_regd = g_key_file_get_boolean(key_file, "Att",
+                                               "SvcChangeRegd", NULL);
+       bt_att_set_svc_changed_indication_registered(device->att,
+                                               svc_change_regd);
+
+
+failed:
+       g_key_file_free(key_file);
+
+       return svc_change_regd;
+}
+#endif
+
 bool device_attach_att(struct btd_device *dev, GIOChannel *io)
 {
        GError *gerr = NULL;
@@ -4755,10 +6680,16 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
        struct btd_gatt_database *database;
        const bdaddr_t *src, *dst;
        char srcaddr[18], dstaddr[18];
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint8_t dst_type = BDADDR_BREDR;
+#endif
 
        bt_io_get(io, &gerr, BT_IO_OPT_SEC_LEVEL, &sec_level,
                                                BT_IO_OPT_IMTU, &mtu,
                                                BT_IO_OPT_CID, &cid,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                                               BT_IO_OPT_DEST_TYPE, &dst_type,
+#endif
                                                BT_IO_OPT_INVALID);
 
        if (gerr) {
@@ -4767,6 +6698,7 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
                return false;
        }
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (sec_level == BT_IO_SEC_LOW && dev->le_state.paired) {
                DBG("Elevating security level since LTK is available");
 
@@ -4779,6 +6711,7 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
                        return false;
                }
        }
+#endif
 
        dev->att_mtu = MIN(mtu, BT_ATT_MAX_LE_MTU);
        attrib = g_attrib_new(io, BT_ATT_DEFAULT_LE_MTU, false);
@@ -4804,6 +6737,16 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
                bt_att_set_remote_key(dev->att, dev->remote_csrk->key,
                                                        remote_counter, dev);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (dst_type != BDADDR_BREDR && device_get_rpa_exist(dev) == true) {
+               bt_att_set_remote_addr(dev->att,
+                                       device_get_rpa(dev), BDADDR_LE_RANDOM);
+       } else {
+               bt_att_set_remote_addr(dev->att,
+                                       &dev->bdaddr, dev->bdaddr_type);
+       }
+#endif
+
        database = btd_adapter_get_database(dev->adapter);
 
        src = btd_adapter_get_address(dev->adapter);
@@ -4825,6 +6768,11 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
         */
        adapter_connect_list_remove(dev->adapter, dev);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* load the service changed indication status on connection */
+       load_svc_change_indication_status(dev, srcaddr, dstaddr);
+#endif
+
        return true;
 }
 
@@ -4988,6 +6936,9 @@ static int device_browse_gatt(struct btd_device *device, DBusMessage *msg)
        struct btd_adapter *adapter = device->adapter;
        struct browse_req *req;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       DBG("");
+#endif
        req = browse_request_new(device, msg);
        if (!req)
                return -EBUSY;
@@ -5060,6 +7011,9 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
        uuid_t uuid;
        int err;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       DBG("");
+#endif
        req = browse_request_new(device, msg);
        if (!req)
                return -EBUSY;
@@ -5072,6 +7026,9 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
                                &device->bdaddr, &uuid, browse_cb, req, NULL,
                                req->sdp_flags);
        if (err < 0) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               device->browse = NULL;
+#endif
                browse_request_free(req);
                return err;
        }
@@ -5079,6 +7036,123 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
        return err;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void device_set_last_addr_type(struct btd_device *device, uint8_t type)
+{
+       if (!device)
+               return;
+
+       //DBG("Last addr type %d", type);
+
+       device->last_bdaddr_type = type;
+}
+
+gboolean device_is_ipsp_connected(struct btd_device * device)
+{
+       return device->ipsp_connected;
+}
+
+void device_set_ipsp_connected(struct btd_device *device, gboolean connected,
+                                       const unsigned char *ifname)
+{
+       char *iface_name = NULL;
+
+       if (device == NULL) {
+               error("device is NULL");
+               return;
+       }
+
+       if (device->ipsp_connected == connected)
+               return;
+
+       device->ipsp_connected = connected;
+
+       memset(device->if_name, 0, sizeof(device->if_name));
+       memcpy(device->if_name, ifname, 16);
+       iface_name = device->if_name;
+
+       DBG("ipsp_connected %d", connected);
+       DBG("ipsp_iface: %s is Up !", iface_name);
+
+       g_dbus_emit_signal(dbus_conn, device->path,
+                       DEVICE_INTERFACE, "IpspStateChanged",
+                       DBUS_TYPE_BOOLEAN, &connected,
+                       DBUS_TYPE_STRING, &iface_name,
+                       DBUS_TYPE_INVALID);
+}
+void device_le_data_length_changed(struct btd_device *device, uint16_t max_tx_octets,
+               uint16_t max_tx_time, uint16_t max_rx_octets, uint16_t max_rx_time)
+{
+       if (device == NULL) {
+               error("device is NULL");
+               return;
+       }
+
+       device->max_tx_octets = max_tx_octets;
+       device->max_tx_time = max_tx_time;
+       device->max_rx_octets = max_rx_octets;
+       device->max_rx_time = max_rx_time;
+
+       DBG("data length changed values :max_tx_octets: %d  max_tx_time: %d  max_rx_octets: %d  max_rx_time: %d",
+               max_tx_octets, max_tx_time, max_rx_octets, max_rx_time);
+
+       g_dbus_emit_signal(dbus_conn, device->path,
+               DEVICE_INTERFACE, "LEDataLengthChanged",
+               DBUS_TYPE_UINT16, &max_tx_octets,
+               DBUS_TYPE_UINT16, &max_tx_time,
+               DBUS_TYPE_UINT16, &max_rx_octets,
+               DBUS_TYPE_UINT16, &max_rx_time,
+               DBUS_TYPE_INVALID);
+}
+
+const bdaddr_t *device_get_rpa(struct btd_device *device)
+{
+       return device->rpa;
+}
+
+const uint8_t *device_get_irk_value(struct btd_device *device)
+{
+       return device->irk_val;
+}
+
+bool device_get_rpa_exist(struct btd_device *device)
+{
+       return device->rpa ? true : false;
+}
+
+void device_set_auth_addr_type(struct btd_device *device, uint8_t type)
+{
+       if (!device)
+               return;
+
+       DBG("Auth addr type %d", type);
+
+       device->auth_bdaddr_type = type;
+}
+
+void device_get_tizen_addr(struct btd_device *device, uint8_t type,
+                          struct device_addr_type *addr)
+{
+       if (!device || !addr)
+               return;
+
+       if (type == BDADDR_BREDR) {
+               bacpy(&addr->bdaddr, &device->bdaddr);
+               addr->bdaddr_type = BDADDR_BREDR;
+               return;
+       }
+
+       if (device->rpa) {
+               bacpy(&addr->bdaddr, device->rpa);
+               addr->bdaddr_type = BDADDR_LE_RANDOM;
+               return;
+       }
+
+       bacpy(&addr->bdaddr, &device->bdaddr);
+       addr->bdaddr_type = device->bdaddr_type;
+}
+#endif
+
 int device_discover_services(struct btd_device *device)
 {
        int err;
@@ -5168,6 +7242,50 @@ void btd_device_set_trusted(struct btd_device *device, gboolean trusted)
                                        DEVICE_INTERFACE, "Trusted");
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void btd_device_set_trusted_profiles(struct btd_device *device,
+               uint32_t pbap, uint32_t map, uint32_t sap)
+{
+       if (!device)
+               return;
+       DBG("TrustedProfiles Parameters: [PBAP %d] [MAP %d] [SAP %d]", pbap, map, sap);
+
+       if (device->trusted_profiles.pbap == pbap &&
+                       device->trusted_profiles.map == map &&
+                       device->trusted_profiles.sap == sap)
+               return;
+
+       device->trusted_profiles.pbap = pbap;
+       device->trusted_profiles.map = map;
+       device->trusted_profiles.sap = sap;
+
+       store_device_info(device);
+
+       g_dbus_emit_property_changed(dbus_conn, device->path,
+                                       DEVICE_INTERFACE, "TrustedProfiles");
+}
+
+void btd_device_set_restricted_profiles(struct btd_device *device,
+               uint32_t hfp_hs, uint32_t a2dp)
+{
+       if (!device)
+               return;
+       DBG("RestrictedProfiles Parameters: [HFP %d] [A2DP %d]", hfp_hs, a2dp);
+
+       if (device->restricted_profiles.hfp_hs == hfp_hs &&
+                       device->restricted_profiles.a2dp == a2dp)
+               return;
+
+       device->restricted_profiles.hfp_hs = hfp_hs;
+       device->restricted_profiles.a2dp = a2dp;
+
+       store_device_info(device);
+
+       g_dbus_emit_property_changed(dbus_conn, device->path,
+                                       DEVICE_INTERFACE, "RestrictedProfiles");
+}
+#endif
+
 void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type)
 {
        if (!device)
@@ -5205,6 +7323,15 @@ void device_set_rssi_with_delta(struct btd_device *device, int8_t rssi,
        if (!device)
                return;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (rssi == 0 || device->rssi == 0) {
+               if (device->rssi == rssi)
+                       return;
+       }
+
+       device->rssi = rssi;
+       DBG("rssi %d", rssi);
+#else
        if (rssi == 0 || device->rssi == 0) {
                if (device->rssi == rssi)
                        return;
@@ -5228,6 +7355,7 @@ void device_set_rssi_with_delta(struct btd_device *device, int8_t rssi,
 
                device->rssi = rssi;
        }
+#endif
 
        g_dbus_emit_property_changed(dbus_conn, device->path,
                                                DEVICE_INTERFACE, "RSSI");
@@ -5378,14 +7506,36 @@ void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type,
        if (status) {
                device_cancel_authentication(device, TRUE);
                device_bonding_failed(device, status);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               device->legacy_pairing = false;
+#endif
                return;
        }
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               device->legacy_pairing = false;
+#endif
        device_auth_req_free(device);
 
        /* If we're already paired nothing more is needed */
-       if (state->paired)
+       if (state->paired) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               if (bdaddr_type == BDADDR_BREDR && state->svc_resolved) {
+                       DBG("Link key has been changed. Report it");
+                       if (!device->rpa)
+                               g_dbus_emit_property_changed(dbus_conn,
+                                               device->path, DEVICE_INTERFACE,
+                                               "Paired");
+                       else
+                               DBG("Just overwrite Link key");
+               } else if (bdaddr_type == BDADDR_LE_RANDOM ||
+                          bdaddr_type == BDADDR_LE_PUBLIC) {
+                       DBG("Long Term Key has been changed. Report it");
+                       g_dbus_emit_property_changed(dbus_conn, device->path,
+                                       DEVICE_INTERFACE, "Paired");
+               }
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
                return;
+       }
 
        device_set_paired(device, bdaddr_type);
 
@@ -5473,6 +7623,13 @@ unsigned int device_wait_for_svc_complete(struct btd_device *dev,
                g_source_remove(dev->discov_timer);
                dev->discov_timer = g_idle_add(start_discovery, dev);
        }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       else if (!dev->browse) {
+               DBG("Service is not going on. Start discovery");
+               dev->discov_timer = g_idle_add(start_discovery, dev);
+       } else
+               DBG("Wait for service discovery");
+#endif
 
        return cb->id;
 }
@@ -5578,8 +7735,19 @@ void device_bonding_failed(struct btd_device *device, uint8_t status)
 
        DBG("status %u", status);
 
-       if (!bonding)
+       if (!bonding) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               if (device->legacy_pairing) {
+                       DBG("Emit LegacyPaired");
+                       g_dbus_emit_property_changed(dbus_conn, device->path,
+                                       DEVICE_INTERFACE, "LegacyPaired");
+               }
+#endif
                return;
+       }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       btd_device_set_temporary(device, TRUE);
+#endif
 
        if (device->authr)
                device_cancel_authentication(device, FALSE);
@@ -5624,9 +7792,16 @@ static void confirm_cb(struct agent *agent, DBusError *err, void *data)
        if (auth->agent == NULL)
                return;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       btd_adapter_confirm_reply(device->adapter, &device->bdaddr,
+                                               auth->addr_type,
+                                               err ? FALSE : TRUE);
+       device_set_auth_addr_type(device, BDADDR_BREDR);
+#else
        btd_adapter_confirm_reply(device->adapter, &device->bdaddr,
                                                        auth->addr_type,
                                                        err ? FALSE : TRUE);
+#endif
 
        agent_unref(device->authr->agent);
        device->authr->agent = NULL;
@@ -5645,8 +7820,14 @@ static void passkey_cb(struct agent *agent, DBusError *err,
        if (err)
                passkey = INVALID_PASSKEY;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       btd_adapter_passkey_reply(device->adapter, &device->bdaddr,
+                                       auth->addr_type, passkey);
+       device_set_auth_addr_type(device, BDADDR_BREDR);
+#else
        btd_adapter_passkey_reply(device->adapter, &device->bdaddr,
                                                auth->addr_type, passkey);
+#endif
 
        agent_unref(device->authr->agent);
        device->authr->agent = NULL;
@@ -5751,10 +7932,12 @@ int device_confirm_passkey(struct btd_device *device, uint8_t type,
 
        auth->passkey = passkey;
 
+#ifndef TIZEN_FEATURE_BLUEZ_CONFIRM_ONLY
        if (confirm_hint)
                err = agent_request_authorization(auth->agent, device,
                                                confirm_cb, auth, NULL);
        else
+#endif
                err = agent_request_confirmation(auth->agent, device, passkey,
                                                confirm_cb, auth, NULL);
 
@@ -5866,12 +8049,23 @@ void device_cancel_authentication(struct btd_device *device, gboolean aborted)
                cancel_authentication(auth);
 
        device_auth_req_free(device);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       device_set_auth_addr_type(device, BDADDR_BREDR);
+#endif
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+gboolean device_is_authenticating(struct btd_device *dev, uint8_t bdaddr_type)
+{
+       return (dev->auth_bdaddr_type == bdaddr_type && dev->authr != NULL);
+}
+#else
 gboolean device_is_authenticating(struct btd_device *device)
 {
        return (device->authr != NULL);
 }
+#endif
 
 struct gatt_primary *btd_device_get_primary(struct btd_device *device,
                                                        const char *uuid)
@@ -5966,6 +8160,11 @@ static sdp_list_t *read_device_records(struct btd_device *device)
        ba2str(btd_adapter_get_address(device->adapter), local);
        ba2str(&device->bdaddr, peer);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (device->rpa)
+               ba2str(device->rpa, peer);
+#endif
+
        snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer);
 
        key_file = g_key_file_new();
@@ -6053,6 +8252,126 @@ void device_set_appearance(struct btd_device *device, uint16_t value)
        store_device_info(device);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int device_get_rpa_res_char_value(struct btd_device *device)
+{
+       return device->rpa_res_support;
+}
+
+/* Store the RPA Resolution Characteristic Value of remote device.
+ * This value would be checked before start directed advertising using RPA.
+ */
+void device_set_rpa_res_char_value(struct btd_device *device, uint8_t value)
+{
+       if (device->rpa_res_support == value)
+               return;
+
+       device->rpa_res_support = value;
+       store_device_info(device);
+}
+
+void device_set_manufacturer_info(struct btd_device *device, struct eir_data *eir)
+{
+       if (!device)
+               return;
+
+       if (eir->manufacturer_data_len == 0)
+               return;
+
+       device->manufacturer_data = g_memdup(eir->manufacturer_data,
+                                                               eir->manufacturer_data_len);
+       device->manufacturer_data_len = eir->manufacturer_data_len;
+
+       store_device_info(device);
+
+       g_dbus_emit_property_changed(dbus_conn, device->path,
+                                       DEVICE_INTERFACE, "ManufacturerDataLen");
+
+       g_dbus_emit_property_changed(dbus_conn, device->path,
+                                       DEVICE_INTERFACE, "ManufacturerData");
+}
+
+
+void device_set_adv_report_info(struct btd_device *device, void *data, uint8_t data_len,
+                               uint8_t adv_type, int8_t rssi)
+{
+       if (!device)
+               return;
+
+       char peer_addr[18];
+       const char *paddr = peer_addr;
+       dbus_int32_t rssi_val = rssi;
+       int adv_len = data_len;
+       uint8_t addr_type;
+
+       ba2str(&device->bdaddr, peer_addr);
+
+       /* Replace address type for paired RPA device since IDA passed from controller */
+       if (device->rpa)
+               addr_type = BDADDR_LE_RANDOM;
+       else
+               addr_type = device->bdaddr_type;
+
+       g_dbus_emit_signal(dbus_conn, device->path,
+               DEVICE_INTERFACE, "AdvReport",
+               DBUS_TYPE_STRING, &paddr,
+               DBUS_TYPE_BYTE, &addr_type,
+               DBUS_TYPE_BYTE, &adv_type,
+               DBUS_TYPE_INT32, &rssi_val,
+               DBUS_TYPE_INT32, &adv_len,
+               DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, data_len,
+               DBUS_TYPE_INVALID);
+}
+
+void device_set_payload_timeout(struct btd_device *device,
+                       uint16_t payload_timeout)
+{
+       if (!device)
+               return;
+       if (device->auth_payload_timeout == payload_timeout)
+               return;
+
+       DBG("Payload timeout %d", payload_timeout);
+
+       device->auth_payload_timeout = payload_timeout;
+       g_dbus_emit_property_changed(dbus_conn, device->path,
+                               DEVICE_INTERFACE, "PayloadTimeout");
+}
+
+void device_set_disconnect_reason(struct btd_device *device, uint8_t reason)
+{
+       device->disc_reason = reason;
+}
+
+void btd_device_disconnect(struct btd_device *device)
+{
+       char dst[18];
+       struct btd_service *service;
+       btd_service_state_t state;
+
+       ba2str(&device->bdaddr, dst);
+
+       DBG("");
+       if (device->bredr_state.connected == false)
+               return;
+
+       service = btd_device_get_service(device, HFP_HS_UUID);
+       if (!service)
+               return;
+
+       state = btd_service_get_state(service);
+       DBG("Connected State : %d", state);
+
+       if (state == BTD_SERVICE_STATE_DISCONNECTED) {
+               btd_adapter_disconnect_device(device->adapter, &device->bdaddr,
+                                                               BDADDR_BREDR);
+       }
+
+       return;
+}
+
+#endif
+
 void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
                        uint16_t vendor, uint16_t product, uint16_t version)
 {
@@ -6083,8 +8402,26 @@ static void service_state_changed(struct btd_service *service,
        struct btd_device *device = btd_service_get_device(service);
        int err = btd_service_get_error(service);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (!err) {
+               if (old_state == BTD_SERVICE_STATE_UNAVAILABLE ||
+                                       new_state == BTD_SERVICE_STATE_UNAVAILABLE)
+                       DBG("Skip status updating ([%d] -> [%d])", old_state, new_state);
+               else
+                       g_dbus_emit_signal(dbus_conn, device->path,
+                               DEVICE_INTERFACE, "ProfileStateChanged",
+                               DBUS_TYPE_STRING, &profile->remote_uuid,
+                               DBUS_TYPE_INT32, &new_state,
+                               DBUS_TYPE_INVALID);
+       }
+
+       if (new_state == BTD_SERVICE_STATE_CONNECTING ||
+                       new_state == BTD_SERVICE_STATE_DISCONNECTING ||
+                       new_state == BTD_SERVICE_STATE_UNAVAILABLE)
+#else
        if (new_state == BTD_SERVICE_STATE_CONNECTING ||
                                new_state == BTD_SERVICE_STATE_DISCONNECTING)
+#endif
                return;
 
        if (old_state == BTD_SERVICE_STATE_CONNECTING)
@@ -6104,6 +8441,13 @@ struct btd_service *btd_device_get_service(struct btd_device *dev,
 
                if (g_str_equal(p->remote_uuid, remote_uuid))
                        return service;
+
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+               if (g_str_equal(HID_UUID, remote_uuid)) {
+                       if (strcmp(p->name, "hid-device") == 0)
+                               return service;
+               }
+#endif
        }
 
        return NULL;
@@ -6120,3 +8464,21 @@ void btd_device_cleanup(void)
 {
        btd_service_remove_state_cb(service_state_cb_id);
 }
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void btd_device_set_legacy_pairing(struct btd_device *dev, bool legacy_pairing)
+{
+       dev->legacy_pairing = legacy_pairing;
+}
+
+void btd_device_set_svc_changed_indication(struct btd_device *dev, bool value)
+{
+       bt_att_set_svc_changed_indication_registered(dev->att, value);
+       store_device_info(dev);
+}
+
+bool btd_device_get_svc_changed_indication(struct btd_device *dev)
+{
+       return bt_att_get_svc_changed_indication_registered(dev->att);
+}
+#endif
index 3cab366..ff39856 100755 (executable)
 
 struct btd_device;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+/* Device Physical channel connection Type */
+#define DEV_CONN_DEFAULT 0xFF /* Represents support for BREDR and LE */
+#define DEV_CONN_BREDR 0x00 /* Only BREDR */
+#define DEV_CONN_LE 0x01 /* Only LE*/
+#endif
+
 struct btd_device *device_create(struct btd_adapter *adapter,
                                const bdaddr_t *address, uint8_t bdaddr_type);
 struct btd_device *device_create_from_storage(struct btd_adapter *adapter,
@@ -59,6 +66,9 @@ struct device_addr_type {
        uint8_t bdaddr_type;
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int device_addr_type_strict_cmp(gconstpointer a, gconstpointer b);
+#endif
 int device_addr_type_cmp(gconstpointer a, gconstpointer b);
 GSList *btd_device_get_uuids(struct btd_device *device);
 void device_probe_profiles(struct btd_device *device, GSList *profiles);
@@ -83,6 +93,26 @@ void device_remove_profile(gpointer a, gpointer b);
 struct btd_adapter *device_get_adapter(struct btd_device *device);
 const bdaddr_t *device_get_address(struct btd_device *device);
 const char *device_get_path(const struct btd_device *device);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void device_set_remote_feature_flag(struct btd_device *device, int flags);
+gboolean device_is_bredrle(struct btd_device *device);
+void device_set_disconnect_reason(struct btd_device *device, uint8_t reason);
+void device_set_gatt_connected(struct btd_device *device, gboolean connected);
+void device_unpair(struct btd_device *device, gboolean remove_stored);
+gboolean device_get_gatt_connected(const struct btd_device *device);
+void device_set_rpa(struct btd_device *device, const bdaddr_t *rpa_addr);
+const bdaddr_t *device_get_rpa(struct btd_device *device);
+bool device_get_rpa_exist(struct btd_device *device);
+int device_rpa_cmp(gconstpointer a, gconstpointer b);
+int device_addr_cmp(gconstpointer a, gconstpointer b);
+void device_remove_stored_folder(struct btd_device *device);
+const uint8_t *device_get_irk_value(struct btd_device *device);
+void device_set_irk_value(struct btd_device *device, const uint8_t *val);
+void device_set_conn_update_state(struct btd_device *device, bool state);
+bool device_get_conn_update_state(struct btd_device *device);
+void btd_device_set_trusted_profiles(struct btd_device *device,
+               uint32_t pbap, uint32_t map, uint32_t sap);
+#endif
 gboolean device_is_temporary(struct btd_device *device);
 bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type);
 bool device_is_bonded(struct btd_device *device, uint8_t bdaddr_type);
@@ -119,7 +149,11 @@ int device_notify_passkey(struct btd_device *device, uint8_t type,
 int device_notify_pincode(struct btd_device *device, gboolean secure,
                                                        const char *pincode);
 void device_cancel_authentication(struct btd_device *device, gboolean aborted);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+gboolean device_is_authenticating(struct btd_device *dev, uint8_t bdaddr_type);
+#else
 gboolean device_is_authenticating(struct btd_device *device);
+#endif
 void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type);
 void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type);
 void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
@@ -135,6 +169,27 @@ void device_remove_disconnect_watch(struct btd_device *device, guint id);
 int device_get_appearance(struct btd_device *device, uint16_t *value);
 void device_set_appearance(struct btd_device *device, uint16_t value);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct eir_data;
+void device_set_manufacturer_info(struct btd_device *dev, struct eir_data *eir);
+void device_set_adv_report_info(struct btd_device *device, void *data,
+                       uint8_t data_len, uint8_t adv_info, int8_t rssi);
+void device_set_payload_timeout(struct btd_device *device,
+                       uint16_t payload_timeout);
+void device_set_auth_addr_type(struct btd_device *device, uint8_t type);
+void device_set_last_addr_type(struct btd_device *device, uint8_t type);
+gboolean device_is_ipsp_connected(struct btd_device * device);
+void device_set_ipsp_connected(struct btd_device *device, gboolean connected,
+                                               const unsigned char *ifname);
+int device_get_rpa_res_char_value(struct btd_device *device);
+void device_set_rpa_res_char_value(struct btd_device *device, uint8_t value);
+void device_le_data_length_changed(struct btd_device *device, uint16_t max_tx_octets,
+                       uint16_t max_tx_time, uint16_t max_rx_octets,
+                       uint16_t max_rx_time);
+void device_get_tizen_addr(struct btd_device *device, uint8_t type,
+                          struct device_addr_type *addr);
+#endif
+
 struct btd_device *btd_device_ref(struct btd_device *device);
 void btd_device_unref(struct btd_device *device);
 
@@ -158,6 +213,23 @@ bool device_remove_svc_complete_callback(struct btd_device *dev,
 struct btd_service *btd_device_get_service(struct btd_device *dev,
                                                const char *remote_uuid);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void device_print_addr(struct btd_device *dev);
+gboolean device_is_profile_trusted(struct btd_device *device,
+               const char *uuid);
+gboolean device_is_profile_blocked(struct btd_device *device,
+               const char *uuid);
+gboolean device_is_profile_restricted(struct btd_device *device,
+               const char *uuid);
+void btd_device_disconnect(struct btd_device *dev);
+void btd_device_set_legacy_pairing(struct btd_device *dev, bool legacy_pairing);
+void btd_device_set_svc_changed_indication(struct btd_device *dev, bool value);
+bool btd_device_get_svc_changed_indication(struct btd_device *dev);
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+void device_change_pkt_type(gpointer data, gpointer user_data);
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
+
 int device_discover_services(struct btd_device *device);
 int btd_device_connect_services(struct btd_device *dev, GSList *services);
 
index c984fa5..ee5975a 100755 (executable)
--- a/src/eir.c
+++ b/src/eir.c
@@ -65,6 +65,10 @@ void eir_data_free(struct eir_data *eir)
        eir->msd_list = NULL;
        g_slist_free_full(eir->sd_list, sd_free);
        eir->sd_list = NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       g_free(eir->manufacturer_data);
+       eir->manufacturer_data = NULL;
+#endif
 }
 
 static void eir_parse_uuid16(struct eir_data *eir, const void *data,
@@ -128,6 +132,26 @@ static void eir_parse_uuid128(struct eir_data *eir, const uint8_t *data,
 
 static char *name2utf8(const uint8_t *name, uint8_t len)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char *utf8_name;
+       char *in_name;
+       char *ptr;
+
+       in_name = g_malloc0(sizeof(char) * (len + 1));
+       /* Fix : NULL_RETURNS */
+       if (in_name == NULL)
+               return NULL;
+       memcpy(in_name, name, sizeof(char) * len);
+       in_name[len] = '\0';
+
+       if (!g_utf8_validate(in_name, -1, (const char **)&ptr))
+               *ptr = '\0';
+
+       utf8_name = g_strdup(in_name);
+       g_free(in_name);
+
+       return utf8_name;
+#else
        char utf8_name[HCI_MAX_NAME_LENGTH + 2];
        int i;
 
@@ -147,6 +171,7 @@ static char *name2utf8(const uint8_t *name, uint8_t len)
        g_strstrip(utf8_name);
 
        return g_strdup(utf8_name);
+#endif
 }
 
 static void eir_parse_msd(struct eir_data *eir, const uint8_t *data,
@@ -343,6 +368,14 @@ void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len)
                        break;
 
                case EIR_MANUFACTURER_DATA:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                       if (data_len < 1)
+                               break;
+
+                       eir->manufacturer_data = g_memdup(data,
+                                                               data_len);
+                       eir->manufacturer_data_len = data_len;
+#endif
                        eir_parse_msd(eir, data, data_len);
                        break;
 
index 219ee79..5e8bbd5 100755 (executable)
--- a/src/eir.h
+++ b/src/eir.h
@@ -92,6 +92,10 @@ struct eir_data {
        uint16_t did_source;
        GSList *msd_list;
        GSList *sd_list;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char *manufacturer_data;
+       uint8_t manufacturer_data_len;
+#endif
 };
 
 void eir_data_free(struct eir_data *eir);
index 114981c..a7d0829 100755 (executable)
@@ -62,6 +62,9 @@ struct btd_gatt_client {
 
        struct queue *services;
        struct queue *all_notify_clients;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       guint wait_charcs_id;
+#endif
 };
 
 struct service {
@@ -72,6 +75,12 @@ struct service {
        bt_uuid_t uuid;
        char *path;
        struct queue *chrcs;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       bool chrcs_ready;
+       struct queue *pending_ext_props;
+       guint idle_id;
+       guint idle_id2;
+#endif
 };
 
 typedef bool (*async_dbus_op_complete_t)(void *data);
@@ -216,6 +225,26 @@ static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len)
        return 0;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static int parse_type_value_arg(DBusMessageIter *iter, uint8_t *type, uint8_t **value,
+                                                       int *len)
+{
+       DBusMessageIter array;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BYTE)
+               return -EINVAL;
+       dbus_message_iter_get_basic(iter, type);
+       dbus_message_iter_next(iter);
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+               return -EINVAL;
+
+       dbus_message_iter_recurse(iter, &array);
+       dbus_message_iter_get_fixed_array(&array, value, len);
+
+       return 0;
+}
+#endif
+
 static void async_dbus_op_free(void *data)
 {
        struct async_dbus_op *op = data;
@@ -310,6 +339,12 @@ static void async_dbus_op_reply(struct async_dbus_op *op, int err,
                if (err) {
                        reply = err > 0 ? create_gatt_dbus_error(msg, err) :
                                btd_error_failed(msg, strerror(-err));
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                       if (err > 0)
+                               dbus_message_append_args(reply,
+                                                       DBUS_TYPE_BYTE, &err,
+                                                       DBUS_TYPE_INVALID);
+#endif
                        goto send_reply;
                }
 
@@ -545,6 +580,29 @@ static struct async_dbus_op *start_write_request(DBusMessage *msg,
 
        return op;
 }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static struct async_dbus_op *start_write_cmd(DBusMessage *msg, uint16_t handle,
+                               struct bt_gatt_client *gatt, bool signed_write,
+                               const uint8_t *value, size_t value_len,
+                               void *data, async_dbus_op_complete_t complete)
+{
+       struct async_dbus_op *op;
+
+       op = async_dbus_op_new(msg, data);
+       op->complete = complete;
+
+       op->id = bt_gatt_client_write_without_response_async(gatt, handle,
+                       signed_write, value, value_len,
+                       write_cb, op, async_dbus_op_free);
+
+       if (!op->id) {
+               async_dbus_op_free(op);
+               return NULL;
+       }
+
+       return op;
+}
+#endif
 
 static bool desc_write_complete(void *data)
 {
@@ -824,6 +882,17 @@ static void write_characteristic_cb(struct gatt_db_attribute *attr, int err,
 
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void notify_characteristic_cb(struct gatt_db_attribute *attr, int err,
+                                                               void *user_data)
+{
+       if (err) {
+               error("Failed to notify_characteristic_cb : %d", err);
+               return;
+       }
+}
+#endif
+
 static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
                                        uint16_t length, void *user_data)
 {
@@ -939,6 +1008,7 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn,
         *     - If value is larger than MTU - 3: long-write
         *   * "write-without-response" property set -> write command.
         */
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if ((chrc->ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE)) {
                supported = true;
                chrc->write_op = start_long_write(msg, chrc->value_handle, gatt,
@@ -947,6 +1017,7 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn,
                if (chrc->write_op)
                        return NULL;
        }
+#endif
 
        if (chrc->props & BT_GATT_CHRC_PROP_WRITE) {
                uint16_t mtu;
@@ -989,6 +1060,78 @@ fail:
        return btd_error_not_supported(msg);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *characteristic_write_value_by_type(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct characteristic *chrc = user_data;
+       struct bt_gatt_client *gatt = chrc->service->client->gatt;
+       DBusMessageIter iter;
+       uint8_t *value = NULL;
+       int value_len = 0;
+       bool supported = false;
+       uint8_t write_type = 0;
+       uint16_t offset = 0;
+
+       if (!gatt)
+               return btd_error_failed(msg, "Not connected");
+
+       if (chrc->write_op)
+               return btd_error_in_progress(msg);
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (parse_type_value_arg(&iter, &write_type, &value, &value_len))
+               return btd_error_invalid_args(msg);
+
+       if (parse_options(&iter, &offset))
+               return btd_error_invalid_args(msg);
+
+       if ((write_type & chrc->props) == BT_GATT_CHRC_PROP_WRITE) {
+               uint16_t mtu;
+
+               supported = true;
+               mtu = bt_gatt_client_get_mtu(gatt);
+               if (!mtu)
+                       return btd_error_failed(msg, "No ATT transport");
+
+               if (value_len <= mtu - 3 && !offset)
+                       chrc->write_op = start_write_request(msg,
+                                               chrc->value_handle,
+                                               gatt, value, value_len,
+                                               chrc, chrc_write_complete);
+               else
+                       chrc->write_op = start_long_write(msg,
+                                               chrc->value_handle, gatt,
+                                               false, value, value_len, offset,
+                                               chrc, chrc_write_complete);
+
+               if (chrc->write_op)
+                       return NULL;
+       } else if ((write_type & chrc->props) ==
+                       BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP) {
+               supported = true;
+               chrc->write_op = start_write_cmd(msg, chrc->value_handle, gatt,
+                                                false, value, value_len,
+                                                chrc, chrc_write_complete);
+               if (chrc->write_op)
+                       return NULL;
+       } else if ((write_type & chrc->props) == BT_GATT_CHRC_PROP_AUTH) {
+               supported = true;
+               chrc->write_op = start_write_cmd(msg, chrc->value_handle, gatt,
+                                                true, value, value_len,
+                                                chrc, chrc_write_complete);
+               if (chrc->write_op)
+                       return NULL;
+       }
+
+       if (supported)
+               return btd_error_failed(msg, "Failed to initiate write");
+
+       return btd_error_not_supported(msg);
+}
+#endif
+
 struct notify_client {
        struct characteristic *chrc;
        int ref_count;
@@ -1099,6 +1242,27 @@ static bool match_notify_sender(const void *a, const void *b)
        return strcmp(client->owner, sender) == 0;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void gatt_characteristic_value_changed(struct notify_client *client, const uint8_t *data, uint16_t data_len, void *user_data)
+{
+       struct characteristic *chrc = user_data;
+       char *chrc_path = strdup(chrc->path);
+       dbus_int32_t result = 0;
+
+       g_dbus_emit_signal_to_dest(btd_get_dbus_connection(),
+               client->owner, chrc_path,
+               GATT_CHARACTERISTIC_IFACE, "GattValueChanged",
+               DBUS_TYPE_INT32, &result,
+               DBUS_TYPE_STRING, &chrc_path,
+               DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, data_len,
+               DBUS_TYPE_INVALID);
+
+       if (chrc_path)
+               free(chrc_path);
+
+}
+#endif
+
 static void notify_cb(uint16_t value_handle, const uint8_t *value,
                                        uint16_t length, void *user_data)
 {
@@ -1112,8 +1276,15 @@ static void notify_cb(uint16_t value_handle, const uint8_t *value,
         * applications.
         */
        gatt_db_attribute_reset(chrc->attr);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL,
+                                               notify_characteristic_cb, chrc);
+
+       gatt_characteristic_value_changed(client, value, length, chrc);
+#else
        gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL,
                                                write_characteristic_cb, chrc);
+#endif
 }
 
 static void create_notify_reply(struct async_dbus_op *op, bool success,
@@ -1245,14 +1416,48 @@ static DBusMessage *characteristic_stop_notify(DBusConnection *conn,
        return dbus_message_new_method_return(msg);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void append_desc_path(void *data, void *user_data)
+{
+       struct descriptor *desc = data;
+       DBusMessageIter *array = user_data;
+
+       dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH,
+                                                               &desc->path);
+}
+
+static gboolean characteristic_get_descriptors(
+                                       const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct characteristic *chrc = data;
+       DBusMessageIter array;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array);
+
+       queue_foreach(chrc->descs, append_desc_path, &array);
+
+       dbus_message_iter_close_container(iter, &array);
+
+       return TRUE;
+}
+#endif
+
 static const GDBusPropertyTable characteristic_properties[] = {
        { "UUID", "s", characteristic_get_uuid, NULL, NULL },
        { "Service", "o", characteristic_get_service, NULL, NULL },
        { "Value", "ay", characteristic_get_value, NULL,
                                        characteristic_value_exists },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { "ChangedValue", "ay", characteristic_get_value, NULL,
+                                       characteristic_value_exists },
+#endif
        { "Notifying", "b", characteristic_get_notifying, NULL,
                                        characteristic_notifying_exists },
        { "Flags", "as", characteristic_get_flags, NULL, NULL },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { "Descriptors", "ao", characteristic_get_descriptors },
+#endif
        { }
 };
 
@@ -1264,6 +1469,11 @@ static const GDBusMethodTable characteristic_methods[] = {
                                                { "options", "a{sv}" }),
                                        NULL,
                                        characteristic_write_value) },
+       { GDBUS_ASYNC_METHOD("WriteValuebyType",
+                                       GDBUS_ARGS({ "type", "y" }, { "value", "ay" },
+                                               { "options", "a{sv}" }),
+                                       GDBUS_ARGS({ "result", "y" }),
+                                       characteristic_write_value_by_type) },
        { GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL,
                                        characteristic_start_notify) },
        { GDBUS_METHOD("StopNotify", NULL, NULL,
@@ -1271,6 +1481,19 @@ static const GDBusMethodTable characteristic_methods[] = {
        { }
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static const GDBusSignalTable service_signals[] = {
+       { GDBUS_SIGNAL("GattServiceAdded",
+                       GDBUS_ARGS({ "Service Path","s"})) },
+};
+static const GDBusSignalTable characteristic_signals[] = {
+       { GDBUS_SIGNAL("GattValueChanged",
+                       GDBUS_ARGS({ "Result", "i"},
+                                       { "Characteristic Path","s"},
+                                       { "GattData", "ay"})) },
+};
+#endif
+
 static void characteristic_free(void *data)
 {
        struct characteristic *chrc = data;
@@ -1295,11 +1518,24 @@ static struct characteristic *characteristic_create(
        chrc->notify_clients = queue_new();
        chrc->service = service;
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        gatt_db_attribute_get_char_data(attr, &chrc->handle,
                                                        &chrc->value_handle,
                                                        &chrc->props,
                                                        &chrc->ext_props,
                                                        &uuid);
+#else
+       if (!gatt_db_attribute_get_char_data(attr, &chrc->handle,
+                                                       &chrc->value_handle,
+                                                       &chrc->props,
+                                                       &chrc->ext_props,
+                                                       &uuid)) {
+               queue_destroy(chrc->descs, NULL);
+               queue_destroy(chrc->notify_clients, NULL);
+               free(chrc);
+               return NULL;
+       }
+#endif
 
        chrc->attr = gatt_db_get_attribute(service->client->db,
                                                        chrc->value_handle);
@@ -1316,7 +1552,11 @@ static struct characteristic *characteristic_create(
 
        if (!g_dbus_register_interface(btd_get_dbus_connection(), chrc->path,
                                                GATT_CHARACTERISTIC_IFACE,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                                               characteristic_methods, characteristic_signals,
+#else
                                                characteristic_methods, NULL,
+#endif
                                                characteristic_properties,
                                                chrc, characteristic_free)) {
                error("Unable to register GATT characteristic with handle "
@@ -1398,10 +1638,40 @@ static gboolean service_get_primary(const GDBusPropertyTable *property,
        return TRUE;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void append_chrc_path(void *data, void *user_data)
+{
+       struct characteristic *chrc = data;
+       DBusMessageIter *array = user_data;
+
+       dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH,
+                                                               &chrc->path);
+}
+
+static gboolean service_get_characteristics(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct service *service = data;
+       DBusMessageIter array;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array);
+
+       if (service->chrcs_ready)
+               queue_foreach(service->chrcs, append_chrc_path, &array);
+
+       dbus_message_iter_close_container(iter, &array);
+
+       return TRUE;
+}
+#endif
+
 static const GDBusPropertyTable service_properties[] = {
        { "UUID", "s", service_get_uuid },
        { "Device", "o", service_get_device },
        { "Primary", "b", service_get_primary },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { "Characteristics", "ao", service_get_characteristics },
+#endif
        { }
 };
 
@@ -1436,7 +1706,11 @@ static struct service *service_create(struct gatt_db_attribute *attr,
 
        if (!g_dbus_register_interface(btd_get_dbus_connection(), service->path,
                                                GATT_SERVICE_IFACE,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                                               NULL, service_signals,
+#else
                                                NULL, NULL,
+#endif
                                                service_properties,
                                                service, service_free)) {
                error("Unable to register GATT service with handle 0x%04x for "
@@ -1465,12 +1739,36 @@ static void unregister_service(void *data)
 
        DBG("Removing GATT service: %s", service->path);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (service->idle_id)
+               g_source_remove(service->idle_id);
+
+       if (service->idle_id2)
+               g_source_remove(service->idle_id2);
+#endif
+
        queue_remove_all(service->chrcs, NULL, NULL, unregister_characteristic);
 
        g_dbus_unregister_interface(btd_get_dbus_connection(), service->path,
                                                        GATT_SERVICE_IFACE);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void notify_chrcs(struct service *service)
+{
+
+       if (service->chrcs_ready ||
+                               !queue_isempty(service->pending_ext_props))
+               return;
+
+       service->chrcs_ready = true;
+
+       g_dbus_emit_property_changed(btd_get_dbus_connection(), service->path,
+                                                       GATT_SERVICE_IFACE,
+                                                       "Characteristics");
+}
+#endif
+
 struct export_data {
        void *root;
        bool failed;
@@ -1527,6 +1825,11 @@ static void export_char(struct gatt_db_attribute *attr, void *user_data)
 
        queue_push_tail(service->chrcs, charac);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (charac->ext_props_handle)
+               queue_push_tail(service->pending_ext_props, charac);
+#endif
+
        return;
 
 fail:
@@ -1546,6 +1849,38 @@ static bool create_characteristics(struct gatt_db_attribute *attr,
        return !data.failed;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean set_chrcs_ready(gpointer user_data)
+{
+       struct service *service = user_data;
+
+       service->idle_id = 0;
+       notify_chrcs(service);
+
+       return FALSE;
+}
+
+static gboolean notify_service_added_complete(gpointer user_data)
+{
+       struct service *service = user_data;
+       char *svc_path;
+
+       if (service == NULL || service->path == NULL)
+               return FALSE;
+
+       svc_path = g_strdup(service->path);
+
+       g_dbus_emit_signal(btd_get_dbus_connection(), service->path,
+               GATT_SERVICE_IFACE, "GattServiceAdded",
+               DBUS_TYPE_STRING, &svc_path,
+               DBUS_TYPE_INVALID);
+
+       g_free(svc_path);
+
+       return FALSE;
+}
+#endif
+
 static void export_service(struct gatt_db_attribute *attr, void *user_data)
 {
        struct btd_gatt_client *client = user_data;
@@ -1565,6 +1900,19 @@ static void export_service(struct gatt_db_attribute *attr, void *user_data)
        }
 
        queue_push_tail(client->services, service);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /*
+        * Asynchronously update the "Characteristics" property of the service.
+        * If there are any pending reads to obtain the value of the "Extended
+        * Properties" descriptor then wait until they are complete.
+        */
+       if (!service->chrcs_ready && queue_isempty(service->pending_ext_props))
+               service->idle_id = g_idle_add(set_chrcs_ready, service);
+
+       if (service->primary == true)
+               service->idle_id2 = g_idle_add(notify_service_added_complete, service);
+#endif
 }
 
 static void create_services(struct btd_gatt_client *client)
@@ -1602,6 +1950,10 @@ void btd_gatt_client_destroy(struct btd_gatt_client *client)
        if (!client)
                return;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (client->wait_charcs_id)
+               g_source_remove(client->wait_charcs_id);
+#endif
        queue_destroy(client->services, unregister_service);
        queue_destroy(client->all_notify_clients, NULL);
        bt_gatt_client_unref(client->gatt);
@@ -1637,6 +1989,58 @@ static void register_notify(void *data, void *user_data)
        notify_client_free(notify_client);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void check_chrcs_ready(void *data, void *user_data)
+{
+       gboolean *chrcs_ready = user_data;
+       struct service *service = data;
+
+       /*
+       * Return FALSE if charcteristics are not ready or if there is
+       * any pending request to read char. extended properties exist.
+       */
+       if (!service->chrcs_ready ||
+               !queue_isempty(service->pending_ext_props))
+               *chrcs_ready = FALSE;
+}
+
+static gboolean check_all_chrcs_ready(gpointer user_data)
+{
+       struct btd_gatt_client *client = user_data;
+       gboolean all_chrcs_ready = TRUE;
+       static int count = 0;
+
+       queue_foreach(client->services, check_chrcs_ready, &all_chrcs_ready);
+
+       /*
+       * By adding below condition, forcing to call check_chrcs_ready()
+       * function to check whether all char./extended properties are ready.
+       * Above function would be called max. for 50 times (Assuming more
+       * no of services). Emit signal only when all characteristics are ready.
+       */
+       if (all_chrcs_ready == FALSE && count < 50) {
+               count++;
+               return TRUE;
+       }
+
+       /*
+       * There are chances when all of the primary services are not found,
+       * instead service changed is received while reading REQs in progress,
+       * so emit signal after service changed operation is completed (if any).
+       */
+       if (bt_gatt_client_svc_changed_received(client->gatt))
+               return TRUE;
+
+       device_set_gatt_connected(client->device, TRUE);
+
+       client->wait_charcs_id = 0;
+
+       count = 0;
+
+       return FALSE;
+}
+#endif
+
 void btd_gatt_client_ready(struct btd_gatt_client *client)
 {
        if (!client)
@@ -1658,6 +2062,20 @@ void btd_gatt_client_ready(struct btd_gatt_client *client)
        DBG("GATT client ready");
 
        create_services(client);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /*
+        * In case of more number of services and services having
+        * characteristics extended properties; GattConnected signal
+        * should be emitted only after all the characteristics are ready.
+        * This piece of code checks all the characteristics periodically and
+        * emit the signal if characteristics are ready.
+        */
+       if (client->wait_charcs_id > 0)
+               g_source_remove(client->wait_charcs_id);
+
+       client->wait_charcs_id = g_timeout_add(100,
+                       check_all_chrcs_ready, client);
+#endif
 }
 
 void btd_gatt_client_connected(struct btd_gatt_client *client)
@@ -1731,6 +2149,13 @@ void btd_gatt_client_disconnected(struct btd_gatt_client *client)
 
        DBG("Device disconnected. Cleaning up.");
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (client->wait_charcs_id) {
+               g_source_remove(client->wait_charcs_id);
+               client->wait_charcs_id = 0;
+       }
+#endif
+
        /*
         * TODO: Once GATT over BR/EDR is properly supported, we should pass the
         * correct bdaddr_type based on the transport over which GATT is being
index 0877b25..9fa059b 100755 (executable)
@@ -170,6 +170,10 @@ struct device_info {
        uint8_t bdaddr_type;
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void conf_cb(void *user_data);
+#endif
+
 static void ccc_cb_free(void *data)
 {
        struct ccc_cb_data *ccc_cb = data;
@@ -223,6 +227,56 @@ find_device_state(struct btd_gatt_database *database, bdaddr_t *bdaddr,
        return queue_find(database->device_states, dev_state_match, &dev_info);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void send_service_changed_indication_on_reconnect(
+                                       struct btd_device *device,
+                                       struct bt_gatt_server *server)
+{
+       struct btd_gatt_database *database;
+       uint16_t start_handle = 0X0001, end_handle = 0xFFFF;
+       uint8_t value[4];
+       uint16_t svc_chngd_handle;
+       DBG("");
+
+       database = btd_adapter_get_database(device_get_adapter(device));
+
+       if (!database)
+               return;
+
+       svc_chngd_handle = gatt_db_attribute_get_handle(database->svc_chngd_ccc);
+
+       put_le16(start_handle, value);
+       put_le16(end_handle, value + 2);
+
+       bt_gatt_server_send_indication(server, svc_chngd_handle,
+                               value, sizeof(value), conf_cb,
+                               NULL, NULL);
+}
+
+static bool dev_addr_match(const void *a, const void *b)
+{
+       const struct device_state *dev_state = a;
+       const struct device_info *dev_info = b;
+
+       if (bacmp(&dev_state->bdaddr, &dev_info->bdaddr) == 0)
+               return TRUE;
+
+       return FALSE;
+}
+
+static struct device_state *
+find_device_state_from_address(struct btd_gatt_database *database, const bdaddr_t *bdaddr)
+{
+       struct device_info dev_info;
+
+       memset(&dev_info, 0, sizeof(dev_info));
+
+       bacpy(&dev_info.bdaddr, bdaddr);
+
+       return queue_find(database->device_states, dev_addr_match, &dev_info);
+}
+#endif
+
 static bool ccc_state_match(const void *a, const void *b)
 {
        const struct ccc_state *ccc = a;
@@ -242,6 +296,12 @@ static struct device_state *device_state_create(bdaddr_t *bdaddr,
                                                        uint8_t bdaddr_type)
 {
        struct device_state *dev_state;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char addr[18] = { 0 };
+
+       ba2str(bdaddr, addr);
+       DBG("create device_state for %s [%d]", addr, bdaddr_type);
+#endif
 
        dev_state = new0(struct device_state, 1);
        dev_state->ccc_states = queue_new();
@@ -279,6 +339,9 @@ static struct ccc_state *get_ccc_state(struct btd_gatt_database *database,
 {
        struct device_state *dev_state;
        struct ccc_state *ccc;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char addr[18] = { 0 };
+#endif
 
        dev_state = get_device_state(database, bdaddr, bdaddr_type);
 
@@ -286,6 +349,12 @@ static struct ccc_state *get_ccc_state(struct btd_gatt_database *database,
        if (ccc)
                return ccc;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       ba2str(bdaddr, addr);
+       DBG("create ccc_state of handle: 0x%04x for %s [%d]",
+                       handle, addr, bdaddr_type);
+#endif
+
        ccc = new0(struct ccc_state, 1);
        ccc->handle = handle;
        queue_push_tail(dev_state->ccc_states, ccc);
@@ -499,8 +568,22 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
                return;
 
        device_attach_att(device, io);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (btd_device_get_svc_changed_indication(device)) {
+               send_service_changed_indication_on_reconnect(device,
+                                       btd_device_get_gatt_server(device));
+       }
+#endif
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define LE_BEARER_POSTFIX      " LE"
+#define LE_BEARER_POSTFIX_LEN  3
+
+static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type);
+#endif
+
 static void gap_device_name_read_cb(struct gatt_db_attribute *attrib,
                                        unsigned int id, uint16_t offset,
                                        uint8_t opcode, struct bt_att *att,
@@ -511,10 +594,32 @@ static void gap_device_name_read_cb(struct gatt_db_attribute *attrib,
        size_t len = 0;
        const uint8_t *value = NULL;
        const char *device_name;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       bdaddr_t dst = { { 0 } };
+       uint8_t dst_type = 0;
+       char le_name[MAX_NAME_LENGTH + 1];
+#endif
 
        DBG("GAP Device Name read request\n");
 
        device_name = btd_adapter_get_name(database->adapter);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (get_dst_info(att, &dst, &dst_type) && dst_type != BDADDR_BREDR &&
+           bacmp(btd_adapter_get_address(database->adapter),
+                       btd_adapter_get_le_address(database->adapter))) {
+               char *ptr = NULL;
+
+               g_strlcpy(le_name, device_name,
+                               sizeof(le_name) - LE_BEARER_POSTFIX_LEN);
+               if (!g_utf8_validate(le_name, -1, (const char **)&ptr))
+                       *ptr = '\0';
+
+               g_strlcat(le_name, LE_BEARER_POSTFIX, sizeof(le_name));
+               DBG("Append LE bearer postfix : %s", le_name);
+
+               device_name = le_name;
+       }
+#endif
        len = strlen(device_name);
 
        if (offset > len) {
@@ -560,6 +665,33 @@ done:
        gatt_db_attribute_read_result(attrib, id, error, value, len);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void gap_rpa_res_support_read_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct btd_gatt_database *database = user_data;
+       uint8_t error = 0;
+       size_t len = 1;
+       const uint8_t *value = NULL;
+       uint8_t rpa_res_support = 0x00;
+
+       rpa_res_support = btd_adapter_get_rpa_res_support_value(database->adapter);
+
+       if (offset > 1) {
+               error = BT_ATT_ERROR_INVALID_OFFSET;
+               goto done;
+       }
+
+       len -= offset;
+       value = len ? &rpa_res_support : NULL;
+
+done:
+       gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+#endif
+
 static sdp_record_t *record_new(uuid_t *uuid, uint16_t start, uint16_t end)
 {
        sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto;
@@ -632,14 +764,20 @@ static uint32_t database_add_record(struct btd_gatt_database *database,
                return 0;
 
        if (name != NULL)
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               sdp_set_info_attr(record, name, "Samsung", NULL);
+#else
                sdp_set_info_attr(record, name, "BlueZ", NULL);
+#endif
 
        sdp_uuid16_create(&gap_uuid, UUID_GAP);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) {
                sdp_set_url_attr(record, "http://www.bluez.org/",
                                "http://www.bluez.org/",
                                "http://www.bluez.org/");
        }
+#endif
 
        if (adapter_service_add(database->adapter, record) == 0)
                return record->handle;
@@ -677,11 +815,23 @@ static void populate_gap_service(struct btd_gatt_database *database)
                                                        gap_appearance_read_cb,
                                                        NULL, database);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* Central address resolution characteristic */
+       bt_uuid16_create(&uuid, GATT_CHARAC_CENTRAL_RPA_RESOLUTION);
+       gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ,
+                                                       BT_GATT_CHRC_PROP_READ,
+                                                       gap_rpa_res_support_read_cb,
+                                                       NULL, database);
+#endif
+
        gatt_db_service_set_active(service, true);
 }
 
 static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       return bt_att_get_remote_addr(att, dst, dst_type);
+#else
        GIOChannel *io = NULL;
        GError *gerr = NULL;
 
@@ -700,7 +850,9 @@ static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
        }
 
        g_io_channel_unref(io);
+
        return true;
+#endif
 }
 
 static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib,
@@ -796,6 +948,23 @@ static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
 
 done:
        gatt_db_attribute_write_result(attrib, id, ecode);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (!ecode) {
+               uint16_t svc_chngd_handle = gatt_db_attribute_get_handle(database->svc_chngd_ccc);
+               if (handle == svc_chngd_handle) {
+                       if (ccc->value[0] != 0) {
+                               struct btd_device *device = NULL;
+                               device = btd_adapter_get_device(
+                                                       database->adapter,
+                                                       &bdaddr, bdaddr_type);
+
+                               if (!device)
+                                       return;
+                               btd_device_set_svc_changed_indication(device, true);
+                       }
+               }
+       }
+#endif
 }
 
 static struct gatt_db_attribute *
@@ -868,10 +1037,182 @@ struct notify {
        bool indicate;
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct notify_indicate {
+       struct btd_gatt_database *database;
+       GDBusProxy *proxy;
+       uint16_t handle, ccc_handle;
+       const uint8_t *value;
+       uint16_t len;
+       bool indicate;
+};
+
+struct notify_indicate_cb {
+       GDBusProxy *proxy;
+       struct btd_device *device;
+};
+
+static void indicate_confirm_free(void *data)
+{
+       struct notify_indicate_cb *indicate = data;
+
+       if (indicate)
+               free(indicate);
+}
+
+static void indicate_confirm_setup_cb(DBusMessageIter *iter, void *user_data)
+{
+       struct btd_device *device = user_data;
+       char dst_addr[18] = { 0 };
+       char *addr_value = dst_addr;
+       gboolean complete = FALSE;
+
+       if (device_get_rpa_exist(device) == true) {
+               ba2str(device_get_rpa(device), dst_addr);
+       } else {
+               ba2str(device_get_address(device), dst_addr);
+       }
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &addr_value);
+
+       complete = TRUE;
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &complete);
+}
+
+static void indicate_confirm_reply_cb(DBusMessage *message, void *user_data)
+{
+       DBusError error;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               DBG("Failed to send indication/notification");
+               dbus_error_free(&error);
+               return;
+       }
+}
+#endif
+
 static void conf_cb(void *user_data)
 {
        DBG("GATT server received confirmation");
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       struct notify_indicate_cb *confirm = user_data;
+
+       if (confirm) {
+               /* Send confirmation to application */
+               if (g_dbus_proxy_method_call(confirm->proxy, "IndicateConfirm",
+                                                       indicate_confirm_setup_cb,
+                                                       indicate_confirm_reply_cb, confirm->device,
+                                                       NULL) == TRUE)
+                       return;
+       }
+#endif
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void send_notification_indication_to_device(void *data, void *user_data)
+{
+       struct device_state *device_state = data;
+       struct notify_indicate *notify_indicate = user_data;
+       struct ccc_state *ccc;
+       struct btd_device *device;
+       struct notify_indicate_cb *confirm;
+
+       ccc = find_ccc_state(device_state, notify_indicate->ccc_handle);
+       if (!ccc)
+               return;
+
+       if (!ccc->value[0] || (notify_indicate->indicate && !(ccc->value[0] & 0x02)))
+               return;
+
+       device = btd_adapter_get_device(notify_indicate->database->adapter,
+                                               &device_state->bdaddr,
+                                               device_state->bdaddr_type);
+       if (!device)
+               return;
+
+       confirm = new0(struct notify_indicate_cb, 1);
+       confirm->proxy = notify_indicate->proxy;
+       confirm->device = device;
+       /*
+        * TODO: If the device is not connected but bonded, send the
+        * notification/indication when it becomes connected.
+        */
+       if (!notify_indicate->indicate) {
+               DBG("GATT server sending notification");
+               bt_gatt_server_send_notification(
+                                       btd_device_get_gatt_server(device),
+                                       notify_indicate->handle, notify_indicate->value,
+                                       notify_indicate->len);
+               /* In case of Notification, send response to application
+                * as remote device do not respond for notification */
+               conf_cb(confirm);
+               indicate_confirm_free((void *)confirm);
+               return;
+       }
+
+       DBG("GATT server sending indication");
+
+       bt_gatt_server_send_indication(btd_device_get_gatt_server(device),
+                                                       notify_indicate->handle,
+                                                       notify_indicate->value,
+                                                       notify_indicate->len, conf_cb,
+                                                       confirm, indicate_confirm_free);
+}
+
+static void send_notification_indication_to_devices(GDBusProxy *proxy,
+                                       struct btd_gatt_database *database,
+                                       uint16_t handle, const uint8_t *value,
+                                       uint16_t len, uint16_t ccc_handle,
+                                       bool indicate)
+{
+       struct notify_indicate notify_indicate;
+
+       DBG("notify for handle: 0x%04x", handle);
+
+       memset(&notify_indicate, 0, sizeof(notify_indicate));
+
+       notify_indicate.database = database;
+       notify_indicate.proxy = proxy;
+       notify_indicate.handle = handle;
+       notify_indicate.ccc_handle = ccc_handle;
+       notify_indicate.value = value;
+       notify_indicate.len = len;
+       notify_indicate.indicate = indicate;
+
+       queue_foreach(database->device_states, send_notification_indication_to_device,
+                                                               &notify_indicate);
+}
+
+static void send_unicast_notification_indication_to_device(GDBusProxy *proxy,
+                                       struct btd_gatt_database *database,
+                                       uint16_t handle, const uint8_t *value,
+                                       uint16_t len, uint16_t ccc_handle,
+                                       bool indicate, const bdaddr_t *unicast_addr)
+{
+       struct device_state *dev_state;
+       struct notify_indicate notify_indicate;
+
+       DBG("notify for handle: 0x%04x", handle);
+
+       memset(&notify_indicate, 0, sizeof(notify_indicate));
+
+       notify_indicate.database = database;
+       notify_indicate.proxy = proxy;
+       notify_indicate.handle = handle;
+       notify_indicate.ccc_handle = ccc_handle;
+       notify_indicate.value = value;
+       notify_indicate.len = len;
+       notify_indicate.indicate = indicate;
+
+        /* Find and return a device state. */
+       dev_state = find_device_state_from_address(database, unicast_addr);
+
+       if (dev_state)
+               send_notification_indication_to_device(dev_state, &notify_indicate);
 }
+#endif
 
 static void send_notification_to_device(void *data, void *user_data)
 {
@@ -1620,6 +1961,7 @@ static struct pending_op *pending_read_new(struct btd_device *device,
        return op;
 }
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
 static void append_options(DBusMessageIter *iter, void *user_data)
 {
        struct pending_op *op = user_data;
@@ -1627,10 +1969,22 @@ static void append_options(DBusMessageIter *iter, void *user_data)
 
        dict_append_entry(iter, "device", DBUS_TYPE_OBJECT_PATH, &path);
 }
+#endif
 
 static void read_setup_cb(DBusMessageIter *iter, void *user_data)
 {
        struct pending_op *op = user_data;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char dst_addr[18] = { 0 };
+       char *addr_value = dst_addr;
+       uint16_t offset = 0;
+       const bdaddr_t *bdaddr = device_get_address(op->device);
+
+       ba2str(bdaddr, dst_addr);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &addr_value);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &op->id);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &offset);
+#else
        DBusMessageIter dict;
 
        dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
@@ -1643,6 +1997,7 @@ static void read_setup_cb(DBusMessageIter *iter, void *user_data)
        append_options(&dict, op);
 
        dbus_message_iter_close_container(iter, &dict);
+#endif
 }
 
 static struct pending_op *send_read(struct btd_device *device,
@@ -1668,12 +2023,25 @@ static void write_setup_cb(DBusMessageIter *iter, void *user_data)
 {
        struct pending_op *op = user_data;
        DBusMessageIter array, dict;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char dst_addr[18] = { 0 };
+       char *addr_value = dst_addr;
+       uint16_t offset = 0;
+       gboolean response_needed = TRUE;
+       const bdaddr_t *bdaddr = device_get_address(op->device);
+
+       ba2str(bdaddr, dst_addr);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &addr_value);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &op->id);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &offset);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &response_needed);
+#endif
        dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
        dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
                                        &op->data.iov_base, op->data.iov_len);
        dbus_message_iter_close_container(iter, &array);
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
                                        DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
                                        DBUS_TYPE_STRING_AS_STRING
@@ -1684,6 +2052,7 @@ static void write_setup_cb(DBusMessageIter *iter, void *user_data)
        append_options(&dict, op);
 
        dbus_message_iter_close_container(iter, &dict);
+#endif
 
        if (!op->owner_queue) {
                gatt_db_attribute_write_result(op->attrib, op->id, 0);
@@ -1826,7 +2195,87 @@ static void property_changed_cb(GDBusProxy *proxy, const char *name,
        DBusMessageIter array;
        uint8_t *value = NULL;
        int len = 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       bool enable = FALSE;
+       const bdaddr_t *unicast_addr = NULL;
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (strcmp(name, "Value") == 0) {
+               if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+                       DBG("Malformed \"Value\" property received");
+                       return;
+               }
+
+               dbus_message_iter_recurse(iter, &array);
+               dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+               if (len < 0) {
+                       DBG("Malformed \"Value\" property received");
+                       return;
+               }
+
+               /* Truncate the value if it's too large */
+               len = MIN(BT_ATT_MAX_VALUE_LEN, len);
+               value = len ? value : NULL;
+       } else if (strcmp(name, "Notifying") == 0) {
+               gboolean notify_indicate = FALSE;
+
+               if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) {
+                       DBG("Malformed \"Notifying\" property received");
+                       return;
+               }
+
+               dbus_message_iter_get_basic(iter, &notify_indicate);
+
+               DBG("Set Notification %d", notify_indicate);
+               /* Set notification/indication */
+               set_ccc_notify_indicate(chrc->ccc, notify_indicate);
+               return;
+       } else if (strcmp(name, "Unicast") == 0) {
+               const char *address = NULL;
+               if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+                       DBG("Malformed \"Value\" property received");
+                       return;
+               }
+
+               dbus_message_iter_get_basic(iter, &address);
 
+               if (address) {
+                       /* Set the address for unicast notification/indication */
+                       set_ccc_unicast_address(chrc->ccc, address);
+               }
+               return;
+       } else
+               return;
+
+       enable = get_ccc_notify_indicate(chrc->ccc);
+
+       if (enable) {
+               unicast_addr = get_ccc_unicast_address(chrc->ccc);
+
+               if (unicast_addr && bacmp(unicast_addr, BDADDR_ANY)) {
+                       send_unicast_notification_indication_to_device(proxy,
+                                       chrc->service->app->database,
+                                       gatt_db_attribute_get_handle(chrc->attrib),
+                                       value, len,
+                                       gatt_db_attribute_get_handle(chrc->ccc),
+                                       chrc->props & BT_GATT_CHRC_PROP_INDICATE,
+                                       unicast_addr);
+                       /* reset the unicast address */
+                       set_ccc_unicast_address(chrc->ccc, NULL);
+               } else {
+                       send_notification_indication_to_devices(proxy,
+                                       chrc->service->app->database,
+                                       gatt_db_attribute_get_handle(chrc->attrib),
+                                       value, len,
+                                       gatt_db_attribute_get_handle(chrc->ccc),
+                                       chrc->props & BT_GATT_CHRC_PROP_INDICATE);
+               }
+
+               set_ccc_notify_indicate(chrc->ccc, FALSE);
+       }
+#else
        if (strcmp(name, "Value"))
                return;
 
@@ -1852,6 +2301,7 @@ static void property_changed_cb(GDBusProxy *proxy, const char *name,
                                value, len,
                                gatt_db_attribute_get_handle(chrc->ccc),
                                chrc->props & BT_GATT_CHRC_PROP_INDICATE);
+#endif
 }
 
 static bool database_add_ccc(struct external_service *service,
@@ -2096,6 +2546,24 @@ fail:
                                                                NULL, 0);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static bool database_check_ccc_desc(struct external_desc *desc)
+{
+       bt_uuid_t uuid, uuid_ccc;
+
+       if (!parse_uuid(desc->proxy, &uuid)) {
+               error("Failed to read \"UUID\" property of descriptor");
+               return false;
+       }
+
+       bt_uuid16_create(&uuid_ccc, GATT_CLIENT_CHARAC_CFG_UUID);
+       if (bt_uuid_cmp(&uuid, &uuid_ccc) == 0)
+               return true;
+       else
+               return false;
+}
+#endif
+
 static bool database_add_chrc(struct external_service *service,
                                                struct external_chrc *chrc)
 {
@@ -2121,8 +2589,13 @@ static bool database_add_chrc(struct external_service *service,
                return false;
        }
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* Existing implementation adds CCC descriptor by default
+         * if notification and indication properties are set. But as per the requirment
+         * CCCD shall be added by the application */
        if (!database_add_ccc(service, chrc))
                return false;
+#endif
 
        if (!database_add_cep(service, chrc))
                return false;
@@ -2135,11 +2608,28 @@ static bool database_add_chrc(struct external_service *service,
                if (desc->handled || g_strcmp0(desc->chrc_path, chrc->path))
                        continue;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               /* Check if Application wants to add CCC and use existing
+                * implemenation to add CCC descriptors */
+               if (database_check_ccc_desc(desc)) {
+                       if (!database_add_ccc(service, chrc)) {
+                               chrc->attrib = NULL;
+                               return false;
+                       }
+                       desc->attrib = chrc->ccc;
+                       desc->handled = true;
+               } else if (!database_add_desc(service, desc)) {
+                       chrc->attrib = NULL;
+                       error("Failed to create descriptor entry");
+                               return false;
+               }
+#else
                if (!database_add_desc(service, desc)) {
                        chrc->attrib = NULL;
                        error("Failed to create descriptor entry");
                        return false;
                }
+#endif
        }
 
        return true;
index 0b785ee..6449879 100755 (executable)
@@ -41,6 +41,9 @@ struct main_opts {
        gboolean        name_resolv;
        gboolean        debug_keys;
        gboolean        fast_conn;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       gboolean        le_privacy;
+#endif
 
        uint16_t        did_source;
        uint16_t        did_vendor;
index d2a20de..8a5057e 100755 (executable)
--- a/src/log.c
+++ b/src/log.c
@@ -302,6 +302,22 @@ void __btd_toggle_debug(void)
                desc->flags |= BTD_DEBUG_FLAG_PRINT;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void __hci_attach_log_init(void)
+{
+       int option = LOG_NDELAY | LOG_PID;
+
+       enabled = g_strsplit_set(g_strdup("*"), ":, ", 0);
+
+       __btd_enable_debug(__start___debug, __stop___debug);
+
+       openlog("hciattach", option, LOG_DAEMON);
+
+       syslog(LOG_INFO, "hciattach daemon for debugging");
+}
+#endif
+
+
 void __btd_log_init(const char *debug, int detach)
 {
        int option = LOG_NDELAY | LOG_PID;
index 0d243ce..1299cdd 100755 (executable)
--- a/src/log.h
+++ b/src/log.h
@@ -39,6 +39,9 @@ void btd_info(uint16_t index, const char *format, ...)
 void btd_debug(uint16_t index, const char *format, ...)
                                        __attribute__((format(printf, 2, 3)));
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void __hci_attach_log_init(void );
+#endif
 void __btd_log_init(const char *debug, int detach);
 void __btd_log_cleanup(void);
 void __btd_toggle_debug(void);
index bcc1e6f..44110c5 100755 (executable)
@@ -90,6 +90,9 @@ static const char * const supported_options[] = {
        "ControllerMode",
        "MultiProfile",
        "Privacy",
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       "EnableLEPrivacy",
+#endif
 };
 
 GKeyFile *btd_get_main_conf(void)
@@ -357,6 +360,14 @@ static void parse_config(GKeyFile *config)
                g_clear_error(&err);
        else
                main_opts.fast_conn = boolean;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       boolean = g_key_file_get_boolean(config, "General",
+                                               "EnableLEPrivacy", &err);
+       if (err)
+               g_clear_error(&err);
+       else
+               main_opts.le_privacy = boolean;
+#endif
 }
 
 static void init_defaults(void)
@@ -372,14 +383,18 @@ static void init_defaults(void)
        main_opts.reverse_sdp = TRUE;
        main_opts.name_resolv = TRUE;
        main_opts.debug_keys = FALSE;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       main_opts.le_privacy = FALSE;
+#endif
 
        if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2)
                return;
-
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        main_opts.did_source = 0x0002;          /* USB */
        main_opts.did_vendor = 0x1d6b;          /* Linux Foundation */
        main_opts.did_product = 0x0246;         /* BlueZ */
        main_opts.did_version = (major << 8 | minor);
+#endif
 }
 
 static void log_handler(const gchar *log_domain, GLogLevelFlags log_level,
diff --git a/src/main_hive.conf b/src/main_hive.conf
new file mode 100755 (executable)
index 0000000..f521092
--- /dev/null
@@ -0,0 +1,87 @@
+[General]
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+# Defaults to 'BlueZ'
+#Name = %h-%d
+
+# Default device class. Only the major and minor device class bits are
+# considered. Defaults to '0x000000'.
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+Class = 0x40414                # HIVE
+#else
+#Class = 0x000100
+#endif
+
+# How long to stay in discoverable mode before going back to non-discoverable
+# The value is in seconds. Default is 180, i.e. 3 minutes.
+# 0 = disable timer, i.e. stay discoverable forever
+#DiscoverableTimeout = 0
+
+# How long to stay in pairable mode before going back to non-discoverable
+# The value is in seconds. Default is 0.
+# 0 = disable timer, i.e. stay pairable forever
+#PairableTimeout = 0
+
+# Automatic connection for bonded devices driven by platform/user events.
+# If a platform plugin uses this mechanism, automatic connections will be
+# enabled during the interval defined below. Initially, this feature
+# intends to be used to establish connections to ATT channels. Default is 60.
+#AutoConnectTimeout = 60
+
+# Use vendor id source (assigner), vendor, product and version information for
+# DID profile support. The values are separated by ":" and assigner, VID, PID
+# and version.
+# Possible vendor id source values: bluetooth, usb (defaults to usb)
+#DeviceID = bluetooth:1234:5678:abcd
+
+# Do reverse service discovery for previously unknown devices that connect to
+# us. This option is really only needed for qualification since the BITE tester
+# doesn't like us doing reverse SDP for some test cases (though there could in
+# theory be other useful purposes for this too). Defaults to 'true'.
+#ReverseServiceDiscovery = true
+
+# Enable name resolving after inquiry. Set it to 'false' if you don't need
+# remote devices name and want shorter discovery cycle. Defaults to 'true'.
+#NameResolving = true
+
+# Enable runtime persistency of debug link keys. Default is false which
+# makes debug link keys valid only for the duration of the connection
+# that they were created for.
+#DebugKeys = false
+
+# Restricts all controllers to the specified transport. Default value
+# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW).
+# Possible values: "dual", "bredr", "le"
+#ControllerMode = dual
+
+# Enables Multi Profile Specification support. This allows to specify if
+# system supports only Multiple Profiles Single Device (MPSD) configuration
+# or both Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple
+# Devices (MPMD) configurations.
+# Possible values: "off", "single", "multiple"
+#MultiProfile = off
+
+# Permanently enables the Fast Connectable setting for adapters that
+# support it. When enabled other devices can connect faster to us,
+# however the tradeoff is increased power consumptions. This feature
+# will fully work only on kernel version 4.1 and newer. Defaults to
+# 'false'.
+#FastConnectable = false
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled
+# otherwise the feature is disabled by default for the local device.
+EnableLEPrivacy = true
+#endif
+
+#[Policy]
+#
+# The ReconnectUUIDs defines the set of remote services that should try
+# to be reconnected to in case of a link loss (link supervision
+# timeout). The policy plugin should contain a sane set of values by
+# default, but this list can be overridden here. By setting the list to
+# empty the reconnection feature gets disabled.
+#ReconnectUUIDs=
+
diff --git a/src/main_ivi.conf b/src/main_ivi.conf
new file mode 100755 (executable)
index 0000000..4e7b1f2
--- /dev/null
@@ -0,0 +1,87 @@
+[General]
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+# Defaults to 'BlueZ'
+#Name = %h-%d
+
+# Default device class. Only the major and minor device class bits are
+# considered. Defaults to '0x000000'.
+#ifdef __TIZEN_PATCH__
+Class = 0x200428               # IVI device
+#else
+#Class = 0x000100
+#endif
+
+# How long to stay in discoverable mode before going back to non-discoverable
+# The value is in seconds. Default is 180, i.e. 3 minutes.
+# 0 = disable timer, i.e. stay discoverable forever
+#DiscoverableTimeout = 0
+
+# How long to stay in pairable mode before going back to non-discoverable
+# The value is in seconds. Default is 0.
+# 0 = disable timer, i.e. stay pairable forever
+#PairableTimeout = 0
+
+# Automatic connection for bonded devices driven by platform/user events.
+# If a platform plugin uses this mechanism, automatic connections will be
+# enabled during the interval defined below. Initially, this feature
+# intends to be used to establish connections to ATT channels. Default is 60.
+#AutoConnectTimeout = 60
+
+# Use vendor id source (assigner), vendor, product and version information for
+# DID profile support. The values are separated by ":" and assigner, VID, PID
+# and version.
+# Possible vendor id source values: bluetooth, usb (defaults to usb)
+#DeviceID = bluetooth:1234:5678:abcd
+
+# Do reverse service discovery for previously unknown devices that connect to
+# us. This option is really only needed for qualification since the BITE tester
+# doesn't like us doing reverse SDP for some test cases (though there could in
+# theory be other useful purposes for this too). Defaults to 'true'.
+#ReverseServiceDiscovery = true
+
+# Enable name resolving after inquiry. Set it to 'false' if you don't need
+# remote devices name and want shorter discovery cycle. Defaults to 'true'.
+#NameResolving = true
+
+# Enable runtime persistency of debug link keys. Default is false which
+# makes debug link keys valid only for the duration of the connection
+# that they were created for.
+#DebugKeys = false
+
+# Restricts all controllers to the specified transport. Default value
+# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW).
+# Possible values: "dual", "bredr", "le"
+#ControllerMode = dual
+
+# Enables Multi Profile Specification support. This allows to specify if
+# system supports only Multiple Profiles Single Device (MPSD) configuration
+# or both Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple
+# Devices (MPMD) configurations.
+# Possible values: "off", "single", "multiple"
+#MultiProfile = off
+
+# Permanently enables the Fast Connectable setting for adapters that
+# support it. When enabled other devices can connect faster to us,
+# however the tradeoff is increased power consumptions. This feature
+# will fully work only on kernel version 4.1 and newer. Defaults to
+# 'false'.
+#FastConnectable = false
+
+#ifdef __TIZEN_PATCH__
+# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled
+# otherwise the feature is disabled by default for the local device.
+EnableLEPrivacy = true
+#endif
+
+#[Policy]
+#
+# The ReconnectUUIDs defines the set of remote services that should try
+# to be reconnected to in case of a link loss (link supervision
+# timeout). The policy plugin should contain a sane set of values by
+# default, but this list can be overridden here. By setting the list to
+# empty the reconnection feature gets disabled.
+#ReconnectUUIDs=
+
diff --git a/src/main_m.conf b/src/main_m.conf
new file mode 100755 (executable)
index 0000000..bb431e4
--- /dev/null
@@ -0,0 +1,87 @@
+[General]
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+# Defaults to 'BlueZ'
+#Name = %h-%d
+
+# Default device class. Only the major and minor device class bits are
+# considered. Defaults to '0x000000'.
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+Class = 0x00020C        # Smart phone
+#else
+#Class = 0x000100
+#endif
+
+# How long to stay in discoverable mode before going back to non-discoverable
+# The value is in seconds. Default is 180, i.e. 3 minutes.
+# 0 = disable timer, i.e. stay discoverable forever
+#DiscoverableTimeout = 0
+
+# How long to stay in pairable mode before going back to non-discoverable
+# The value is in seconds. Default is 0.
+# 0 = disable timer, i.e. stay pairable forever
+#PairableTimeout = 0
+
+# Automatic connection for bonded devices driven by platform/user events.
+# If a platform plugin uses this mechanism, automatic connections will be
+# enabled during the interval defined below. Initially, this feature
+# intends to be used to establish connections to ATT channels. Default is 60.
+#AutoConnectTimeout = 60
+
+# Use vendor id source (assigner), vendor, product and version information for
+# DID profile support. The values are separated by ":" and assigner, VID, PID
+# and version.
+# Possible vendor id source values: bluetooth, usb (defaults to usb)
+#DeviceID = bluetooth:1234:5678:abcd
+
+# Do reverse service discovery for previously unknown devices that connect to
+# us. This option is really only needed for qualification since the BITE tester
+# doesn't like us doing reverse SDP for some test cases (though there could in
+# theory be other useful purposes for this too). Defaults to 'true'.
+#ReverseServiceDiscovery = true
+
+# Enable name resolving after inquiry. Set it to 'false' if you don't need
+# remote devices name and want shorter discovery cycle. Defaults to 'true'.
+#NameResolving = true
+
+# Enable runtime persistency of debug link keys. Default is false which
+# makes debug link keys valid only for the duration of the connection
+# that they were created for.
+#DebugKeys = false
+
+# Restricts all controllers to the specified transport. Default value
+# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW).
+# Possible values: "dual", "bredr", "le"
+#ControllerMode = dual
+
+# Enables Multi Profile Specification support. This allows to specify if
+# system supports only Multiple Profiles Single Device (MPSD) configuration
+# or both Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple
+# Devices (MPMD) configurations.
+# Possible values: "off", "single", "multiple"
+#MultiProfile = off
+
+# Permanently enables the Fast Connectable setting for adapters that
+# support it. When enabled other devices can connect faster to us,
+# however the tradeoff is increased power consumptions. This feature
+# will fully work only on kernel version 4.1 and newer. Defaults to
+# 'false'.
+#FastConnectable = false
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled
+# otherwise the feature is disabled by default for the local device.
+EnableLEPrivacy = false
+#endif
+
+#[Policy]
+#
+# The ReconnectUUIDs defines the set of remote services that should try
+# to be reconnected to in case of a link loss (link supervision
+# timeout). The policy plugin should contain a sane set of values by
+# default, but this list can be overridden here. By setting the list to
+# empty the reconnection feature gets disabled.
+#ReconnectUUIDs=
+
diff --git a/src/main_w.conf b/src/main_w.conf
new file mode 100755 (executable)
index 0000000..822be5e
--- /dev/null
@@ -0,0 +1,84 @@
+[General]
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+# Defaults to 'BlueZ'
+#Name = %h-%d
+
+# Default device class. Only the major and minor device class bits are
+# considered. Defaults to '0x000000'.
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+Class = 0x000704        # Wearable, Wrist Watch
+#else
+#Class = 0x000100
+#endif
+
+# How long to stay in discoverable mode before going back to non-discoverable
+# The value is in seconds. Default is 180, i.e. 3 minutes.
+# 0 = disable timer, i.e. stay discoverable forever
+#DiscoverableTimeout = 0
+
+# How long to stay in pairable mode before going back to non-discoverable
+# The value is in seconds. Default is 0.
+# 0 = disable timer, i.e. stay pairable forever
+#PairableTimeout = 0
+
+# Automatic connection for bonded devices driven by platform/user events.
+# If a platform plugin uses this mechanism, automatic connections will be
+# enabled during the interval defined below. Initially, this feature
+# intends to be used to establish connections to ATT channels. Default is 60.
+#AutoConnectTimeout = 60
+
+# Use vendor id source (assigner), vendor, product and version information for
+# DID profile support. The values are separated by ":" and assigner, VID, PID
+# and version.
+# Possible vendor id source values: bluetooth, usb (defaults to usb)
+#DeviceID = bluetooth:1234:5678:abcd
+
+# Do reverse service discovery for previously unknown devices that connect to
+# us. This option is really only needed for qualification since the BITE tester
+# doesn't like us doing reverse SDP for some test cases (though there could in
+# theory be other useful purposes for this too). Defaults to 'true'.
+#ReverseServiceDiscovery = true
+
+# Enable name resolving after inquiry. Set it to 'false' if you don't need
+# remote devices name and want shorter discovery cycle. Defaults to 'true'.
+#NameResolving = true
+
+# Enable runtime persistency of debug link keys. Default is false which
+# makes debug link keys valid only for the duration of the connection
+# that they were created for.
+#DebugKeys = false
+
+# Restricts all controllers to the specified transport. Default value
+# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW).
+# Possible values: "dual", "bredr", "le"
+#ControllerMode = dual
+
+# Permanently enables the Fast Connectable setting for adapters that
+# support it. When enabled other devices can connect faster to us,
+# however the tradeoff is increased power consumptions. This feature
+# will fully work only on kernel version 4.1 and newer. Defaults to
+# 'false'.
+#FastConnectable = false
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled
+# otherwise the feature is disabled by default for the local device.
+EnableLEPrivacy = false
+#endif
+
+[Policy]
+#
+# The ReconnectUUIDs defines the set of remote services that should try
+# to be reconnected to in case of a link loss (link supervision
+# timeout). The policy plugin should contain a sane set of values by
+# default, but this list can be overridden here. By setting the list to
+# empty the reconnection feature gets disabled.
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ReconnectUUIDs=
+#else
+#ReconnectUUIDs=
+#endif
+
diff --git a/src/oob.c b/src/oob.c
new file mode 100755 (executable)
index 0000000..708467b
--- /dev/null
+++ b/src/oob.c
@@ -0,0 +1,46 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  ST-Ericsson SA
+ *
+ *  Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ *  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
+ *
+ */
+
+#include "adapter.h"
+#include "oob.h"
+
+static oob_read_cb_t local_oob_read_cb = NULL;
+
+void oob_register_cb(oob_read_cb_t cb)
+{
+       local_oob_read_cb = cb;
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+                                                       uint8_t *randomizer, void *user_data)
+#else
+void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+                                                       uint8_t *randomizer)
+#endif
+{
+       if (local_oob_read_cb)
+               local_oob_read_cb(adapter, hash, randomizer);
+}
diff --git a/src/oob.h b/src/oob.h
new file mode 100755 (executable)
index 0000000..d720315
--- /dev/null
+++ b/src/oob.h
@@ -0,0 +1,36 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  ST-Ericsson SA
+ *
+ *  Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ *  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
+ *
+ */
+
+typedef void (*oob_read_cb_t) (struct btd_adapter *adapter, uint8_t *hash,
+                                                       uint8_t *randomizer);
+
+void oob_register_cb(oob_read_cb_t cb);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+                                                       uint8_t *randomizer, void *user_data);
+#else
+void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+                                                       uint8_t *randomizer);
+#endif
index 7c5318c..bb833bd 100755 (executable)
 #define BTD_PROFILE_PSM_AUTO   -1
 #define BTD_PROFILE_CHAN_AUTO  -1
 
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+#define HID_DEVICE_INTR_PSM 17
+#define HID_DEVICE_CTRL_PSM 19
+#endif
+
 #define HFP_HF_RECORD                                                  \
        "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>                    \
        <record>                                                        \
                </attribute>                                            \
        </record>"
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define SPP_RECORD                                                     \
+       "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>                    \
+       <record>                                                        \
+               <attribute id=\"0x0001\">                               \
+                       <sequence>                                      \
+                               %s                                      \
+                       </sequence>                                     \
+               </attribute>                                            \
+               <attribute id=\"0x0004\">                               \
+                       <sequence>                                      \
+                               <sequence>                              \
+                                       <uuid value=\"0x0100\" />       \
+                               </sequence>                             \
+                               <sequence>                              \
+                                       <uuid value=\"0x0003\" />       \
+                                       <uint8 value=\"0x%02x\" />      \
+                               </sequence>                             \
+                       </sequence>                                     \
+               </attribute>                                            \
+               <attribute id=\"0x0005\">                               \
+                       <sequence>                                      \
+                               <uuid value=\"0x1002\" />               \
+                       </sequence>                                     \
+               </attribute>                                            \
+               %s                                                              \
+               <attribute id=\"0x0009\">                               \
+                       <sequence>                                      \
+                               <sequence>                              \
+                                       <uuid value=\"0x1101\" />       \
+                                       <uint16 value=\"0x%04x\" />     \
+                               </sequence>                             \
+                       </sequence>                                     \
+               </attribute>                                            \
+               <attribute id=\"0x0100\">                               \
+                       <text value=\"%s\" />                           \
+               </attribute>                                            \
+       </record>"
+
+#define LANG_SEQ                                                               \
+                       "<attribute id=\"0x0006\">                              \
+                               <sequence>                                      \
+                                       <uint16 value=\"0x%04x\" />     \
+                                       <uint16 value=\"0x%04x\" />     \
+                                       <uint16 value=\"0x%04x\" />     \
+                               </sequence>                             \
+                       </attribute>"
+#else
 #define SPP_RECORD                                                     \
        "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>                    \
        <record>                                                        \
                        <text value=\"%s\" />                           \
                </attribute>                                            \
        </record>"
+#endif
 
 #define DUN_RECORD                                                     \
        "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>                    \
                </attribute>                                            \
        </record>"
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define OPP_RECORD                                                     \
+       "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>                    \
+       <record>                                                        \
+               <attribute id=\"0x0001\">                               \
+                       <sequence>                                      \
+                               <uuid value=\"0x1105\" />               \
+                       </sequence>                                     \
+               </attribute>                                            \
+               <attribute id=\"0x0004\">                               \
+                       <sequence>                                      \
+                               <sequence>                              \
+                                       <uuid value=\"0x0100\" />       \
+                               </sequence>                             \
+                               <sequence>                              \
+                                       <uuid value=\"0x0003\" />       \
+                                       <uint8 value=\"0x%02x\" />      \
+                               </sequence>                             \
+                               <sequence>                              \
+                                       <uuid value=\"0x0008\"/>        \
+                               </sequence>                             \
+                       </sequence>                                     \
+               </attribute>                                            \
+               <attribute id=\"0x0005\">                               \
+                       <sequence>                                      \
+                               <uuid value=\"0x1002\" />               \
+                       </sequence>                                     \
+               </attribute>                                            \
+               <attribute id=\"0x0009\">                               \
+                       <sequence>                                      \
+                               <sequence>                              \
+                                       <uuid value=\"0x1105\" />       \
+                                       <uint16 value=\"0x%04x\" />     \
+                               </sequence>                             \
+                       </sequence>                                     \
+               </attribute>                                            \
+               <attribute id=\"0x0303\">                               \
+                       <sequence>                                      \
+                               <uint8 value=\"0x01\"/>                 \
+                               <uint8 value=\"0x02\"/>                 \
+                               <uint8 value=\"0x03\"/>                 \
+                               <uint8 value=\"0x04\"/>                 \
+                               <uint8 value=\"0x05\"/>                 \
+                               <uint8 value=\"0x06\"/>                 \
+                               <uint8 value=\"0xff\"/>                 \
+                       </sequence>                                     \
+               </attribute>                                            \
+               <attribute id=\"0x0100\">                               \
+                       <text value=\"%s\" />                           \
+               </attribute>                                            \
+       </record>"
+#else
 #define OPP_RECORD                                                     \
        "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>                    \
        <record>                                                        \
                        <text value=\"%s\" />                           \
                </attribute>                                            \
        </record>"
+#endif
 
 #define FTP_RECORD                                                     \
        "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>                    \
                </attribute>                                            \
        </record>"
 
+#ifdef TIZEN_FEATURE_BLUEZ_PBAP_SIM
+#define PBAP_ACCESS "0x03"     /* Phone and SIM access support*/
+#else
+#define PBAP_ACCESS "0x01"     /* Phone access support only*/
+#endif
+
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define PSE_RECORD                                                     \
+       "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>                    \
+       <record>                                                        \
+               <attribute id=\"0x0001\">                               \
+                       <sequence>                                      \
+                               <uuid value=\"0x112f\" />               \
+                       </sequence>                                     \
+               </attribute>                                            \
+               <attribute id=\"0x0004\">                               \
+                       <sequence>                                      \
+                               <sequence>                              \
+                                       <uuid value=\"0x0100\" />       \
+                               </sequence>                             \
+                               <sequence>                              \
+                                       <uuid value=\"0x0003\" />       \
+                                       <uint8 value=\"0x%02x\" />      \
+                               </sequence>                             \
+                               <sequence>                              \
+                                       <uuid value=\"0x0008\"/>        \
+                               </sequence>                             \
+                       </sequence>                                     \
+               </attribute>                                            \
+               <attribute id=\"0x0005\">                               \
+                       <sequence>                                      \
+                               <uuid value=\"0x1002\" />               \
+                       </sequence>                                     \
+               </attribute>                                            \
+               <attribute id=\"0x0009\">                               \
+                       <sequence>                                      \
+                               <sequence>                              \
+                                       <uuid value=\"0x1130\" />       \
+                                       <uint16 value=\"0x%04x\" />     \
+                               </sequence>                             \
+                       </sequence>                                     \
+               </attribute>                                            \
+               <attribute id=\"0x0100\">                               \
+                       <text value=\"%s\" />                           \
+               </attribute>                                            \
+               <attribute id=\"0x0314\">                               \
+                       <uint8 value=\""PBAP_ACCESS"\"/>                                \
+               </attribute>                                            \
+       </record>"
+#else
 #define PSE_RECORD                                                     \
        "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>                    \
        <record>                                                        \
                        <uint16 value=\"%u\" name=\"psm\"/>             \
                </attribute>                                            \
        </record>"
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define SUPPORTED_MESSAGE_TYPES "0x03" /* EMAIL and SMS_GSM */
+#else
+#define SUPPORTED_MESSAGE_TYPES "0x0F" /* EMAIL, SMS_GSM, SMS_CDMA and MMS */
+#endif
 
 #define MAS_RECORD                                                     \
        "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>                    \
                </attribute>                                            \
        </record>"
 
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+#define HID_DEVICE_RECORD      \
+       "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>    \
+       <record>        \
+       <attribute id=\"0x0001\">       \
+               <sequence>      \
+                       <uuid value=\"0x1124\" />       \
+               </sequence>     \
+       </attribute>    \
+       <attribute id=\"0x0004\">       \
+               <sequence>      \
+                       <sequence>      \
+                               <uuid value=\"0x0100\" />       \
+                               <uint16 value=\"0x0011\" />     \
+                       </sequence>     \
+                       <sequence>      \
+                               <uuid value=\"0x0011\" />       \
+                       </sequence>     \
+               </sequence>     \
+       </attribute>    \
+       <attribute id=\"0x0005\">       \
+               <sequence>      \
+                       <uuid value=\"0x1002\" />       \
+               </sequence>     \
+       </attribute>    \
+       <attribute id=\"0x0006\">       \
+               <sequence>      \
+                       <uint16 value=\"0x656e\" />     \
+                       <uint16 value=\"0x006a\" />     \
+                       <uint16 value=\"0x0100\" />     \
+               </sequence>     \
+       </attribute>    \
+       <attribute id=\"0x0009\">       \
+               <sequence>      \
+                       <sequence>      \
+                               <uuid value=\"0x0011\" />       \
+                               <uint16 value=\"0x0100\" />     \
+                       </sequence>     \
+               </sequence>     \
+       </attribute>    \
+       <attribute id=\"0x000d\">       \
+               <sequence>      \
+                       <sequence>      \
+                               <sequence>      \
+                                       <uuid value=\"0x0100\" />       \
+                                       <uint16 value=\"0x0013\" />     \
+                               </sequence>     \
+                               <sequence>      \
+                                       <uuid value=\"0x0011\" />       \
+                               </sequence>     \
+                       </sequence>     \
+               </sequence>     \
+       </attribute>    \
+       <attribute id=\"0x0100\">       \
+               <text value=\"Bluez Mouse\" />  \
+       </attribute>    \
+       <attribute id=\"0x0101\">       \
+               <text value=\"Mouse\" />        \
+       </attribute>    \
+       <attribute id=\"0x0200\">       \
+               <uint16 value=\"0x0100\" />     \
+       </attribute>    \
+       <attribute id=\"0x0201\">       \
+               <uint16 value=\"0x0111\" />     \
+       </attribute>    \
+       <attribute id=\"0x0202\">       \
+               <uint8 value=\"0x40\" />                \
+       </attribute>    \
+       <attribute id=\"0x0203\">       \
+               <uint8 value=\"0x00\" />                \
+       </attribute>    \
+       <attribute id=\"0x0204\">       \
+               <boolean value=\"true\" />      \
+       </attribute>    \
+       <attribute id=\"0x0205\">       \
+               <boolean value=\"true\" />      \
+       </attribute>    \
+       <attribute id=\"0x0206\">       \
+               <sequence>      \
+                       <sequence>      \
+                               <uint8 value=\"0x22\" />                \
+                               <text encoding=\"hex\" value=\"05010902a10185010901a100050919012903150025017501950381027505950181010501093009311581257f750895028106a10285010938950175081581257f8106c0c0c005010906a1018502a100050719e029e71500250175019508810295087508150025650507190029658100c0c005010905A10185030901A1000930093109330934150026FF00350046FF0075089504810209397504950115002507463B016614008142750195048103050919012910750195108102C0C0\" />      \
+                       </sequence>     \
+               </sequence>     \
+       </attribute>    \
+       <attribute id=\"0x0207\">       \
+               <sequence>      \
+                       <sequence>      \
+                               <uint16 value=\"0x0409\" />     \
+                               <uint16 value=\"0x0100\" />     \
+                       </sequence>     \
+               </sequence>     \
+       </attribute>    \
+       <attribute id=\"0x020b\">       \
+               <uint16 value=\"0x0100\" />     \
+       </attribute>    \
+       <attribute id=\"0x020e\">       \
+               <boolean value=\"true\" />      \
+       </attribute>    \
+       </record>"
+#endif
+
+
 struct ext_io;
 
 struct ext_profile {
@@ -652,6 +920,10 @@ struct ext_profile {
        GSList *conns;
 
        GSList *connects;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char *destination;
+       char *app_path;
+#endif
 };
 
 struct ext_io {
@@ -695,6 +967,11 @@ static GSList *custom_props = NULL;
 static GSList *profiles = NULL;
 static GSList *ext_profiles = NULL;
 
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static int connect_io(struct ext_io *conn, const bdaddr_t *src,
+                                                       const bdaddr_t *dst);
+#endif
+
 void btd_profile_foreach(void (*func)(struct btd_profile *p, void *data),
                                                                void *data)
 {
@@ -1174,6 +1451,23 @@ static void ext_confirm(GIOChannel *io, gpointer user_data)
 
        DBG("incoming connect from %s", addr);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+{
+       struct btd_device *device;
+       gboolean restricted = FALSE;
+
+       device = btd_adapter_find_device(adapter_find(&src), &dst,
+                                                               BDADDR_BREDR);
+       if (device) {
+               restricted = device_is_profile_restricted(device, HFP_HS_UUID);
+               if (restricted) {
+                       DBG("HFP_HS is restricted");
+                       return;
+               }
+       }
+}
+#endif
+
        conn = create_conn(server, io, &src, &dst);
        if (conn == NULL)
                return;
@@ -1368,6 +1662,21 @@ static struct ext_profile *find_ext(struct btd_profile *p)
        return l->data;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+gboolean ext_profile_is_registered_as_client_role(struct btd_profile *p)
+{
+       struct ext_profile *ext = find_ext(p);
+       if (ext && ext->role) {
+               if(strcasecmp(ext->role, "client") == 0) {
+                       return TRUE;
+               } else {
+                       return FALSE;
+               }
+       }
+       return FALSE;
+}
+#endif
+
 static int ext_adapter_probe(struct btd_profile *p,
                                                struct btd_adapter *adapter)
 {
@@ -1569,6 +1878,38 @@ static void record_cb(sdp_list_t *recs, int err, gpointer user_data)
                sdp_record_t *rec = r->data;
                sdp_list_t *protos;
                int port;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               char *profile_uuid;
+               sdp_list_t *svcclass = NULL;
+
+               if (sdp_get_service_classes(rec, &svcclass) < 0)
+                       continue;
+
+               /* Check for empty service classes list */
+               if (svcclass == NULL) {
+                       DBG("Skipping record with no service classes");
+                       continue;
+               }
+
+               /* Extract the first element and skip the remainning */
+               profile_uuid = bt_uuid2string(svcclass->data);
+               if (!profile_uuid) {
+                       sdp_list_free(svcclass, free);
+                       continue;
+               }
+
+               sdp_list_free(svcclass, free);
+
+               DBG("profile uuid %s port uuid %s", profile_uuid, ext->remote_uuid);
+
+               if (g_ascii_strncasecmp(profile_uuid, ext->remote_uuid,
+                                               strlen(profile_uuid)) != 0) {
+                       free(profile_uuid);
+                       continue;
+               }
+
+               free(profile_uuid);
+#endif
 
                if (sdp_get_access_protos(rec, &protos) < 0) {
                        error("Unable to get proto list from %s record",
@@ -1771,14 +2112,28 @@ static char *get_spp_record(struct ext_profile *ext, struct ext_io *l2cap,
                                                        struct ext_io *rfcomm)
 {
        char *svc, *rec;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char *lan_seq;
+       uint16_t code_ISO639 = (0x65 << 8) | 0x6e;
+       uint16_t encoding = 106;
+       uint16_t base_offset = SDP_PRIMARY_LANG_BASE;
+#endif
 
        if (ext->service)
                svc = g_strdup_printf("<uuid value=\"%s\" />", ext->service);
        else
                svc = g_strdup("");
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       lan_seq = g_strdup_printf(LANG_SEQ, code_ISO639, encoding,
+                                                       base_offset);
+       rec = g_strdup_printf(SPP_RECORD, svc, rfcomm->chan, lan_seq, ext->version,
+                                                               ext->name);
+       g_free(lan_seq);
+#else
        rec = g_strdup_printf(SPP_RECORD, svc, rfcomm->chan, ext->version,
                                                                ext->name);
+#endif
        g_free(svc);
        return rec;
 }
@@ -1799,6 +2154,14 @@ static char *get_pce_record(struct ext_profile *ext, struct ext_io *l2cap,
 static char *get_pse_record(struct ext_profile *ext, struct ext_io *l2cap,
                                                        struct ext_io *rfcomm)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint8_t chan = 0;
+
+       if (rfcomm)
+               chan = rfcomm->chan;
+
+       return g_strdup_printf(PSE_RECORD, chan, ext->version, ext->name);
+#else
        uint16_t psm = 0;
        uint8_t chan = 0;
 
@@ -1808,6 +2171,7 @@ static char *get_pse_record(struct ext_profile *ext, struct ext_io *l2cap,
                chan = rfcomm->chan;
 
        return g_strdup_printf(PSE_RECORD, chan, ext->version, ext->name, psm);
+#endif
 }
 
 static char *get_mas_record(struct ext_profile *ext, struct ext_io *l2cap,
@@ -1845,18 +2209,34 @@ static char *get_sync_record(struct ext_profile *ext, struct ext_io *l2cap,
                                                                ext->name);
 }
 
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static char *get_hid_device_record(struct ext_profile *ext, struct ext_io *l2cap,
+                                                       struct ext_io *rfcomm)
+{
+       return g_strdup(HID_DEVICE_RECORD);
+}
+#endif
+
 static char *get_opp_record(struct ext_profile *ext, struct ext_io *l2cap,
                                                        struct ext_io *rfcomm)
 {
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        uint16_t psm = 0;
+#endif
        uint8_t chan = 0;
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (l2cap)
                psm = l2cap->psm;
+#endif
        if (rfcomm)
                chan = rfcomm->chan;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       return g_strdup_printf(OPP_RECORD, chan, ext->version, ext->name);
+#else
        return g_strdup_printf(OPP_RECORD, chan, ext->version, psm, ext->name);
+#endif
 }
 
 static char *get_ftp_record(struct ext_profile *ext, struct ext_io *l2cap,
@@ -2001,7 +2381,11 @@ static struct default_settings {
                .sec_level      = BT_IO_SEC_LOW,
                .authorize      = false,
                .get_record     = get_opp_record,
-               .version        = 0x0102,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               .version    = 0x0100,
+#else
+               .version    = 0x0102,
+#endif
        }, {
                .uuid           = OBEX_FTP_UUID,
                .name           = "File Transfer",
@@ -2052,7 +2436,18 @@ static struct default_settings {
                .authorize      = true,
                .get_record     = get_mns_record,
                .version        = 0x0102
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+       }, {
+               .uuid           = HID_DEVICE_UUID,
+               .name           = "HID Device",
+               .psm            = HID_DEVICE_INTR_PSM,
+               .authorize      = TRUE,
+               .get_record     = get_hid_device_record,
+               .version                = 0x0100,
        },
+#else
+       },
+#endif
 };
 
 static void ext_set_defaults(struct ext_profile *ext)
@@ -2151,10 +2546,32 @@ static int parse_ext_opt(struct ext_profile *ext, const char *key,
                        return -EINVAL;
 
                dbus_message_iter_get_basic(value, &b);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                if (b)
                        ext->sec_level = BT_IO_SEC_MEDIUM;
                else
                        ext->sec_level = BT_IO_SEC_LOW;
+#else
+#ifdef TIZEN_BT_IO_CAPA_NO_INPUT_OUTPUT
+               /*
+                * NoInputOut device should have another authentication method.
+                * So turn off force authentication setting for that device.
+                */
+               if (b)
+                       ext->sec_level = BT_IO_SEC_MEDIUM;
+               else
+                       ext->sec_level = BT_IO_SEC_LOW;
+#else
+               if (!strcasecmp(ext->uuid, WEARABLE_OLD_SAP_UUID) ||
+                   !strcasecmp(ext->uuid, WEARABLE_NEW_SAP_UUID)) {
+                       DBG("Set SAP UUID's sec_level to HIGH");
+                       ext->sec_level = BT_IO_SEC_HIGH;
+               } else if (b)
+                       ext->sec_level = BT_IO_SEC_MEDIUM;
+               else
+                       ext->sec_level = BT_IO_SEC_LOW;
+#endif
+#endif
        } else if (strcasecmp(key, "RequireAuthorization") == 0) {
                if (type != DBUS_TYPE_BOOLEAN)
                        return -EINVAL;
@@ -2235,6 +2652,89 @@ static void set_service(struct ext_profile *ext)
        }
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static struct ext_profile *create_ext2(const char *owner, const char *path,
+                                       const char *uuid, const char *destination, const char *app_path,
+                                       DBusMessageIter *opts)
+{
+       struct btd_profile *p;
+       struct ext_profile *ext;
+
+       ext = g_new0(struct ext_profile, 1);
+
+       ext->uuid = bt_name2string(uuid);
+       if (ext->uuid == NULL) {
+               g_free(ext);
+               return NULL;
+       }
+
+       ext->owner = g_strdup(destination);
+       ext->path = g_strdup(app_path);
+       ext->destination = g_strdup(destination);
+       ext->app_path = g_strdup(app_path);
+       DBG("VALUES Dest %s, path2 %s", destination, app_path);
+       ext_set_defaults(ext);
+
+       while (dbus_message_iter_get_arg_type(opts) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter value, entry;
+               const char *key;
+
+               dbus_message_iter_recurse(opts, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (parse_ext_opt(ext, key, &value) < 0)
+                       error("Invalid value for profile option %s", key);
+
+               dbus_message_iter_next(opts);
+       }
+
+       if (!ext->service)
+               set_service(ext);
+
+       if (ext->enable_server && !(ext->record || ext->get_record))
+               ext->get_record = get_generic_record;
+
+       if (!ext->name)
+               ext->name = g_strdup_printf("%s%s/%s", owner, path, uuid);
+
+       if (!ext->remote_uuid) {
+               if (ext->service)
+                       ext->remote_uuid = g_strdup(ext->service);
+               else
+                       ext->remote_uuid = g_strdup(ext->uuid);
+       }
+
+       p = &ext->p;
+
+       p->name = ext->name;
+       p->local_uuid = ext->service ? ext->service : ext->uuid;
+       p->remote_uuid = ext->remote_uuid;
+
+       if (ext->enable_server) {
+               p->adapter_probe = ext_adapter_probe;
+               p->adapter_remove = ext_adapter_remove;
+       }
+
+       if (ext->enable_client) {
+               p->device_probe = ext_device_probe;
+               p->device_remove = ext_device_remove;
+               p->connect = ext_connect_dev;
+               p->disconnect = ext_disconnect_dev;
+       }
+
+       DBG("Created \"%s\"", ext->name);
+
+       ext_profiles = g_slist_append(ext_profiles, ext);
+
+       adapter_foreach(adapter_add_profile, &ext->p);
+
+       return ext;
+}
+#endif
+
 static struct ext_profile *create_ext(const char *owner, const char *path,
                                        const char *uuid,
                                        DBusMessageIter *opts)
@@ -2307,7 +2807,10 @@ static struct ext_profile *create_ext(const char *owner, const char *path,
        }
 
        DBG("Created \"%s\"", ext->name);
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+       if (g_strcmp0(ext->uuid , HID_DEVICE_UUID) == 0)
+               ext->local_psm = 0;
+#endif
        ext_profiles = g_slist_append(ext_profiles, ext);
 
        adapter_foreach(adapter_add_profile, &ext->p);
@@ -2411,11 +2914,72 @@ static DBusMessage *unregister_profile(DBusConnection *conn,
        return dbus_message_new_method_return(msg);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *register_profile2(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       const char *path, *sender, *uuid;
+       DBusMessageIter args, opts;
+       struct ext_profile *ext;
+       const char *destination, *app_path;
+       sender = dbus_message_get_sender(msg);
+
+       DBG("sender %s", sender);
+
+       dbus_message_iter_init(msg, &args);
+
+       dbus_message_iter_get_basic(&args, &path);
+       dbus_message_iter_next(&args);
+       DBG("path %s", path);
+
+       DBG("path %s", path);
+       dbus_message_iter_get_basic(&args, &uuid);
+       dbus_message_iter_next(&args);
+       DBG("uuid %s", uuid);
+       dbus_message_iter_get_basic(&args, &destination);
+       dbus_message_iter_next(&args);
+       DBG("destination %s", destination);
+       dbus_message_iter_get_basic(&args, &app_path);
+       dbus_message_iter_next(&args);
+       DBG("path2 %s", app_path);
+       ext = find_ext_profile(destination, path);
+       if (ext)
+               return btd_error_already_exists(msg);
+       if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
+               return btd_error_invalid_args(msg);
+       DBG("interator");
+       dbus_message_iter_recurse(&args, &opts);
+
+       ext = create_ext2(sender, path, uuid, destination, app_path, &opts);
+       if (!ext)
+               return btd_error_invalid_args(msg);
+#if 0
+       ext->id = g_dbus_add_disconnect_watch(conn, sender, ext_exited, ext,
+                                                                       NULL);
+#endif
+
+       return dbus_message_new_method_return(msg);
+}
+#endif
+
 static const GDBusMethodTable methods[] = {
        { GDBUS_METHOD("RegisterProfile",
                        GDBUS_ARGS({ "profile", "o"}, { "UUID", "s" },
                                                { "options", "a{sv}" }),
                        NULL, register_profile) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* For Dbus Smack devides dbus API, the functionality is same */
+       { GDBUS_METHOD("RegisterProfile1",
+                       GDBUS_ARGS({ "profile", "o"}, { "UUID", "s" },
+                                               { "options", "a{sv}" }),
+                       NULL, register_profile) },
+       { GDBUS_METHOD("RegisterProfile2",
+                       GDBUS_ARGS({"profile", "o"}, { "UUID", "s" },
+                                               {"destination", "s"}, {"path", "s"},
+                                               { "options", "a{sv}"}),
+                       NULL, register_profile2) },
+#endif
+
        { GDBUS_METHOD("UnregisterProfile", GDBUS_ARGS({ "profile", "o" }),
                        NULL, unregister_profile) },
        { }
@@ -2509,11 +3073,22 @@ void btd_profile_cleanup(void)
                g_slist_free_full(ext->conns, ext_io_destroy);
                ext->conns = NULL;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               if (ext->destination == NULL) {
+                       msg = dbus_message_new_method_call(ext->owner,
+                                                       ext->path,
+                                                       "org.bluez.Profile1",
+                                                       "Release");
+                       if (msg)
+                               g_dbus_send_message(conn, msg);
+               }
+#else
                msg = dbus_message_new_method_call(ext->owner, ext->path,
                                                        "org.bluez.Profile1",
                                                        "Release");
                if (msg)
                        g_dbus_send_message(conn, msg);
+#endif
 
                g_dbus_remove_watch(conn, ext->id);
                remove_ext(ext);
index 4448a2a..d6e20f3 100755 (executable)
@@ -73,5 +73,9 @@ bool btd_profile_add_custom_prop(const char *uuid, const char *type,
                                        void *user_data);
 bool btd_profile_remove_custom_prop(const char *uuid, const char *name);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+gboolean ext_profile_is_registered_as_client_role(struct btd_profile *p);
+#endif
+
 void btd_profile_init(void);
 void btd_profile_cleanup(void);
index 0a3eb60..ec863b6 100755 (executable)
@@ -531,6 +531,11 @@ static void element_end(GMarkupParseContext *context,
        struct context_data *ctx_data = user_data;
        struct sdp_xml_data *elem;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (element_name == NULL)
+               return;
+#endif
+
        if (!strcmp(element_name, "record"))
                return;
 
index 207ffae..28a4db7 100755 (executable)
@@ -171,6 +171,10 @@ int service_probe(struct btd_service *service)
 
 void service_remove(struct btd_service *service)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (service->profile == NULL)
+               return;
+#endif
        change_state(service, BTD_SERVICE_STATE_DISCONNECTED, -ECONNABORTED);
        change_state(service, BTD_SERVICE_STATE_UNAVAILABLE, 0);
        service->profile->device_remove(service);
@@ -238,8 +242,11 @@ int btd_service_connect(struct btd_service *service)
        struct btd_profile *profile = service->profile;
        char addr[18];
        int err;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (profile != NULL && !profile->connect)
+#else
        if (!profile->connect)
+#endif
                return -ENOTSUP;
 
        switch (service->state) {
@@ -373,9 +380,14 @@ bool btd_service_remove_state_cb(unsigned int id)
 
 void btd_service_connecting_complete(struct btd_service *service, int err)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        if (service->state != BTD_SERVICE_STATE_DISCONNECTED &&
                        service->state != BTD_SERVICE_STATE_CONNECTING)
                return;
+#else
+       if (service->state != BTD_SERVICE_STATE_CONNECTING)
+               return;
+#endif
 
        if (err == 0)
                change_state(service, BTD_SERVICE_STATE_CONNECTED, 0);
index 3071b51..bb6a437 100755 (executable)
@@ -68,6 +68,12 @@ struct bt_att {
 
        bool in_req;                    /* There's a pending incoming request */
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+       bool service_change_indication; /* Service changed indication status */
+#endif
+
        uint8_t *buf;
        uint16_t mtu;
 
@@ -209,6 +215,10 @@ static void destroy_att_send_op(void *data)
 
 static void cancel_att_send_op(struct att_send_op *op)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (op->callback)
+               op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data);
+#endif
        if (op->destroy)
                op->destroy(op->user_data);
 
@@ -334,8 +344,14 @@ static struct att_send_op *create_att_send_op(struct bt_att *att,
         * response from the remote end, then no callback should have been
         * provided, since it will never be called.
         */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (callback && type != ATT_OP_TYPE_REQ && type != ATT_OP_TYPE_IND
+                    && type != ATT_OP_TYPE_CMD)
+               return NULL;
+#else
        if (callback && type != ATT_OP_TYPE_REQ && type != ATT_OP_TYPE_IND)
                return NULL;
+#endif
 
        /* Similarly, if the operation does elicit a response then a callback
         * must be provided.
@@ -479,12 +495,21 @@ static bool can_write_data(struct io *io, void *user_data)
        case ATT_OP_TYPE_IND:
                att->pending_ind = op;
                break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       case ATT_OP_TYPE_CMD:
+               if (op->callback)
+                       op->callback(0, NULL, 0, op->user_data);
+               destroy_att_send_op(op);
+               return true;
+#endif
        case ATT_OP_TYPE_RSP:
                /* Set in_req to false to indicate that no request is pending */
                att->in_req = false;
 
                /* Fall through to the next case */
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        case ATT_OP_TYPE_CMD:
+#endif
        case ATT_OP_TYPE_NOT:
        case ATT_OP_TYPE_CONF:
        case ATT_OP_TYPE_UNKNOWN:
@@ -645,6 +670,13 @@ static bool handle_error_rsp(struct bt_att *att, uint8_t *pdu,
 
        att->pending_req = NULL;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (op->timeout_id) {
+               timeout_remove(op->timeout_id);
+               op->timeout_id = 0;
+       }
+#endif
+
        /* Push operation back to request queue */
        return queue_push_head(att->req_queue, op);
 }
@@ -1515,3 +1547,50 @@ bool bt_att_has_crypto(struct bt_att *att)
 
        return att->crypto ? true : false;
 }
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+bool bt_att_set_remote_addr(struct bt_att *att,
+                               const bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+       if (!att)
+               return false;
+
+       bacpy(&att->bdaddr, bdaddr);
+       att->bdaddr_type = bdaddr_type;
+
+       return true;
+}
+
+bool bt_att_get_remote_addr(struct bt_att *att,
+                               bdaddr_t *bdaddr, uint8_t *bdaddr_type)
+{
+       if (!att)
+               return false;
+
+       if (!bacmp(&att->bdaddr, BDADDR_ANY))
+               return false;
+
+       bacpy(bdaddr, &att->bdaddr);
+       *bdaddr_type = att->bdaddr_type;
+
+       return true;
+}
+
+bool bt_att_set_svc_changed_indication_registered(struct bt_att *att, bool value)
+{
+       if (!att)
+               return false;
+
+       att->service_change_indication = value;
+
+       return true;
+}
+
+bool bt_att_get_svc_changed_indication_registered(struct bt_att *att)
+{
+       if (!att)
+               return false;
+
+       return att->service_change_indication;
+}
+#endif
index 7bffee7..d05e720 100755 (executable)
@@ -25,6 +25,9 @@
 #include <stdint.h>
 
 #include "src/shared/att-types.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "lib/bluetooth.h"
+#endif
 
 struct bt_att;
 
@@ -92,3 +95,11 @@ bool bt_att_set_local_key(struct bt_att *att, uint8_t sign_key[16],
 bool bt_att_set_remote_key(struct bt_att *att, uint8_t sign_key[16],
                        bt_att_counter_func_t func, void *user_data);
 bool bt_att_has_crypto(struct bt_att *att);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+bool bt_att_set_remote_addr(struct bt_att *att,
+                               const bdaddr_t *bdaddr, uint8_t bdaddr_type);
+bool bt_att_get_remote_addr(struct bt_att *att,
+                               bdaddr_t *bdaddr, uint8_t *bdaddr_type);
+bool bt_att_set_svc_changed_indication_registered(struct bt_att *att, bool value);
+bool bt_att_get_svc_changed_indication_registered(struct bt_att *att);
+#endif
\ No newline at end of file
index e20d1b3..d111e15 100755 (executable)
@@ -25,6 +25,9 @@
 #include <config.h>
 #endif
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include <stdio.h>
+#endif
 #include <endian.h>
 #include <fcntl.h>
 #include <unistd.h>
@@ -73,6 +76,11 @@ struct btsnoop {
        bool aborted;
        bool pklg_format;
        bool pklg_v2;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char *path;
+       int16_t rotate_count;
+       ssize_t file_size;
+#endif
 };
 
 struct btsnoop *btsnoop_open(const char *path, unsigned long flags)
@@ -131,7 +139,12 @@ failed:
        return NULL;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct btsnoop *btsnoop_create(const char *path, uint32_t format,
+               int16_t rotate_count, ssize_t file_size)
+#else
 struct btsnoop *btsnoop_create(const char *path, uint32_t format)
+#endif
 {
        struct btsnoop *btsnoop;
        struct btsnoop_hdr hdr;
@@ -162,9 +175,99 @@ struct btsnoop *btsnoop_create(const char *path, uint32_t format)
                return NULL;
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (rotate_count > 0 && file_size > 0) {
+               btsnoop->path = strdup(path);
+               btsnoop->rotate_count = rotate_count;
+               btsnoop->file_size = file_size;
+       }
+#endif
+
        return btsnoop_ref(btsnoop);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static int btsnoop_create_2(struct btsnoop *btsnoop)
+{
+       struct btsnoop_hdr hdr;
+       ssize_t written;
+
+       if (btsnoop->fd >= 0)
+               close(btsnoop->fd);
+
+       btsnoop->fd = open(btsnoop->path,
+                       O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
+                       S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+       if (btsnoop->fd < 0) {
+               btsnoop_unref(btsnoop);
+               return -1;
+       }
+
+       memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
+       hdr.version = htobe32(btsnoop_version);
+       hdr.type = htobe32(btsnoop->format);
+
+       written = write(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE);
+       if (written < 0) {
+               btsnoop_unref(btsnoop);
+               return -1;
+       }
+
+       return btsnoop->fd;
+}
+
+static void btsnoop_rotate_files(struct btsnoop *btsnoop)
+{
+       char *filename = NULL;
+       char *new_filename = NULL;
+       int i;
+       int postfix_width = 0;
+       int err;
+
+       if (btsnoop->rotate_count <= 1)
+               return;
+
+       for (i = btsnoop->rotate_count / 10; i; i /= 10)
+               postfix_width++;
+
+       for (i = btsnoop->rotate_count - 2; i >= 0; i--) {
+               if (i == 0) {
+                       filename = strdup(btsnoop->path);
+                       err = (filename == NULL) ? -1 : 0;
+               } else {
+                       err = asprintf(&filename, "%s.%0*d",
+                                       btsnoop->path, postfix_width, i);
+               }
+
+               if (err < 0 || access(filename, F_OK) < 0)
+                       goto done;
+
+               err = asprintf(&new_filename, "%s.%0*d",
+                               btsnoop->path, postfix_width, i + 1);
+               if (err < 0)
+                       goto done;
+
+               err = rename(filename, new_filename);
+
+done:
+               if (new_filename) {
+                       free(new_filename);
+                       new_filename = NULL;
+               }
+
+               if (filename) {
+                       free(filename);
+                       filename = NULL;
+               }
+
+               if (err < 0)
+                       break;
+       }
+
+       return;
+}
+#endif
+
 struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop)
 {
        if (!btsnoop)
@@ -183,6 +286,11 @@ void btsnoop_unref(struct btsnoop *btsnoop)
        if (__sync_sub_and_fetch(&btsnoop->ref_count, 1))
                return;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (btsnoop->path)
+               free(btsnoop->path);
+#endif
+
        if (btsnoop->fd >= 0)
                close(btsnoop->fd);
 
@@ -216,6 +324,16 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
        pkt.drops = htobe32(drops);
        pkt.ts    = htobe64(ts + 0x00E03AB44A676000ll);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if ((btsnoop->rotate_count > 0 && btsnoop->file_size > 0) &&
+                       lseek(btsnoop->fd, 0x00, SEEK_CUR) +
+                       BTSNOOP_PKT_SIZE + size > btsnoop->file_size) {
+               btsnoop_rotate_files(btsnoop);
+               if (btsnoop_create_2(btsnoop) < 0)
+                       return false;
+       }
+#endif
+
        written = write(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE);
        if (written < 0)
                return false;
index 3df8998..25d1af1 100755 (executable)
@@ -99,7 +99,12 @@ struct btsnoop_opcode_user_logging {
 struct btsnoop;
 
 struct btsnoop *btsnoop_open(const char *path, unsigned long flags);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct btsnoop *btsnoop_create(const char *path, uint32_t format,
+               int16_t rotate_count, ssize_t file_size);
+#else
 struct btsnoop *btsnoop_create(const char *path, uint32_t format);
+#endif
 
 struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop);
 void btsnoop_unref(struct btsnoop *btsnoop);
index 6de5514..d8188f2 100755 (executable)
@@ -213,7 +213,11 @@ static int alg_new(int fd, const void *keyval, socklen_t keylen)
                return -1;
 
        /* FIXME: This should use accept4() with SOCK_CLOEXEC */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       return accept(fd, NULL, NULL);
+#else
        return accept(fd, NULL, 0);
+#endif
 }
 
 static bool alg_encrypt(int fd, const void *inbuf, size_t inlen,
index 4386692..025a533 100755 (executable)
@@ -33,6 +33,9 @@
 #include "src/shared/queue.h"
 #include "src/shared/gatt-db.h"
 #include "src/shared/gatt-client.h"
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+#include "../log.h"
+#endif
 
 #include <assert.h>
 #include <limits.h>
@@ -112,6 +115,12 @@ struct bt_gatt_client {
 
        struct bt_gatt_request *discovery_req;
        unsigned int mtu_req_id;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char *device_name;
+       struct queue *pending_noti;
+       bool svc_changed_failed;
+#endif
 };
 
 struct request {
@@ -126,6 +135,26 @@ struct request {
        void (*destroy)(void *);
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct noti {
+       uint8_t opcode;
+       void *pdu;
+       uint16_t length;
+};
+
+static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length,
+                                                       void *user_data);
+
+static void notification_free(void *data)
+{
+       struct noti *noti = data;
+
+       if (noti->pdu)
+               free(noti->pdu);
+       free(noti);
+}
+#endif
+
 static struct request *request_ref(struct request *req)
 {
        __sync_fetch_and_add(&req->ref_count, 1);
@@ -341,11 +370,31 @@ static void discovery_op_complete(struct discovery_op *op, bool success,
 {
        /* Reset remaining range */
        if (success) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               util_debug(op->client->debug_callback, op->client->debug_data,
+                               "op->start : %u, op->end : %u, op->last : %u",
+                               op->start, op->end, op->last);
+
+               if (op->last != UINT16_MAX) {
+                       if (op->start != op->last)
+                               op->last++;
+
+                       if (op->last <= op->end)
+                               gatt_db_clear_range(op->client->db,
+                                               op->last, op->end);
+               }
+#else
                if (op->last != UINT16_MAX)
                        gatt_db_clear_range(op->client->db, op->last + 1,
                                                                UINT16_MAX);
-       } else
+#endif
+       } else {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               util_debug(op->client->debug_callback, op->client->debug_data,
+                               "Fail to discover service. Clear DB [%d]", err);
+#endif
                gatt_db_clear(op->client->db);
+       }
 
        op->success = success;
        op->complete_func(op, success, err);
@@ -368,6 +417,9 @@ static struct discovery_op *discovery_op_create(struct bt_gatt_client *client,
        op->failure_func = failure_func;
        op->start = start;
        op->end = end;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       op->last = start;
+#endif
 
        return op;
 }
@@ -817,6 +869,56 @@ done:
        discovery_op_complete(op, success, att_ecode);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void read_name_cb(bool success, uint8_t att_ecode, const uint8_t *value,
+                                       uint16_t length, void *user_data)
+{
+       struct bt_gatt_client *client = user_data;
+       char *name;
+
+       if (!success) {
+               util_debug(client->debug_callback, client->debug_data,
+                               "read_name_cb failed");
+               return;
+       }
+
+       if (length == 0)
+               return;
+
+       name = malloc(length + 1);
+       if (!name)
+               return;
+
+       memcpy(name, value, length);
+       name[length] = '\0';
+
+       util_debug(client->debug_callback, client->debug_data,
+                               "read_name_cb : %s", name);
+
+       if (client->device_name)
+               free(client->device_name);
+
+       client->device_name = name;
+}
+
+bool bt_gatt_request_att_mtu(struct bt_gatt_client *client, uint16_t mtu,
+                                       void *callback, void *user_data)
+{
+       if (!client || !client->ready)
+               return false;
+
+       /* Configure the MTU */
+       if(!bt_gatt_exchange_mtu(client->att,
+                                               MAX(BT_ATT_DEFAULT_LE_MTU, mtu),
+                                               callback,
+                                               user_data,
+                                               NULL)) {
+               return false;
+       }
+       return true;
+}
+#endif
+
 static void discover_chrcs_cb(bool success, uint8_t att_ecode,
                                                struct bt_gatt_result *result,
                                                void *user_data)
@@ -874,6 +976,16 @@ static void discover_chrcs_cb(bool success, uint8_t att_ecode,
                chrc_data->properties = properties;
                chrc_data->uuid = uuid;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               if (!strcmp(uuid_str, "00002a00-0000-1000-8000-00805f9b34fb")) {
+                       if (!bt_gatt_client_read_value(client, chrc_data->value_handle,
+                                               read_name_cb, client, NULL)) {
+                               util_debug(client->debug_callback, client->debug_data,
+                                               "Failed to read value");
+                       }
+               }
+#endif
+
                queue_push_tail(op->pending_chrcs, chrc_data);
        }
 
@@ -970,6 +1082,13 @@ static void discover_secondary_cb(bool success, uint8_t att_ecode,
                                start, end, uuid_str);
 
                /* Store the service */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               if (client->in_svc_chngd) {
+                       util_debug(client->debug_callback, client->debug_data,
+                               "In service changed, delete service first.");
+                       gatt_db_clear_range(client->db, start, end);
+               }
+#endif
                attr = gatt_db_insert_service(client->db, start, &uuid, false,
                                                        end - start + 1);
                if (!attr) {
@@ -1052,7 +1171,11 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
                                        "Primary service discovery failed."
                                        " ATT ECODE: 0x%02x", att_ecode);
                /* Reset error in case of not found */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
+#else
                if (BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
+#endif
                        success = true;
                        att_ecode = 0;
                }
@@ -1077,6 +1200,13 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
                                "start: 0x%04x, end: 0x%04x, uuid: %s",
                                start, end, uuid_str);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               if (client->in_svc_chngd) {
+                       util_debug(client->debug_callback, client->debug_data,
+                               "In service changed, delete service first.");
+                       gatt_db_clear_range(client->db, start, end);
+               }
+#endif
                attr = gatt_db_insert_service(client->db, start, &uuid, true,
                                                        end - start + 1);
                if (!attr) {
@@ -1185,6 +1315,9 @@ static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
                                        bt_att_get_mtu(client->att));
 
 discover:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       op->success = false;
+#endif
        client->discovery_req = bt_gatt_discover_all_primary_services(
                                                        client->att, NULL,
                                                        discover_primary_cb,
@@ -1208,6 +1341,33 @@ struct service_changed_op {
        uint16_t end_handle;
 };
 
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+bool bt_gatt_discover_services(struct bt_gatt_client *client)
+{
+       struct discovery_op *op;
+
+       if (!client->ready)
+               return false;
+
+       op = new0(struct discovery_op, 1);
+       if (!op)
+               return false;
+
+       op->client = client;
+       gatt_db_unref(op->client->db);
+
+       if (bt_gatt_discover_all_primary_services(client->att, NULL,
+                                                       discover_primary_cb,
+                                                       discovery_op_ref(op),
+                                                       discovery_op_unref))
+               return true;
+
+       discovery_op_unref(op);
+
+       return false;
+}
+#endif
+
 static void process_service_changed(struct bt_gatt_client *client,
                                                        uint16_t start_handle,
                                                        uint16_t end_handle);
@@ -1424,6 +1584,23 @@ static void service_changed_register_cb(uint16_t att_ecode, void *user_data)
 
 done:
        notify_client_ready(client, success, att_ecode);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (success) {
+               struct noti *noti;
+
+               while ((noti = queue_pop_head(client->pending_noti))) {
+                       notify_cb(noti->opcode, noti->pdu,
+                                       noti->length, client);
+                       notification_free(noti);
+               }
+       } else {
+               util_debug(client->debug_callback, client->debug_data,
+                               "Remove all pending notifications");
+               queue_remove_all(client->pending_noti, NULL, NULL,
+                               notification_free);
+       }
+#endif
 }
 
 static bool register_service_changed(struct bt_gatt_client *client)
@@ -1471,6 +1648,9 @@ static void service_changed_complete(struct discovery_op *op, bool success,
                        "Failed to discover services within changed range - "
                        "error: 0x%02x", att_ecode);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               client->svc_changed_failed = true;
+#endif
                gatt_db_clear_range(client->db, start_handle, end_handle);
        }
 
@@ -1509,6 +1689,11 @@ static void service_changed_failure(struct discovery_op *op)
 {
        struct bt_gatt_client *client = op->client;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       util_debug(client->debug_callback, client->debug_data,
+               "Failed to discover services");
+#endif
+
        gatt_db_clear_range(client->db, op->start, op->end);
 }
 
@@ -1549,8 +1734,14 @@ static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
        struct service_changed_op *op;
        uint16_t start, end;
 
-       if (length != 4)
+       if (length != 4) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               util_debug(client->debug_callback, client->debug_data,
+                       "Service changed is received with invalid length : %d",
+                       length);
+#endif
                return;
+       }
 
        start = get_le16(value);
        end = get_le16(value + 2);
@@ -1603,12 +1794,34 @@ fail:
 
 done:
        notify_client_ready(client, success, att_ecode);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (success) {
+               struct noti *noti;
+
+               while ((noti = queue_pop_head(client->pending_noti))) {
+                       notify_cb(noti->opcode, noti->pdu,
+                                       noti->length, client);
+                       notification_free(noti);
+               }
+       } else {
+               util_debug(client->debug_callback, client->debug_data,
+                               "Remove all pending notifications");
+               queue_remove_all(client->pending_noti, NULL, NULL,
+                               notification_free);
+       }
+#endif
 }
 
 static void init_fail(struct discovery_op *op)
 {
        struct bt_gatt_client *client = op->client;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       util_debug(client->debug_callback, client->debug_data,
+                       "GATT client init is failed");
+#endif
+
        gatt_db_clear(client->db);
 }
 
@@ -1707,6 +1920,10 @@ static void complete_unregister_notify(void *data)
        if (notify_data->att_id) {
                bt_att_cancel(notify_data->client->att, notify_data->att_id);
                notify_data->att_id = 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               notify_data->chrc->ccc_write_id = 0;
+               __sync_sub_and_fetch(&notify_data->chrc->notify_count, 1);
+#endif
                goto done;
        }
 
@@ -1750,6 +1967,35 @@ static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length,
        struct bt_gatt_client *client = user_data;
        struct pdu_data pdu_data;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (client->ready == false) {
+               struct noti *noti;
+
+               util_debug(client->debug_callback, client->debug_data,
+                               "Client is not ready. pend notification.");
+
+               noti = new0(struct noti, 1);
+               if (!noti)
+                       return;
+
+               noti->pdu = malloc(length);
+               if (!noti->pdu) {
+                       free(noti);
+                       return;
+               }
+
+               noti->opcode = opcode;
+               noti->length = length;
+               memcpy(noti->pdu, pdu, length);
+
+               util_debug(client->debug_callback, client->debug_data,
+                               "Notification handle : %d", get_le16(pdu));
+
+               queue_push_tail(client->pending_noti, noti);
+               return;
+       }
+#endif
+
        bt_gatt_client_ref(client);
 
        memset(&pdu_data, 0, sizeof(pdu_data));
@@ -1784,6 +2030,16 @@ static void bt_gatt_client_free(struct bt_gatt_client *client)
                bt_att_unref(client->att);
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (client->in_svc_chngd || client->svc_changed_failed) {
+               util_debug(client->debug_callback, client->debug_data,
+                               "Service changed is going. Clear DB");
+               gatt_db_clear(client->db);
+       }
+
+       queue_destroy(client->pending_noti, notification_free);
+#endif
+
        gatt_db_unref(client->db);
 
        queue_destroy(client->clones, NULL);
@@ -1792,6 +2048,13 @@ static void bt_gatt_client_free(struct bt_gatt_client *client)
        queue_destroy(client->notify_chrcs, notify_chrc_free);
        queue_destroy(client->pending_requests, request_unref);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (client->device_name) {
+               free(client->device_name);
+               client->device_name = NULL;
+       }
+#endif
+
        if (client->parent) {
                queue_remove(client->parent->clones, client);
                bt_gatt_client_unref(client->parent);
@@ -1834,6 +2097,9 @@ static struct bt_gatt_client *gatt_client_new(struct gatt_db *db,
        client->notify_list = queue_new();
        client->notify_chrcs = queue_new();
        client->pending_requests = queue_new();
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       client->pending_noti = queue_new();
+#endif
 
        client->notify_id = bt_att_register(att, BT_ATT_OP_HANDLE_VAL_NOT,
                                                notify_cb, client, NULL);
@@ -2443,6 +2709,93 @@ unsigned int bt_gatt_client_read_long_value(struct bt_gatt_client *client,
        return req->id;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct write_cmd_op {
+       struct bt_gatt_client *client;
+       bt_gatt_client_callback_t callback;
+       void *user_data;
+       bt_gatt_destroy_func_t destroy;
+};
+
+static void destroy_write_cmd_op(void *data)
+{
+       struct write_cmd_op *op = data;
+
+       if (op->destroy)
+               op->destroy(op->user_data);
+
+       free(op);
+}
+
+static void write_cmd_cb(uint8_t opcode, const void *pdu, uint16_t length,
+                                                               void *user_data)
+{
+       struct request *req = user_data;
+       struct write_cmd_op *op = req->data;
+       bool success = true;
+       uint8_t att_ecode = 0;
+
+       if (op->callback)
+               op->callback(success, att_ecode, op->user_data);
+}
+
+unsigned int bt_gatt_client_write_without_response_async(
+                                       struct bt_gatt_client *client,
+                                       uint16_t value_handle,
+                                       bool signed_write,
+                                       const uint8_t *value, uint16_t length,
+                                       bt_gatt_client_callback_t callback,
+                                       void *user_data,
+                                       bt_gatt_client_destroy_func_t destroy)
+{
+
+       uint8_t pdu[2 + length];
+       struct request *req;
+       struct write_cmd_op *op;
+       int security;
+       uint8_t opcode;
+
+       if (!client)
+               return 0;
+
+       op = new0(struct write_cmd_op, 1);
+       if (!op)
+               return 0;
+
+       req = request_create(client);
+       if (!req)
+               return 0;
+
+       op->callback = callback;
+       op->user_data = user_data;
+       op->destroy = destroy;
+
+       req->data = op;
+       req->destroy = destroy_write_cmd_op;
+
+       /* Only use signed write if unencrypted */
+       if (signed_write) {
+               security = bt_att_get_security(client->att);
+               opcode = security > BT_SECURITY_LOW ?  BT_ATT_OP_WRITE_CMD :
+                                               BT_ATT_OP_SIGNED_WRITE_CMD;
+       } else
+               opcode = BT_ATT_OP_WRITE_CMD;
+
+       put_le16(value_handle, pdu);
+       memcpy(pdu + 2, value, length);
+
+       req->att_id = bt_att_send(client->att, opcode,
+                       pdu, sizeof(pdu), write_cmd_cb, req, request_unref);
+       if (!req->att_id) {
+               op->destroy = NULL;
+               request_unref(req);
+               return 0;
+       }
+
+       return req->id;
+}
+#endif
+
 unsigned int bt_gatt_client_write_without_response(
                                        struct bt_gatt_client *client,
                                        uint16_t value_handle,
@@ -3134,8 +3487,14 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
        if (!client || !client->db || !chrc_value_handle || !callback)
                return 0;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (client->in_svc_chngd)
+               util_debug(client->debug_callback, client->debug_data,
+                               "register_notify in service changed handling");
+#else
        if (client->in_svc_chngd)
                return 0;
+#endif
 
        return register_notify(client, chrc_value_handle, callback, notify,
                                                        user_data, destroy);
@@ -3176,3 +3535,21 @@ int bt_gatt_client_get_security(struct bt_gatt_client *client)
 
        return bt_att_get_security(client->att);
 }
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+char *bt_gatt_client_get_gap_device_name(struct bt_gatt_client *client)
+{
+       if (!client)
+               return NULL;
+
+       return client->device_name;
+}
+
+bool bt_gatt_client_svc_changed_received(struct bt_gatt_client *client)
+{
+       if (!client)
+               return false;
+
+       return client->in_svc_chngd;
+}
+#endif
index aceb570..58931c4 100755 (executable)
@@ -92,6 +92,17 @@ unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client,
                                        void *user_data,
                                        bt_gatt_client_destroy_func_t destroy);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+unsigned int bt_gatt_client_write_without_response_async(
+                                       struct bt_gatt_client *client,
+                                       uint16_t value_handle,
+                                       bool signed_write,
+                                       const uint8_t *value, uint16_t length,
+                                       bt_gatt_client_callback_t callback,
+                                       void *user_data,
+                                       bt_gatt_client_destroy_func_t destroy);
+#endif
+
 unsigned int bt_gatt_client_write_without_response(
                                        struct bt_gatt_client *client,
                                        uint16_t value_handle,
@@ -134,3 +145,14 @@ bool bt_gatt_client_unregister_notify(struct bt_gatt_client *client,
 
 bool bt_gatt_client_set_security(struct bt_gatt_client *client, int level);
 int bt_gatt_client_get_security(struct bt_gatt_client *client);
+
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+bool bt_gatt_discover_services(struct bt_gatt_client *client);
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+char *bt_gatt_client_get_gap_device_name(struct bt_gatt_client *client);
+bool bt_gatt_request_att_mtu(struct bt_gatt_client *client, uint16_t mtu,
+                                       void *callback, void *user_data);
+bool bt_gatt_client_svc_changed_received(struct bt_gatt_client *client);
+#endif
index 513451f..69554a9 100755 (executable)
@@ -95,6 +95,10 @@ struct gatt_db_attribute {
        uint32_t permissions;
        uint16_t value_len;
        uint8_t *value;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       bool notify_indicate;
+       bdaddr_t unicast_addr;
+#endif
 
        gatt_db_read_t read_func;
        gatt_db_write_t write_func;
@@ -719,6 +723,9 @@ service_insert_characteristic(struct gatt_db_service *service,
        service->attributes[i] = new_attribute(service, handle, uuid, NULL, 0);
        if (!service->attributes[i]) {
                free(service->attributes[i - 1]);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               service->attributes[i - 1] = NULL;
+#endif
                return NULL;
        }
 
@@ -1784,3 +1791,34 @@ bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib)
 
        return true;
 }
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void set_ccc_notify_indicate(struct gatt_db_attribute *ccc,
+                                                       bool enable)
+{
+       if (ccc)
+               ccc->notify_indicate = enable;
+}
+
+bool get_ccc_notify_indicate(const struct gatt_db_attribute *ccc)
+{
+       if (ccc)
+               return ccc->notify_indicate;
+
+       return false;
+}
+
+void set_ccc_unicast_address(struct gatt_db_attribute *ccc,
+                                                       const char *address)
+{
+       if (ccc)
+               str2ba(address, &ccc->unicast_addr);
+}
+
+const bdaddr_t *get_ccc_unicast_address(const struct gatt_db_attribute *ccc)
+{
+       if (ccc)
+               return &ccc->unicast_addr;
+       return NULL;
+}
+#endif
index 134ec63..8ac0c32 100755 (executable)
@@ -235,3 +235,15 @@ bool gatt_db_attribute_write_result(struct gatt_db_attribute *attrib,
                                                unsigned int id, int err);
 
 bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void set_ccc_notify_indicate(struct gatt_db_attribute *ccc,
+                                                       bool enable);
+
+bool get_ccc_notify_indicate(const struct gatt_db_attribute *ccc);
+
+void set_ccc_unicast_address(struct gatt_db_attribute *ccc,
+                                                       const char *address);
+
+const bdaddr_t *get_ccc_unicast_address(const struct gatt_db_attribute *ccc);
+#endif
index 79e01c8..f89ae01 100755 (executable)
@@ -112,6 +112,12 @@ struct bt_gatt_server {
        bt_gatt_server_debug_func_t debug_callback;
        bt_gatt_server_destroy_func_t debug_destroy;
        void *debug_data;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       bt_gatt_server_mtu_changed_callback_t mtu_chngd_callback;
+       bt_gatt_server_destroy_func_t mtu_chngd_destroy;
+       void *mtu_chngd_data;
+#endif
 };
 
 static void bt_gatt_server_free(struct bt_gatt_server *server)
@@ -182,7 +188,11 @@ static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q,
        int iter = 0;
        uint16_t start_handle, end_handle;
        struct iovec value;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint8_t data_val_len = 0;
+#else
        uint8_t data_val_len;
+#endif
 
        *len = 0;
 
@@ -524,7 +534,11 @@ static bool encode_find_info_rsp(struct gatt_db *db, struct queue *q,
        uint16_t handle;
        struct gatt_db_attribute *attr;
        const bt_uuid_t *type;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       int uuid_len = 0, cur_uuid_len;
+#else
        int uuid_len, cur_uuid_len;
+#endif
        int iter = 0;
 
        *len = 0;
@@ -789,14 +803,26 @@ static void write_cb(uint8_t opcode, const void *pdu,
                goto error;
 
        if (server->pending_write_op) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               if (opcode != BT_ATT_OP_WRITE_CMD) {
+#endif
                ecode = BT_ATT_ERROR_UNLIKELY;
                goto error;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               }
+#endif
        }
 
        op = new0(struct async_write_op, 1);
        op->server = server;
        op->opcode = opcode;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (opcode != BT_ATT_OP_WRITE_CMD)
+               server->pending_write_op = op;
+#else
        server->pending_write_op = op;
+#endif
 
        if (gatt_db_attribute_write(attr, 0, pdu + 2, length - 2, opcode,
                                                        server->att,
@@ -809,6 +835,13 @@ static void write_cb(uint8_t opcode, const void *pdu,
        ecode = BT_ATT_ERROR_UNLIKELY;
 
 error:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       util_debug(server->debug_callback, server->debug_data,
+                               "Handling \"Write %s\" is failed : %d",
+                               (opcode == BT_ATT_OP_WRITE_REQ) ? "Req" : "Cmd",
+                               ecode);
+#endif
+
        if (opcode == BT_ATT_OP_WRITE_CMD)
                return;
 
@@ -912,6 +945,12 @@ static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode,
        ecode = BT_ATT_ERROR_UNLIKELY;
 
 error:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       util_debug(server->debug_callback, server->debug_data,
+                               "Handling \"Read %sReq\" is failed : %d",
+                               (opcode == BT_ATT_OP_READ_BLOB_REQ) ? "Blob" : "",
+                               ecode);
+#endif
        if (op)
                async_read_op_destroy(op);
 
@@ -1369,6 +1408,7 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
        }
 
        client_rx_mtu = get_le16(pdu);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        final_mtu = MAX(MIN(client_rx_mtu, server->mtu), BT_ATT_DEFAULT_LE_MTU);
 
        /* Respond with the server MTU */
@@ -1379,6 +1419,21 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
        /* Set MTU to be the minimum */
        server->mtu = final_mtu;
        bt_att_set_mtu(server->att, final_mtu);
+#else
+       final_mtu = MAX(MIN(client_rx_mtu, BT_ATT_MAX_LE_MTU), BT_ATT_DEFAULT_LE_MTU);
+
+       /* Set MTU to be the minimum */
+       server->mtu = final_mtu;
+       bt_att_set_mtu(server->att, final_mtu);
+
+       /* Respond with the server MTU */
+       put_le16(server->mtu, rsp_pdu);
+       bt_att_send(server->att, BT_ATT_OP_MTU_RSP, rsp_pdu, 2, NULL, NULL,
+                                                                       NULL);
+
+       if (server->mtu_chngd_callback)
+               server->mtu_chngd_callback(final_mtu, server->mtu_chngd_data);
+#endif
 
        util_debug(server->debug_callback, server->debug_data,
                        "MTU exchange complete, with MTU: %u", final_mtu);
@@ -1633,3 +1688,23 @@ bool bt_gatt_server_send_indication(struct bt_gatt_server *server,
 
        return result;
 }
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+bool bt_gatt_server_set_mtu_changed(struct bt_gatt_server *server,
+                       bt_gatt_server_mtu_changed_callback_t callback,
+                       void *user_data,
+                       bt_gatt_server_destroy_func_t destroy)
+{
+       if (!server)
+               return false;
+
+       if (server->mtu_chngd_destroy)
+               server->mtu_chngd_destroy(server->mtu_chngd_data);
+
+       server->mtu_chngd_callback = callback;
+       server->mtu_chngd_destroy = destroy;
+       server->mtu_chngd_data = user_data;
+
+       return true;
+}
+#endif
index 0e480e1..c61eabe 100755 (executable)
@@ -50,3 +50,14 @@ bool bt_gatt_server_send_indication(struct bt_gatt_server *server,
                                        bt_gatt_server_conf_func_t callback,
                                        void *user_data,
                                        bt_gatt_server_destroy_func_t destroy);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+typedef void (*bt_gatt_server_mtu_changed_callback_t)(uint16_t mtu,
+                                                       void *user_data);
+
+bool bt_gatt_server_set_mtu_changed(struct bt_gatt_server *server,
+                       bt_gatt_server_mtu_changed_callback_t callback,
+                       void *user_data,
+                       bt_gatt_server_destroy_func_t destroy);
+
+#endif
index 734a9e0..c478e03 100755 (executable)
@@ -144,6 +144,53 @@ int read_local_name(const bdaddr_t *bdaddr, char *name)
        return 0;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int write_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+                                       uint8_t bdaddr_type, uint16_t handle,
+                                                                 const char *chars)
+{
+       char filename[PATH_MAX + 1], addr[18], key[25];
+
+       create_filename(filename, PATH_MAX, sba, "characteristics");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(dba, addr);
+       snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle);
+
+       return textfile_put(filename, key, chars);
+}
+
+char *read_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+                                       uint8_t bdaddr_type, uint16_t handle)
+{
+       char filename[PATH_MAX + 1], addr[18], key[25];
+
+       create_filename(filename, PATH_MAX, sba, "characteristics");
+
+       ba2str(dba, addr);
+       snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle);
+
+       return textfile_get(filename, key);
+}
+
+int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba,
+                               uint8_t bdaddr_type, uint16_t handle,
+                                                       const char *chars)
+{
+       char filename[PATH_MAX + 1], addr[18], key[25];
+
+       create_filename(filename, PATH_MAX, sba, "attributes");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(dba, addr);
+       snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle);
+
+       return textfile_put(filename, key, chars);
+}
+#endif
+
 sdp_record_t *record_from_string(const char *str)
 {
        sdp_record_t *rec;
index 1c0ad57..979075b 100755 (executable)
@@ -25,5 +25,15 @@ int read_discoverable_timeout(const char *src, int *timeout);
 int read_pairable_timeout(const char *src, int *timeout);
 int read_on_mode(const char *src, char *mode, int length);
 int read_local_name(const bdaddr_t *bdaddr, char *name);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int write_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+                                       uint8_t bdaddr_type, uint16_t handle,
+                                       const char *chars);
+char *read_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+                                       uint8_t bdaddr_type, uint16_t handle);
+int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba,
+                               uint8_t bdaddr_type, uint16_t handle,
+                               const char *chars);
+#endif
 sdp_record_t *record_from_string(const char *str);
 sdp_record_t *find_record_in_list(sdp_list_t *recs, const char *uuid);
diff --git a/test/advertisement-example b/test/advertisement-example
new file mode 100755 (executable)
index 0000000..98aeafa
--- /dev/null
@@ -0,0 +1,170 @@
+#!/usr/bin/python
+
+import dbus
+import dbus.exceptions
+import dbus.mainloop.glib
+import dbus.service
+
+import array
+import gobject
+
+from random import randint
+
+mainloop = None
+
+BLUEZ_SERVICE_NAME = 'org.bluez'
+LE_ADVERTISING_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1'
+DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
+
+LE_ADVERTISEMENT_IFACE = 'org.bluez.LEAdvertisement1'
+
+
+class InvalidArgsException(dbus.exceptions.DBusException):
+    _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
+
+
+class NotSupportedException(dbus.exceptions.DBusException):
+    _dbus_error_name = 'org.bluez.Error.NotSupported'
+
+
+class NotPermittedException(dbus.exceptions.DBusException):
+    _dbus_error_name = 'org.bluez.Error.NotPermitted'
+
+
+class InvalidValueLengthException(dbus.exceptions.DBusException):
+    _dbus_error_name = 'org.bluez.Error.InvalidValueLength'
+
+
+class FailedException(dbus.exceptions.DBusException):
+    _dbus_error_name = 'org.bluez.Error.Failed'
+
+
+class Advertisement(dbus.service.Object):
+    PATH_BASE = '/org/bluez/example/advertisement'
+
+    def __init__(self, bus, index, advertising_type):
+        self.path = self.PATH_BASE + str(index)
+        self.bus = bus
+        self.ad_type = advertising_type
+        self.service_uuids = None
+        self.manufacturer_data = None
+        self.solicit_uuids = None
+        self.service_data = None
+        dbus.service.Object.__init__(self, bus, self.path)
+
+    def get_properties(self):
+        properties = dict()
+        properties['Type'] = self.ad_type
+        if self.service_uuids is not None:
+            properties['ServiceUUIDs'] = dbus.Array(self.service_uuids,
+                                                    signature='s')
+        if self.solicit_uuids is not None:
+            properties['SolicitUUIDs'] = dbus.Array(self.solicit_uuids,
+                                                    signature='s')
+        if self.manufacturer_data is not None:
+            properties['ManufacturerData'] = dbus.Dictionary(
+                self.manufacturer_data, signature='qay')
+        if self.service_data is not None:
+            properties['ServiceData'] = dbus.Dictionary(self.service_data,
+                                                        signature='say')
+        return {LE_ADVERTISEMENT_IFACE: properties}
+
+    def get_path(self):
+        return dbus.ObjectPath(self.path)
+
+    def add_service_uuid(self, uuid):
+        if not self.service_uuids:
+            self.service_uuids = []
+        self.service_uuids.append(uuid)
+
+    def add_solicit_uuid(self, uuid):
+        if not self.solicit_uuids:
+            self.solicit_uuids = []
+        self.solicit_uuids.append(uuid)
+
+    def add_manufacturer_data(self, manuf_code, data):
+        if not self.manufacturer_data:
+            self.manufacturer_data = dict()
+        self.manufacturer_data[manuf_code] = data
+
+    def add_service_data(self, uuid, data):
+        if not self.service_data:
+            self.service_data = dict()
+        self.service_data[uuid] = data
+
+    @dbus.service.method(DBUS_PROP_IFACE,
+                         in_signature='s',
+                         out_signature='a{sv}')
+    def GetAll(self, interface):
+        print 'GetAll'
+        if interface != LE_ADVERTISEMENT_IFACE:
+            raise InvalidArgsException()
+        print 'returning props'
+        return self.get_properties()[LE_ADVERTISEMENT_IFACE]
+
+    @dbus.service.method(LE_ADVERTISEMENT_IFACE,
+                         in_signature='',
+                         out_signature='')
+    def Release(self):
+        print '%s: Released!' % self.path
+
+class TestAdvertisement(Advertisement):
+
+    def __init__(self, bus, index):
+        Advertisement.__init__(self, bus, index, 'broadcast')
+        self.add_service_uuid('0000180D-0000-1000-8000-00805F9B34FB')
+        self.add_service_uuid('0000180F-0000-1000-8000-00805F9B34FB')
+        self.add_manufacturer_data(0xffff, [0x00, 0x01, 0x02, 0x03, 0x04])
+        self.add_service_data('00009999-0000-1000-8000-00805F9B34FB',
+                              [0x00, 0x01, 0x02, 0x03, 0x04])
+
+
+def register_ad_cb():
+    print 'Advertisement registered'
+
+
+def register_ad_error_cb(error):
+    print 'Failed to register advertisement: ' + str(error)
+    mainloop.quit()
+
+
+def find_adapter(bus):
+    remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
+                               DBUS_OM_IFACE)
+    objects = remote_om.GetManagedObjects()
+
+    for o, props in objects.iteritems():
+        if LE_ADVERTISING_MANAGER_IFACE in props:
+            return o
+
+    return None
+
+
+def main():
+    global mainloop
+
+    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+    bus = dbus.SystemBus()
+
+    adapter = find_adapter(bus)
+    if not adapter:
+        print 'LEAdvertisingManager1 interface not found'
+        return
+
+    ad_manager = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
+                                LE_ADVERTISING_MANAGER_IFACE)
+
+    test_advertisement = TestAdvertisement(bus, 0)
+
+    mainloop = gobject.MainLoop()
+
+    ad_manager.RegisterAdvertisement(test_advertisement.get_path(), {},
+                                     reply_handler=register_ad_cb,
+                                     error_handler=register_ad_error_cb)
+
+    mainloop.run()
+
+if __name__ == '__main__':
+    main()
diff --git a/test/exchange-business-cards b/test/exchange-business-cards
new file mode 100755 (executable)
index 0000000..6805cf7
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SessionBus()
+client = dbus.Interface(bus.get_object("org.bluez.obex", "/org/bluez/obex"),
+                                       "org.bluez.obex.Client")
+
+if (len(sys.argv) < 4):
+       print "Usage: %s <device> <clientfile> <file>" % (sys.argv[0])
+       sys.exit(1)
+
+print "Creating Session"
+path = client.CreateSession(sys.argv[1], { "Target": "OPP" })
+opp = dbus.Interface(bus.get_object("org.bluez.obex", path),
+                                       "org.bluez.obex.ObjectPush")
+
+opp.ExchangeBusinessCards(sys.argv[2], sys.argv[3])
diff --git a/test/get-managed-objects b/test/get-managed-objects
new file mode 100755 (executable)
index 0000000..3156f65
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                       "org.freedesktop.DBus.ObjectManager")
+
+objects = manager.GetManagedObjects()
+
+for path in objects.keys():
+       print("[ %s ]" % (path))
+
+       interfaces = objects[path]
+
+       for interface in interfaces.keys():
+               if interface in ["org.freedesktop.DBus.Introspectable",
+                                       "org.freedesktop.DBus.Properties"]:
+                       continue
+
+               print("    %s" % (interface))
+
+               properties = interfaces[interface]
+
+               for key in properties.keys():
+                       print("      %s = %s" % (key, properties[key]))
diff --git a/test/get-obex-capabilities b/test/get-obex-capabilities
new file mode 100755 (executable)
index 0000000..e8afbad
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SessionBus()
+client = dbus.Interface(bus.get_object("org.bluez.obex", "/org/bluez/obex"),
+                                       "org.bluez.obex.Client")
+
+if (len(sys.argv) < 3):
+       print "Usage: %s <device> <target>" % (sys.argv[0])
+       sys.exit(1)
+
+print "Creating Session"
+session_path = client.CreateSession(sys.argv[1], { "Target": sys.argv[2] })
+session = dbus.Interface(bus.get_object("org.bluez.obex", session_path),
+                                       "org.bluez.obex.Session")
+
+print session.GetCapabilities()
diff --git a/test/list-folders b/test/list-folders
new file mode 100755 (executable)
index 0000000..7321a15
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+
+def list_folder(folder):
+       bus = dbus.SessionBus()
+       client = dbus.Interface(bus.get_object("org.bluez.obex",
+                                               "/org/bluez/obex"),
+                                               "org.bluez.obex.Client")
+
+       path = client.CreateSession(sys.argv[1], { "Target": "ftp" })
+
+       ftp = dbus.Interface(bus.get_object("org.bluez.obex", path),
+                               "org.bluez.obex.FileTransfer")
+
+       if folder:
+               for node in folder.split("/"):
+                       ftp.ChangeFolder(node)
+
+       for i in ftp.ListFolder():
+               if i["Type"] == "folder":
+                       print "%s/" % (i["Name"])
+               else:
+                       print "%s" % (i["Name"])
+
+
+if __name__ == '__main__':
+
+       if len(sys.argv) < 2:
+               print "Usage: %s <device> [folder]" % (sys.argv[0])
+               sys.exit(1)
+
+       folder = None
+       if len(sys.argv) == 3:
+               folder = sys.argv[2]
+
+       list_folder(folder)
diff --git a/test/simple-obex-agent b/test/simple-obex-agent
new file mode 100755 (executable)
index 0000000..05ec4ed
--- /dev/null
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+BUS_NAME = 'org.bluez.obex'
+PATH = '/org/bluez/obex'
+AGENT_MANAGER_INTERFACE = 'org.bluez.obex.AgentManager1'
+AGENT_INTERFACE = 'org.bluez.obex.Agent1'
+TRANSFER_INTERFACE = 'org.bluez.obex.Transfer1'
+
+def ask(prompt):
+       try:
+               return raw_input(prompt)
+       except:
+               return input(prompt)
+
+class Agent(dbus.service.Object):
+       def __init__(self, conn=None, obj_path=None):
+               dbus.service.Object.__init__(self, conn, obj_path)
+               self.pending_auth = False
+
+       @dbus.service.method(AGENT_INTERFACE, in_signature="o",
+                                                       out_signature="s")
+       def AuthorizePush(self, path):
+               transfer = dbus.Interface(bus.get_object(BUS_NAME, path),
+                                       'org.freedesktop.DBus.Properties')
+               properties = transfer.GetAll(TRANSFER_INTERFACE);
+
+               self.pending_auth = True
+               auth = ask("Authorize (%s, %s) (Y/n):" % (path,
+                                                       properties['Name']))
+
+               if auth == "n" or auth == "N":
+                       self.pending_auth = False
+                       raise dbus.DBusException(
+                                       "org.bluez.obex.Error.Rejected: "
+                                       "Not Authorized")
+
+               self.pending_auth = False
+
+               return properties['Name']
+
+       @dbus.service.method(AGENT_INTERFACE, in_signature="",
+                                                       out_signature="")
+       def Cancel(self):
+               print("Authorization Canceled")
+               self.pending_auth = False
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SessionBus()
+       manager = dbus.Interface(bus.get_object(BUS_NAME, PATH),
+                                               AGENT_MANAGER_INTERFACE)
+
+       path = "/test/agent"
+       agent = Agent(bus, path)
+
+       mainloop = GObject.MainLoop()
+
+       manager.RegisterAgent(path)
+       print("Agent registered")
+
+       cont = True
+       while cont:
+               try:
+                       mainloop.run()
+               except KeyboardInterrupt:
+                       if agent.pending_auth:
+                               agent.Cancel()
+                       elif len(transfers) > 0:
+                               for a in transfers:
+                                       a.cancel()
+                       else:
+                               cont = False
+
+       # manager.UnregisterAgent(path)
+       # print "Agent unregistered"
diff --git a/tools/bdaddr.1 b/tools/bdaddr.1
new file mode 100755 (executable)
index 0000000..efb77d2
--- /dev/null
@@ -0,0 +1,68 @@
+.TH BDADDR 1 "Sep 27 2005" BlueZ "Linux System Administration"
+.SH NAME
+bdaddr \- Utility for changing the Bluetooth device address
+.SH SYNOPSIS
+.B bdaddr
+.br
+.B bdaddr -h
+.br
+.B bdaddr [-i <dev>] [-r] [-t] [new bdaddr]
+
+.SH DESCRIPTION
+.LP
+.B
+bdaddr
+is used to query or set the local Bluetooth device address (BD_ADDR). If run
+with no arguments,
+.B
+bdaddr
+prints the chip manufacturer's name, and the current BD_ADDR. If the IEEE OUI
+index file "oui.txt" is installed on the system, the BD_ADDR owner will be
+displayed. If the optional [new bdaddr] argument is given, the device will be
+reprogrammed with that address. This can either be permanent or temporary, as
+specified by the -t flag. In both cases, the device must be reset before the
+new address will become active. This can be done with a 'soft' reset by
+specifying the -r flag, or a 'hard' reset by removing and replugging the
+device. A 'hard' reset will cause the address to revert to the current
+non-volatile value.
+.PP
+.B
+bdaddr
+uses manufacturer specific commands to set the address, and is therefore
+device specific. For this reason, not all devices are supported, and not all
+options are supported on all devices.
+Current supported manufacturers are:
+.B Ericsson, Cambridge Silicon Radio (CSR), Texas Instruments (TI), Zeevo
+and
+.B ST Microelectronics (ST)
+
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands.
+.TP
+.BI -i\ <dev>
+Specify a particular device to operate on. If not specified, default is the
+first available device.
+.TP
+.BI -r
+Reset device and make new BD_ADDR active.
+.B
+CSR
+devices only.
+.TP
+.BI -t
+Temporary change. Do not write to non-volatile memory.
+.B
+CSR
+devices only.
+.SH FILES
+.TP
+.I
+/usr/share/misc/oui.txt
+IEEE Organizationally Unique Identifier master file.
+Manually update from: http://standards.ieee.org/regauth/oui/oui.txt
+.SH AUTHORS
+Written by Marcel Holtmann <marcel@holtmann.org>,
+man page by Adam Laurie <adam@algroup.co.uk>
+.PP
index 1404252..c761e16 100755 (executable)
@@ -327,7 +327,11 @@ static gboolean setup_bnep_cb(GIOChannel *chan, GIOCondition cond,
        sk = g_io_channel_unix_get_fd(chan);
 
        /* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */
+#ifdef  TIZEN_FEATURE_BLUEZ_MODIFY
        n = recv(sk, packet, sizeof(packet), MSG_PEEK);
+#else
+       n = read(sk, packet, sizeof(packet));
+#endif
        if (n < 0) {
                error("read(): %s(%d)", strerror(errno), errno);
                return FALSE;
index 4c8c9dd..31848f9 100755 (executable)
@@ -365,8 +365,13 @@ static void ready_cb(bool success, uint8_t att_ecode, void *user_data)
        struct client *cli = user_data;
 
        if (!success) {
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                PRLOG("GATT discovery procedures failed - error code: 0x%02x\n",
                                                                att_ecode);
+#else
+               PRLOG("GATT discovery procedures failed: %s (0x%02x)\n",
+                               ecode_to_string(att_ecode), att_ecode);
+#endif
                return;
        }
 
@@ -480,7 +485,12 @@ static void read_multiple_cb(bool success, uint8_t att_ecode,
        int i;
 
        if (!success) {
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                PRLOG("\nRead multiple request failed: 0x%02x\n", att_ecode);
+#else
+               PRLOG("\nRead multiple request failed: %s (0x%02x)\n",
+                               ecode_to_string(att_ecode), att_ecode);
+#endif
                return;
        }
 
@@ -1109,8 +1119,13 @@ static void notify_cb(uint16_t value_handle, const uint8_t *value,
 static void register_notify_cb(uint16_t att_ecode, void *user_data)
 {
        if (att_ecode) {
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                PRLOG("Failed to register notify handler "
                                        "- error code: 0x%02x\n", att_ecode);
+#else
+               PRLOG("Failed to register notify handler: %s (0x%02x)\n",
+                               ecode_to_string(att_ecode), att_ecode);
+#endif
                return;
        }
 
index 3eb8082..c2a194b 100755 (executable)
 #include <stdint.h>
 #include <stdbool.h>
 #include <string.h>
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include <time.h>
+#include <sys/time.h>
+#endif
 #include <getopt.h>
 #include <endian.h>
 #include <arpa/inet.h>
@@ -271,6 +275,118 @@ close_input:
        for (i = 0; i < num_input; i++)
                close(input_fd[i]);
 }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define BT_SNOOP_TYPE_HCI_PREFIX "btsnoop_type_hci"
+#define MAX_SUPPORTED_ADAPTER  16
+
+static void command_split(const char *input)
+{
+       unsigned char buf[BTSNOOP_MAX_PACKET_SIZE];
+       uint16_t pktlen,opcode;
+       uint32_t type;
+       struct timeval tv;
+       uint16_t index, max_index = 0;
+       char write_file_name[255];
+       struct btsnoop *btsnoop_read_file = NULL;
+       struct btsnoop *btsnoop_write_file[MAX_SUPPORTED_ADAPTER] = { NULL };
+       time_t t;
+       struct tm tm;
+       unsigned long num_packets = 0;
+
+       btsnoop_read_file = btsnoop_open(input, BTSNOOP_FLAG_PKLG_SUPPORT);
+       if (!btsnoop_read_file)
+               return;
+
+       type = btsnoop_get_format(btsnoop_read_file);
+       if (type != BTSNOOP_FORMAT_MONITOR) {
+               fprintf(stderr, "unsupported link data type %u\n", type);
+               btsnoop_unref(btsnoop_read_file);
+               return;
+       }
+
+next_packet:
+       if (!btsnoop_read_hci(btsnoop_read_file, &tv, &index, &opcode, buf,
+                                                               &pktlen))
+               goto close_files;
+
+       if (opcode == 0xffff || index >= MAX_SUPPORTED_ADAPTER)
+               goto next_packet;
+
+       switch (opcode) {
+       case BTSNOOP_OPCODE_NEW_INDEX:
+               t = tv.tv_sec;
+               localtime_r(&t, &tm);
+
+               if (max_index < index)
+                       max_index = index;
+
+               sprintf(write_file_name, "%s%d_%02d:%02d:%02d.%06lu.log",
+                       BT_SNOOP_TYPE_HCI_PREFIX, index, tm.tm_hour, tm.tm_min,
+                       tm.tm_sec, tv.tv_usec);
+
+               printf("New Index %d would be saved in %s\n", index,
+                                                       write_file_name);
+
+               btsnoop_write_file[index] = btsnoop_create(write_file_name,
+                               BTSNOOP_FORMAT_HCI, -1, -1);
+               if (!btsnoop_write_file[index])
+                       goto close_files;
+
+               break;
+       case BTSNOOP_OPCODE_DEL_INDEX:
+               printf("Del Index %d\n", index);
+
+               btsnoop_unref(btsnoop_write_file[index]);
+               btsnoop_write_file[index] = NULL;
+               break;
+
+       case BTSNOOP_OPCODE_COMMAND_PKT:
+       case BTSNOOP_OPCODE_EVENT_PKT:
+       case BTSNOOP_OPCODE_ACL_TX_PKT:
+       case BTSNOOP_OPCODE_ACL_RX_PKT:
+       case BTSNOOP_OPCODE_SCO_TX_PKT:
+       case BTSNOOP_OPCODE_SCO_RX_PKT:
+               if (!btsnoop_write_file[index]) {
+                       t = tv.tv_sec;
+                       localtime_r(&t, &tm);
+
+                       if (max_index < index)
+                               max_index = index;
+
+                       sprintf(write_file_name, "%s%d_%02d:%02d:%02d.%06lu.log",
+                                       BT_SNOOP_TYPE_HCI_PREFIX, index,
+                                       tm.tm_hour, tm.tm_min,
+                                       tm.tm_sec, tv.tv_usec);
+
+                       printf("New Index %d would be saved in %s\n", index,
+                                       write_file_name);
+
+                       btsnoop_write_file[index] = btsnoop_create(write_file_name,
+                                       BTSNOOP_FORMAT_HCI, -1, -1);
+               }
+
+               if (!btsnoop_write_file[index])
+                       goto close_files;
+               btsnoop_write_hci(btsnoop_write_file[index], &tv, index, 0,
+                                                       opcode, buf, pktlen);
+               break;
+       default:
+               printf("skip btmon opcode(%d)\n",opcode);
+       }
+       num_packets++;
+
+       goto next_packet;
+
+close_files:
+       for (index = 0; index < max_index; index++)
+               btsnoop_unref(btsnoop_write_file[index]);
+
+       btsnoop_unref(btsnoop_read_file);
+
+       printf("BT Snoop data link transfer is completed for %lu packets\n",
+                                                               num_packets);
+}
+#endif
 
 static void command_extract_eir(const char *input)
 {
@@ -518,6 +634,9 @@ static void usage(void)
        printf("commands:\n"
                "\t-m, --merge <output>   Merge multiple btsnoop files\n"
                "\t-e, --extract <input>  Extract data from btsnoop file\n"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               "\t-s, --split <input>    Split btmon file into legacy btsnoop file(s)\n"
+#endif
                "\t-h, --help             Show help options\n");
 }
 
@@ -525,12 +644,19 @@ static const struct option main_options[] = {
        { "merge",   required_argument, NULL, 'm' },
        { "extract", required_argument, NULL, 'e' },
        { "type",    required_argument, NULL, 't' },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { "split",   required_argument, NULL, 's' },
+#endif
        { "version", no_argument,       NULL, 'v' },
        { "help",    no_argument,       NULL, 'h' },
        { }
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+enum { INVALID, MERGE, EXTRACT, SPLIT };
+#else
 enum { INVALID, MERGE, EXTRACT };
+#endif
 
 int main(int argc, char *argv[])
 {
@@ -541,8 +667,11 @@ int main(int argc, char *argv[])
 
        for (;;) {
                int opt;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               opt = getopt_long(argc, argv, "m:e:s:t:vh", main_options, NULL);
+#else
                opt = getopt_long(argc, argv, "m:e:t:vh", main_options, NULL);
+#endif
                if (opt < 0)
                        break;
 
@@ -555,6 +684,11 @@ int main(int argc, char *argv[])
                        command = EXTRACT;
                        input_path = optarg;
                        break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               case 's':
+                       command = SPLIT;
+                       input_path = optarg;
+#endif
                case 't':
                        type = optarg;
                        break;
@@ -600,6 +734,16 @@ int main(int argc, char *argv[])
                        fprintf(stderr, "extract type not supported\n");
                break;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       case SPLIT:
+               if (argc - optind > 0) {
+                       fprintf(stderr, "extra arguments not allowed\n");
+                       return EXIT_FAILURE;
+               }
+
+               command_split(input_path);
+               break;
+#endif
        default:
                usage();
                return EXIT_FAILURE;
diff --git a/tools/example.psr b/tools/example.psr
new file mode 100755 (executable)
index 0000000..bbbec73
--- /dev/null
@@ -0,0 +1,12 @@
+// PSKEY_BDADDR
+&0001 = 0001 2821 005b 6789
+// PSKEY_ANA_FTRIM
+&01f6 = 0025
+// PSKEY_HOST_INTERFACE
+&01f9 = 0001
+// PSKEY_UART_BAUD_RATE
+&0204 = 01d8
+// PSKEY_ANA_FREQ
+&01fe = 0004
+// PSKEY_UART_CONFIG
+&0205 = 0006
diff --git a/tools/gatt-example b/tools/gatt-example
new file mode 100755 (executable)
index 0000000..a6f5cbe
--- /dev/null
@@ -0,0 +1,533 @@
+#!/usr/bin/python
+
+import dbus
+import dbus.exceptions
+import dbus.mainloop.glib
+import dbus.service
+
+import array
+import gobject
+
+from random import randint
+
+mainloop = None
+
+BLUEZ_SERVICE_NAME = 'org.bluez'
+GATT_MANAGER_IFACE = 'org.bluez.GattManager1'
+DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
+
+GATT_SERVICE_IFACE = 'org.bluez.GattService1'
+GATT_CHRC_IFACE = 'org.bluez.GattCharacteristic1'
+GATT_DESC_IFACE = 'org.bluez.GattDescriptor1'
+
+class InvalidArgsException(dbus.exceptions.DBusException):
+    _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
+
+class NotSupportedException(dbus.exceptions.DBusException):
+    _dbus_error_name = 'org.bluez.Error.NotSupported'
+
+class NotPermittedException(dbus.exceptions.DBusException):
+    _dbus_error_name = 'org.bluez.Error.NotPermitted'
+
+class InvalidValueLengthException(dbus.exceptions.DBusException):
+    _dbus_error_name = 'org.bluez.Error.InvalidValueLength'
+
+class FailedException(dbus.exceptions.DBusException):
+    _dbus_error_name = 'org.bluez.Error.Failed'
+
+
+class Service(dbus.service.Object):
+    PATH_BASE = '/org/bluez/example/service'
+
+    def __init__(self, bus, index, uuid, primary):
+        self.path = self.PATH_BASE + str(index)
+        self.bus = bus
+        self.uuid = uuid
+        self.primary = primary
+        self.characteristics = []
+        dbus.service.Object.__init__(self, bus, self.path)
+
+    def get_properties(self):
+        return {
+                GATT_SERVICE_IFACE: {
+                        'UUID': self.uuid,
+                        'Primary': self.primary,
+                        'Characteristics': dbus.Array(
+                                self.get_characteristic_paths(),
+                                signature='o')
+                }
+        }
+
+    def get_path(self):
+        return dbus.ObjectPath(self.path)
+
+    def add_characteristic(self, characteristic):
+        self.characteristics.append(characteristic)
+
+    def get_characteristic_paths(self):
+        result = []
+        for chrc in self.characteristics:
+            result.append(chrc.get_path())
+        return result
+
+    def get_characteristics(self):
+        return self.characteristics
+
+    @dbus.service.method(DBUS_PROP_IFACE,
+                         in_signature='s',
+                         out_signature='a{sv}')
+    def GetAll(self, interface):
+        if interface != GATT_SERVICE_IFACE:
+            raise InvalidArgsException()
+
+        return self.get_properties[GATT_SERVICE_IFACE]
+
+    @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
+    def GetManagedObjects(self):
+        response = {}
+        print 'GetManagedObjects'
+
+        response[self.get_path()] = self.get_properties()
+        chrcs = self.get_characteristics()
+        for chrc in chrcs:
+            response[chrc.get_path()] = chrc.get_properties()
+            descs = chrc.get_descriptors()
+            for desc in descs:
+                response[desc.get_path()] = desc.get_properties()
+
+        return response
+
+
+class Characteristic(dbus.service.Object):
+    def __init__(self, bus, index, uuid, flags, service):
+        self.path = service.path + '/char' + str(index)
+        self.bus = bus
+        self.uuid = uuid
+        self.service = service
+        self.flags = flags
+        self.descriptors = []
+        dbus.service.Object.__init__(self, bus, self.path)
+
+    def get_properties(self):
+        return {
+                GATT_CHRC_IFACE: {
+                        'Service': self.service.get_path(),
+                        'UUID': self.uuid,
+                        'Flags': self.flags,
+                        'Descriptors': dbus.Array(
+                                self.get_descriptor_paths(),
+                                signature='o')
+                }
+        }
+
+    def get_path(self):
+        return dbus.ObjectPath(self.path)
+
+    def add_descriptor(self, descriptor):
+        self.descriptors.append(descriptor)
+
+    def get_descriptor_paths(self):
+        result = []
+        for desc in self.descriptors:
+            result.append(desc.get_path())
+        return result
+
+    def get_descriptors(self):
+        return self.descriptors
+
+    @dbus.service.method(DBUS_PROP_IFACE,
+                         in_signature='s',
+                         out_signature='a{sv}')
+    def GetAll(self, interface):
+        if interface != GATT_CHRC_IFACE:
+            raise InvalidArgsException()
+
+        return self.get_properties[GATT_CHRC_IFACE]
+
+    @dbus.service.method(GATT_CHRC_IFACE, out_signature='ay')
+    def ReadValue(self):
+        print 'Default ReadValue called, returning error'
+        raise NotSupportedException()
+
+    @dbus.service.method(GATT_CHRC_IFACE, in_signature='ay')
+    def WriteValue(self, value):
+        print 'Default WriteValue called, returning error'
+        raise NotSupportedException()
+
+    @dbus.service.method(GATT_CHRC_IFACE)
+    def StartNotify(self):
+        print 'Default StartNotify called, returning error'
+        raise NotSupportedException()
+
+    @dbus.service.method(GATT_CHRC_IFACE)
+    def StopNotify(self):
+        print 'Default StopNotify called, returning error'
+        raise NotSupportedException()
+
+    @dbus.service.signal(DBUS_PROP_IFACE,
+                         signature='sa{sv}as')
+    def PropertiesChanged(self, interface, changed, invalidated):
+        pass
+
+
+class Descriptor(dbus.service.Object):
+    def __init__(self, bus, index, uuid, characteristic):
+        self.path = characteristic.path + '/desc' + str(index)
+        self.bus = bus
+        self.uuid = uuid
+        self.chrc = characteristic
+        dbus.service.Object.__init__(self, bus, self.path)
+
+    def get_properties(self):
+        return {
+                GATT_DESC_IFACE: {
+                        'Characteristic': self.chrc.get_path(),
+                        'UUID': self.uuid,
+                }
+        }
+
+    def get_path(self):
+        return dbus.ObjectPath(self.path)
+
+    @dbus.service.method(DBUS_PROP_IFACE,
+                         in_signature='s',
+                         out_signature='a{sv}')
+    def GetAll(self, interface):
+        if interface != GATT_DESC_IFACE:
+            raise InvalidArgsException()
+
+        return self.get_properties[GATT_CHRC_IFACE]
+
+    @dbus.service.method(GATT_DESC_IFACE, out_signature='ay')
+    def ReadValue(self):
+        print 'Default ReadValue called, returning error'
+        raise NotSupportedException()
+
+    @dbus.service.method(GATT_DESC_IFACE, in_signature='ay')
+    def WriteValue(self, value):
+        print 'Default WriteValue called, returning error'
+        raise NotSupportedException()
+
+
+class HeartRateService(Service):
+    """
+    Fake Heart Rate Service that simulates a fake heart beat and control point
+    behavior.
+
+    """
+    HR_UUID = '0000180d-0000-1000-8000-00805f9b34fb'
+
+    def __init__(self, bus, index):
+        Service.__init__(self, bus, index, self.HR_UUID, True)
+        self.add_characteristic(HeartRateMeasurementChrc(bus, 0, self))
+        self.add_characteristic(BodySensorLocationChrc(bus, 1, self))
+        self.add_characteristic(HeartRateControlPointChrc(bus, 2, self))
+        self.energy_expended = 0
+
+
+class HeartRateMeasurementChrc(Characteristic):
+    HR_MSRMT_UUID = '00002a37-0000-1000-8000-00805f9b34fb'
+
+    def __init__(self, bus, index, service):
+        Characteristic.__init__(
+                self, bus, index,
+                self.HR_MSRMT_UUID,
+                ['notify'],
+                service)
+        self.notifying = False
+        self.hr_ee_count = 0
+
+    def hr_msrmt_cb(self):
+        value = []
+        value.append(dbus.Byte(0x06))
+
+        value.append(dbus.Byte(randint(90, 130)))
+
+        if self.hr_ee_count % 10 == 0:
+            value[0] = dbus.Byte(value[0] | 0x08)
+            value.append(dbus.Byte(self.service.energy_expended & 0xff))
+            value.append(dbus.Byte((self.service.energy_expended >> 8) & 0xff))
+
+        self.service.energy_expended = \
+                min(0xffff, self.service.energy_expended + 1)
+        self.hr_ee_count += 1
+
+        print 'Updating value: ' + repr(value)
+
+        self.PropertiesChanged(GATT_CHRC_IFACE, { 'Value': value }, [])
+
+        return self.notifying
+
+    def _update_hr_msrmt_simulation(self):
+        print 'Update HR Measurement Simulation'
+
+        if not self.notifying:
+            return
+
+        gobject.timeout_add(1000, self.hr_msrmt_cb)
+
+    def StartNotify(self):
+        if self.notifying:
+            print 'Already notifying, nothing to do'
+            return
+
+        self.notifying = True
+        self._update_hr_msrmt_simulation()
+
+    def StopNotify(self):
+        if not self.notifying:
+            print 'Not notifying, nothing to do'
+            return
+
+        self.notifying = False
+        self._update_hr_msrmt_simulation()
+
+
+class BodySensorLocationChrc(Characteristic):
+    BODY_SNSR_LOC_UUID = '00002a38-0000-1000-8000-00805f9b34fb'
+
+    def __init__(self, bus, index, service):
+        Characteristic.__init__(
+                self, bus, index,
+                self.BODY_SNSR_LOC_UUID,
+                ['read'],
+                service)
+
+    def ReadValue(self):
+        # Return 'Chest' as the sensor location.
+        return [ 0x01 ]
+
+class HeartRateControlPointChrc(Characteristic):
+    HR_CTRL_PT_UUID = '00002a39-0000-1000-8000-00805f9b34fb'
+
+    def __init__(self, bus, index, service):
+        Characteristic.__init__(
+                self, bus, index,
+                self.HR_CTRL_PT_UUID,
+                ['write'],
+                service)
+
+    def WriteValue(self, value):
+        print 'Heart Rate Control Point WriteValue called'
+
+        if len(value) != 1:
+            raise InvalidValueLengthException()
+
+        byte = value[0]
+        print 'Control Point value: ' + repr(byte)
+
+        if byte != 1:
+            raise FailedException("0x80")
+
+        print 'Energy Expended field reset!'
+        self.service.energy_expended = 0
+
+
+class BatteryService(Service):
+    """
+    Fake Battery service that emulates a draining battery.
+
+    """
+    BATTERY_UUID = '180f'
+
+    def __init__(self, bus, index):
+        Service.__init__(self, bus, index, self.BATTERY_UUID, True)
+        self.add_characteristic(BatteryLevelCharacteristic(bus, 0, self))
+
+
+class BatteryLevelCharacteristic(Characteristic):
+    """
+    Fake Battery Level characteristic. The battery level is drained by 2 points
+    every 5 seconds.
+
+    """
+    BATTERY_LVL_UUID = '2a19'
+
+    def __init__(self, bus, index, service):
+        Characteristic.__init__(
+                self, bus, index,
+                self.BATTERY_LVL_UUID,
+                ['read', 'notify'],
+                service)
+        self.notifying = False
+        self.battery_lvl = 100
+        gobject.timeout_add(5000, self.drain_battery)
+
+    def notify_battery_level(self):
+        if not self.notifying:
+            return
+        self.PropertiesChanged(
+                GATT_CHRC_IFACE,
+                { 'Value': [dbus.Byte(self.battery_lvl)] }, [])
+
+    def drain_battery(self):
+        if self.battery_lvl > 0:
+            self.battery_lvl -= 2
+            if self.battery_lvl < 0:
+                self.battery_lvl = 0
+        print 'Battery Level drained: ' + repr(self.battery_lvl)
+        self.notify_battery_level()
+        return True
+
+    def ReadValue(self):
+        print 'Battery Level read: ' + repr(self.battery_lvl)
+        return [dbus.Byte(self.battery_lvl)]
+
+    def StartNotify(self):
+        if self.notifying:
+            print 'Already notifying, nothing to do'
+            return
+
+        self.notifying = True
+        self.notify_battery_level()
+
+    def StopNotify(self):
+        if not self.notifying:
+            print 'Not notifying, nothing to do'
+            return
+
+        self.notifying = False
+
+
+class TestService(Service):
+    """
+    Dummy test service that provides characteristics and descriptors that
+    exercise various API functionality.
+
+    """
+    TEST_SVC_UUID = '12345678-1234-5678-1234-56789abcdef0'
+
+    def __init__(self, bus, index):
+        Service.__init__(self, bus, index, self.TEST_SVC_UUID, False)
+        self.add_characteristic(TestCharacteristic(bus, 0, self))
+
+
+class TestCharacteristic(Characteristic):
+    """
+    Dummy test characteristic. Allows writing arbitrary bytes to its value, and
+    contains "extended properties", as well as a test descriptor.
+
+    """
+    TEST_CHRC_UUID = '12345678-1234-5678-1234-56789abcdef1'
+
+    def __init__(self, bus, index, service):
+        Characteristic.__init__(
+                self, bus, index,
+                self.TEST_CHRC_UUID,
+                ['read', 'write', 'writable-auxiliaries'],
+                service)
+        self.value = []
+        self.add_descriptor(TestDescriptor(bus, 0, self))
+        self.add_descriptor(
+                CharacteristicUserDescriptionDescriptor(bus, 1, self))
+
+    def ReadValue(self):
+        print 'TestCharacteristic Read: ' + repr(self.value)
+        return self.value
+
+    def WriteValue(self, value):
+        print 'TestCharacteristic Write: ' + repr(value)
+        self.value = value
+
+
+class TestDescriptor(Descriptor):
+    """
+    Dummy test descriptor. Returns a static value.
+
+    """
+    TEST_DESC_UUID = '12345678-1234-5678-1234-56789abcdef2'
+
+    def __init__(self, bus, index, characteristic):
+        Descriptor.__init__(
+                self, bus, index,
+                self.TEST_DESC_UUID,
+                characteristic)
+
+    def ReadValue(self):
+        return [
+                dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
+        ]
+
+
+class CharacteristicUserDescriptionDescriptor(Descriptor):
+    """
+    Writable CUD descriptor.
+
+    """
+    CUD_UUID = '2901'
+
+    def __init__(self, bus, index, characteristic):
+        self.writable = 'writable-auxiliaries' in characteristic.flags
+        self.value = array.array('B', 'This is a characteristic for testing')
+        self.value = self.value.tolist()
+        Descriptor.__init__(
+                self, bus, index,
+                self.CUD_UUID,
+                characteristic)
+
+    def ReadValue(self):
+        return self.value
+
+    def WriteValue(self, value):
+        if not self.writable:
+            raise NotPermittedException()
+        self.value = value
+
+
+def register_service_cb():
+    print 'GATT service registered'
+
+
+def register_service_error_cb(error):
+    print 'Failed to register service: ' + str(error)
+    mainloop.quit()
+
+
+def find_adapter(bus):
+    remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
+                               DBUS_OM_IFACE)
+    objects = remote_om.GetManagedObjects()
+
+    for o, props in objects.iteritems():
+        if props.has_key(GATT_MANAGER_IFACE):
+            return o
+
+    return None
+
+def main():
+    global mainloop
+
+    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+    bus = dbus.SystemBus()
+
+    adapter = find_adapter(bus)
+    if not adapter:
+        print 'GattManager1 interface not found'
+        return
+
+    service_manager = dbus.Interface(
+            bus.get_object(BLUEZ_SERVICE_NAME, adapter),
+            GATT_MANAGER_IFACE)
+
+    hr_service = HeartRateService(bus, 0)
+    bat_service = BatteryService(bus, 1)
+    test_service = TestService(bus, 2)
+
+    mainloop = gobject.MainLoop()
+
+    service_manager.RegisterService(hr_service.get_path(), {},
+                                    reply_handler=register_service_cb,
+                                    error_handler=register_service_error_cb)
+    service_manager.RegisterService(bat_service.get_path(), {},
+                                    reply_handler=register_service_cb,
+                                    error_handler=register_service_error_cb)
+    service_manager.RegisterService(test_service.get_path(), {},
+                                    reply_handler=register_service_cb,
+                                    error_handler=register_service_error_cb)
+
+    mainloop.run()
+
+if __name__ == '__main__':
+    main()
index fad176c..0ba56e7 100755 (executable)
@@ -27,6 +27,9 @@
 #include <config.h>
 #endif
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+#define _GNU_SOURCE
+#endif
 #include <stdio.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -49,6 +52,7 @@
 #include "src/shared/tty.h"
 
 #include "hciattach.h"
+#include "../profile.h"
 
 struct uart_t {
        char *type;
@@ -62,8 +66,26 @@ struct uart_t {
        char *bdaddr;
        int  (*init) (int fd, struct uart_t *u, struct termios *ti);
        int  (*post) (int fd, struct uart_t *u, struct termios *ti);
+
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined __TI_PATCH__
+       uint16_t device_param;
+#endif
 };
 
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined __TI_PATCH__
+       int firmware_path = 0;
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#if defined(__TI_PATCH__) || 1
+#define TIOSETBRFPOWER         0x6000
+#define BRF_DEEP_SLEEP_OPCODE_BYTE_1   0x0c
+#define BRF_DEEP_SLEEP_OPCODE_BYTE_2   0xfd
+#define BRF_DEEP_SLEEP_OPCODE          \
+       (BRF_DEEP_SLEEP_OPCODE_BYTE_1 | (BRF_DEEP_SLEEP_OPCODE_BYTE_2 << 8))
+#endif
+#endif
 #define FLOW_CTL       0x0001
 #define AMP_DEV                0x0002
 #define ENABLE_PM      1
@@ -946,40 +968,78 @@ static int bcm2035(int fd, struct uart_t *u, struct termios *ti)
        /* Set the baud rate */
        memset(cmd, 0, sizeof(cmd));
        memset(resp, 0, sizeof(resp));
-       cmd[0] = HCI_COMMAND_PKT;
-       cmd[1] = 0x18;
-       cmd[2] = 0xfc;
-       cmd[3] = 0x02;
-       switch (u->speed) {
-       case 57600:
-               cmd[4] = 0x00;
-               cmd[5] = 0xe6;
-               break;
-       case 230400:
-               cmd[4] = 0x22;
-               cmd[5] = 0xfa;
-               break;
-       case 460800:
-               cmd[4] = 0x22;
-               cmd[5] = 0xfd;
-               break;
-       case 921600:
-               cmd[4] = 0x55;
-               cmd[5] = 0xff;
-               break;
-       default:
-               /* Default is 115200 */
+
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
+       if (TIZEN_FEATURE_BLUEZ_BRCM_CHIP) {
+               cmd[0] = HCI_COMMAND_PKT;
+               cmd[1] = 0x18;
+               cmd[2] = 0xfc;
+               cmd[3] = 0x02;
+               switch (u->speed) {
+               case 57600:
+                       cmd[4] = 0x00;
+                       cmd[5] = 0xe6;
+                       break;
+               case 230400:
+                       cmd[4] = 0x22;
+                       cmd[5] = 0xfa;
+                       break;
+               case 460800:
+                       cmd[4] = 0x22;
+                       cmd[5] = 0xfd;
+                       break;
+               case 921600:
+                       cmd[4] = 0x55;
+                       cmd[5] = 0xff;
+                       break;
+               default:
+                       /* Default is 115200 */
+                       cmd[4] = 0x00;
+                       cmd[5] = 0xf3;
+                       break;
+               }
+               fprintf(stderr, "Baud rate parameters: DHBR=0x%2x,DLBR=0x%2x\n",
+                       cmd[4], cmd[5]);
+
+               /* Send command */
+               if (write(fd, cmd, 6) != 6) {
+                       fprintf(stderr, "Failed to write \"set baud rate\" command\n");
+                       return -1;
+               }
+       } else
+#endif
+       {
+               cmd[0] = HCI_COMMAND_PKT;
+               cmd[1] = 0x18;
+               cmd[2] = 0xfc;
+
+               switch (u->speed) {
+               case 57600:
+               case 230400:
+               case 460800:
+               case 921600:
+               case 3000000:
+                       break;
+               default:
+                       break;
+               }
+
+               cmd[3] = 0x06;
                cmd[4] = 0x00;
-               cmd[5] = 0xf3;
-               break;
-       }
-       fprintf(stderr, "Baud rate parameters: DHBR=0x%2x,DLBR=0x%2x\n",
-               cmd[4], cmd[5]);
+               cmd[5] = 0x00;
+               cmd[6] = u->speed & 0xFF;
+               cmd[7] = (u->speed >> 8) & 0xFF;
+               cmd[8] = (u->speed >> 16) & 0xFF;
+               cmd[9] = (u->speed >> 24) & 0xFF;
 
-       /* Send command */
-       if (write(fd, cmd, 6) != 6) {
-               fprintf(stderr, "Failed to write \"set baud rate\" command\n");
-               return -1;
+               fprintf(stderr, "Set the baud rate %d : 0x%02x,0x%02x,0x%02x,0x%02x\n",u->speed,cmd[6],cmd[7],cmd[8],cmd[9] );
+
+               /* Send command */
+               if (write(fd, cmd, 10) != 10) {
+                       fprintf(stderr, "Failed to write \"set baud rate\" command\n");
+                       return -1;
+               }
        }
 
        if ((n = read_hci_event(fd, resp, 6)) < 0) {
@@ -990,10 +1050,21 @@ static int bcm2035(int fd, struct uart_t *u, struct termios *ti)
        return 0;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY /*SPRD add Start*/
+static int init_sprd_config(int fd, struct uart_t *u, struct termios *ti)
+{
+
+       return sprd_config_init(fd, u->bdaddr, ti);
+}
+#endif
+
 struct uart_t uart[] = {
        { "any",        0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
                                FLOW_CTL, DISABLE_PM, NULL, NULL     },
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY /*SPRD*/
+       { "sprd",        0x0000, 0x0000, HCI_UART_H4,   3000000, 3000000,
+                               FLOW_CTL, DISABLE_PM, NULL, init_sprd_config },
+#endif
        { "ericsson",   0x0000, 0x0000, HCI_UART_H4,   57600,  115200,
                                FLOW_CTL, DISABLE_PM, NULL, ericsson },
 
@@ -1019,12 +1090,18 @@ struct uart_t uart[] = {
        { "swave",      0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
                                FLOW_CTL, DISABLE_PM, NULL, swave    },
 
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined __TI_PATCH__
+       /* Texas Instruments BRF63xx modules */
+       { "texas",      0x0000, 0x0000, HCI_UART_LL,   115200,3000000, FLOW_CTL, NULL, texas,    NULL/*texas_continue_script*/,    BRF_DEEP_SLEEP_OPCODE},
+#else
        /* Texas Instruments Bluelink (BRF) modules */
        { "texas",      0x0000, 0x0000, HCI_UART_LL,   115200, 115200,
                                FLOW_CTL, DISABLE_PM, NULL, texas,    texas2 },
 
        { "texasalt",   0x0000, 0x0000, HCI_UART_LL,   115200, 115200,
                                FLOW_CTL, DISABLE_PM, NULL, texasalt, NULL   },
+#endif
 
        /* ST Microelectronics minikits based on STLC2410/STLC2415 */
        { "st",         0x0000, 0x0000, HCI_UART_H4,    57600, 115200,
@@ -1132,6 +1209,46 @@ static struct uart_t * get_by_type(char *type)
        return NULL;
 }
 
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+static int enable_hci(char *dev, struct uart_t *u)
+{
+       int fd, i;
+       unsigned long flags = 0;
+
+       fd = open(dev, O_RDWR | O_NOCTTY);
+       if (fd < 0) {
+               fprintf(stderr, "Can't open serial port");
+               return -1;
+       }
+
+       tcflush(fd, TCIOFLUSH);
+
+       /* Set TTY to N_HCI line discipline */
+       i = N_HCI;
+       if (ioctl(fd, TIOCSETD, &i) < 0) {
+               fprintf(stderr, "Can't set line discipline");
+               close(fd);
+               return -1;
+       }
+
+       if (flags && ioctl(fd, HCIUARTSETFLAGS, flags) < 0) {
+               fprintf(stderr, "Can't set UART flags");
+               close(fd);
+               return -1;
+       }
+
+       tcflush(fd, TCIOFLUSH);
+
+       if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {
+               fprintf(stderr, "Can't set device");
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
 /* Initialize UART driver */
 static int init_uart(char *dev, struct uart_t *u, int send_break, int raw)
 {
@@ -1139,6 +1256,13 @@ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw)
        int fd, i;
        unsigned long flags = 0;
 
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#if defined(__TI_PATCH__) || 1
+       int power;
+#endif
+#endif
+
        if (raw)
                flags |= 1 << HCI_UART_RAW_DEVICE;
 
@@ -1160,11 +1284,16 @@ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw)
 
        cfmakeraw(&ti);
 
-       ti.c_cflag |= CLOCAL;
-       if (u->flags & FLOW_CTL)
-               ti.c_cflag |= CRTSCTS;
-       else
-               ti.c_cflag &= ~CRTSCTS;
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
+       if (!TIZEN_FEATURE_BLUEZ_BRCM_CHIP) {
+               ti.c_cflag |= CLOCAL;
+               if (u->flags & FLOW_CTL)
+                       ti.c_cflag |= CRTSCTS;
+               else
+                       ti.c_cflag &= ~CRTSCTS;
+       }
+#endif
 
        if (tcsetattr(fd, TCSANOW, &ti) < 0) {
                perror("Can't set port settings");
@@ -1184,6 +1313,24 @@ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw)
                usleep(500000);
        }
 
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#if defined(__TI_PATCH__)
+       /* Power up the BRF chip */
+       power = 1;
+       ioctl(fd, TIOSETBRFPOWER, &power);
+#else
+       if (TIZEN_FEATURE_BLUEZ_BRCM_CHIP) {
+               /* Power up the BRF chip */
+               power = 1;
+               ioctl(fd, TIOSETBRFPOWER, &power);
+       }
+#endif
+#ifdef __TI_PATCH__
+       usleep(500000);
+#endif
+#endif
+
        if (u->init && u->init(fd, u, &ti) < 0)
                goto fail;
 
@@ -1212,8 +1359,10 @@ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw)
                goto fail;
        }
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        if (u->post && u->post(fd, u, &ti) < 0)
                goto fail;
+#endif
 
        return fd;
 
@@ -1226,33 +1375,69 @@ static void usage(void)
 {
        printf("hciattach - HCI UART driver initialization utility\n");
        printf("Usage:\n");
+
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && !defined(__TI_PATCH__)
+/* This commented code was present before bluez 5.25 upgrade
+ * printf("\thciattach [-n] [-p] [-b] [-g device_param] [-r] [-f] [-t timeout] [-s initial_speed] <tty> <type | id> [speed] [flow|noflow] [bdaddr]\n");*/
+       printf("\thciattach [-n] [-p] [-b] [-g device_param] [-r] [-f]"
+                       " [-t timeout] [-s initial_speed]"
+                       " <tty> <type | id> [speed] [flow|noflow]"
+                       " [sleep|nosleep] [bdaddr]\n");
+#else
        printf("\thciattach [-n] [-p] [-b] [-r] [-t timeout] [-s initial_speed]"
                        " <tty> <type | id> [speed] [flow|noflow]"
                        " [sleep|nosleep] [bdaddr]\n");
+#endif
        printf("\thciattach -l\n");
 }
 
 int main(int argc, char *argv[])
 {
        struct uart_t *u = NULL;
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
        int detach, printpid, raw, opt, i, n, ld, err;
+#else
+       int detach, printpid, opt, i, n, ld, err;
+#endif
        int to = 10;
        int init_speed = 0;
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
        int send_break = 0;
+#endif
        pid_t pid;
        struct sigaction sa;
        struct pollfd p;
        sigset_t sigs;
        char dev[PATH_MAX];
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#if defined(__TI_PATCH__) || 1
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+       int power;
+#endif
+#endif
+#ifdef __TI_PATCH__
+       uint16_t device_param = 0;
+       int reset_device = 0;
+       int bt_fd;
+#endif
        detach = 1;
        printpid = 0;
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
        raw = 0;
-
+#endif
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && !defined(__TI_PATCH__)
+       while ((opt=getopt(argc, argv, "bnprft:g:s:l")) != EOF) {
+#else
        while ((opt=getopt(argc, argv, "bnpt:s:lr")) != EOF) {
+#endif
                switch(opt) {
                case 'b':
-                       send_break = 1;
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
+                       if (!TIZEN_FEATURE_BLUEZ_BRCM_CHIP)
+                               send_break = 1;
+#endif
                        break;
 
                case 'n':
@@ -1267,6 +1452,20 @@ int main(int argc, char *argv[])
                        to = atoi(optarg);
                        break;
 
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && defined(__TI_PATCH__)
+               case 'g':
+                       device_param = (uint16_t)strtol(optarg, NULL, 16);
+                       break;
+
+               case 'r':
+                       reset_device = 1;
+                       break;
+
+               case 'f':
+                       firmware_path = 1;
+                       break;
+#endif
                case 's':
                        init_speed = atoi(optarg);
                        break;
@@ -1279,7 +1478,10 @@ int main(int argc, char *argv[])
                        exit(0);
 
                case 'r':
-                       raw = 1;
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
+                       if (!TIZEN_FEATURE_BLUEZ_BRCM_CHIP)
+                               raw = 1;
+#endif
                        break;
 
                default:
@@ -1289,6 +1491,10 @@ int main(int argc, char *argv[])
        }
 
        n = argc - optind;
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && defined(__TI_PATCH__)
+       if (!reset_device || (reset_device && n < 1))
+#endif
        if (n < 2) {
                usage();
                exit(1);
@@ -1352,16 +1558,42 @@ int main(int argc, char *argv[])
                        break;
                }
        }
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && defined(__TI_PATCH__)
+       if (reset_device)
+       {
+               // Reset row device
+               bt_fd = open(dev, O_RDWR | O_NOCTTY);
+               if (bt_fd< 0) {
+                       perror("Can't open serial port");
+                       return -1;
+               }
+               /* Power up the BRF chip */
+               power = 0;
+               ioctl(bt_fd, TIOSETBRFPOWER, &power);
+               return 0;
+       }
+#endif
 
        if (!u) {
                fprintf(stderr, "Unknown device type or id\n");
                exit(1);
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+//     __hci_attach_log_init();
+#endif
+
        /* If user specified a initial speed, use that instead of
           the hardware's default */
        if (init_speed)
                u->init_speed = init_speed;
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && defined(__TI_PATCH__)
+       /* If user specified a device parameter, use that instead of
+          the hardware's default */
+       if (device_param)
+               u->device_param = device_param;
+#endif
 
        memset(&sa, 0, sizeof(sa));
        sa.sa_flags   = SA_NOCLDSTOP;
@@ -1371,8 +1603,14 @@ int main(int argc, char *argv[])
        /* 10 seconds should be enough for initialization */
        alarm(to);
        bcsp_max_retries = to;
-
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+       if (TIZEN_FEATURE_BLUEZ_BRCM_CHIP)
+               n = enable_hci(dev, u);
+       else
+               n = init_uart(dev, u, send_break, raw);
+#else
        n = init_uart(dev, u, send_break, raw);
+#endif
        if (n < 0) {
                perror("Can't initialize device");
                exit(1);
@@ -1433,5 +1671,19 @@ int main(int argc, char *argv[])
                exit(1);
        }
 
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#if defined(__TI_PATCH__)
+       /* Power down the BRF or BCMchip */
+       power = 0;
+       ioctl(n, TIOSETBRFPOWER, &power);
+#else
+       if (TIZEN_FEATURE_BLUEZ_BRCM_CHIP) {
+               /* Power down the BRF or BCMchip */
+               power = 0;
+               ioctl(n, TIOSETBRFPOWER, &power);
+       }
+#endif
+#endif
        return 0;
 }
index 249aab4..9c94965 100755 (executable)
@@ -64,6 +64,9 @@ int stlc2500_init(int fd, bdaddr_t *bdaddr);
 int bgb2xx_init(int dd, bdaddr_t *bdaddr);
 int ath3k_init(int fd, int speed, int init_speed, char *bdaddr,
                                                struct termios *ti);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int sprd_config_init(int fd, char *bdaddr, struct termios *ti);
+#endif
 int ath3k_post(int fd, int pm);
 int qualcomm_init(int fd, int speed, struct termios *ti, const char *bdaddr);
 int intel_init(int fd, int init_speed, int *speed, struct termios *ti);
index 81f38cb..af03538 100755 (executable)
 #include "hciattach.h"
 
 #ifndef FIRMWARE_DIR
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define FIRMWARE_DIR "/usr/etc/bluetooth"
+#else
 #define FIRMWARE_DIR "/etc/firmware"
 #endif
+#endif
 
 #define FW_EXT ".hcd"
 
diff --git a/tools/hciattach_sprd.c b/tools/hciattach_sprd.c
new file mode 100755 (executable)
index 0000000..6fd2954
--- /dev/null
@@ -0,0 +1,592 @@
+#include <linux/kernel.h>
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+#include <sys/stat.h>
+
+#include "hciattach_sprd.h"
+
+//#include <android/log.h>
+//#define DBG
+#ifdef DBG
+#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "pskey_bt", __VA_ARGS__)
+#else
+#define LOGD(fmt, arg...)  fprintf(stderr, "%s:%d()" fmt "\n", __FILE__,__LINE__, ## arg)
+#endif
+typedef unsigned char   UINT8;
+
+#define UINT32_TO_STREAM(p, u32) {*(p)++ = (UINT8)(u32); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 24);}
+#define UINT24_TO_STREAM(p, u24) {*(p)++ = (UINT8)(u24); *(p)++ = (UINT8)((u24) >> 8); *(p)++ = (UINT8)((u24) >> 16);}
+#define UINT16_TO_STREAM(p, u16) {*(p)++ = (UINT8)(u16); *(p)++ = (UINT8)((u16) >> 8);}
+#define UINT8_TO_STREAM(p, u8)   {*(p)++ = (UINT8)(u8);}
+#define INT8_TO_STREAM(p, u8)    {*(p)++ = (INT8)(u8);}
+
+#define PSKEY_PRELOAD_SIZE    0x04
+#define PSKEY_PREAMBLE_SIZE    0xA2
+
+       // for bt mac addr
+#define BT_MAC_FILE_PATH       "/csa/bluetooth/"
+#define DATMISC_MAC_ADDR_PATH  BT_MAC_FILE_PATH".bd_addr"
+#define MAC_ADDR_BUF_LEN    (strlen("FF:FF:FF:FF:FF:FF"))
+#define MAC_ADDR_FILE_LEN    25
+#define MAC_ADDR_LEN    6
+
+#define BD_ADDR_LEN    14
+#define BD_PREFIX      "0002\n"
+
+#if 0
+#ifndef VENDOR_BTWRITE_PROC_NODE
+#define VENDOR_BTWRITE_PROC_NODE "/proc/bluetooth/sleep/btwrite"
+#endif
+#endif
+
+#define MAX_BT_TMP_PSKEY_FILE_LEN 2048
+
+typedef unsigned int   UWORD32;
+typedef unsigned short UWORD16;
+typedef unsigned char  UWORD8;
+
+#define down_bt_is_space(c)    (((c) == '\n') || ((c) == ',') || ((c) == '\r') || ((c) == ' ') || ((c) == '{') || ((c) == '}'))
+#define down_bt_is_comma(c)    (((c) == ','))
+#define down_bt_is_endc(c)     (((c) == '}')) // indicate end of data
+
+/* Macros to swap byte order */
+#define SWAP_BYTE_ORDER_WORD(val) ((((val) & 0x000000FF) << 24) + \
+                                   (((val) & 0x0000FF00) << 8)  + \
+                                   (((val) & 0x00FF0000) >> 8)   + \
+                                   (((val) & 0xFF000000) >> 24))
+#define INLINE static __inline
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN
+#endif
+
+
+// pskey file structure default value
+static BT_PSKEY_CONFIG_T bt_para_setting={
+       .pskey_cmd = 0x001C0101,
+
+       .g_dbg_source_sink_syn_test_data = 0,
+       .g_sys_sleep_in_standby_supported = 0,
+       .g_sys_sleep_master_supported = 0,
+       .g_sys_sleep_slave_supported = 0,
+
+       .default_ahb_clk = 26000000,
+       .device_class = 0x001F00,
+       .win_ext = 30,
+
+       .g_aGainValue = {0x0000F600, 0x0000D000, 0x0000AA00, 0x00008400, 0x00004400, 0x00000A00},
+       .g_aPowerValue = {0x0FC80000, 0x0FF80000, 0x0FDA0000, 0x0FCC0000, 0x0FFC0000},
+
+       .feature_set = {0xFF, 0xFF, 0x8D, 0xFE, 0x9B, 0x7F, 0x79, 0x83, 0xFF, 0xA7, 0xFF, 0x7F, 0x00, 0xE0, 0xF7, 0x3E},
+       .device_addr = {0x6A, 0x6B, 0x8C, 0x8A, 0x8B, 0x8C},
+
+       .g_sys_sco_transmit_mode = 0, //true tramsmit by uart, otherwise by share memory
+       .g_sys_uart0_communication_supported = 1, //true use uart0, otherwise use uart1 for debug
+       .edr_tx_edr_delay = 5,
+       .edr_rx_edr_delay = 14,
+
+       .g_wbs_nv_117 = 0x0031,
+
+       .is_wdg_supported = 0,
+
+       .share_memo_rx_base_addr = 0,
+       //.share_memo_tx_base_addr = 0,
+       .g_wbs_nv_118 = 0x0066,
+       .g_nbv_nv_117 = 0x1063,
+
+       .share_memo_tx_packet_num_addr = 0,
+       .share_memo_tx_data_base_addr = 0,
+
+       .g_PrintLevel = 0xFFFFFFFF,
+
+       .share_memo_tx_block_length = 0,
+       .share_memo_rx_block_length = 0,
+       .share_memo_tx_water_mark = 0,
+       //.share_memo_tx_timeout_value = 0,
+       .g_nbv_nv_118 = 0x0E45,
+
+       .uart_rx_watermark = 48,
+       .uart_flow_control_thld = 63,
+       .comp_id = 0,
+       .pcm_clk_divd = 0x26,
+
+
+       .reserved = {0}
+};
+
+extern int getPskeyFromFile(void *pData);
+extern int bt_getPskeyFromFile(void *pData);
+
+static int create_mac_folder(void)
+{
+       DIR *dp;
+       int err;
+
+       dp = opendir(BT_MAC_FILE_PATH);
+       if (dp == NULL) {
+               if (mkdir(BT_MAC_FILE_PATH, 0755) < 0) {
+                       err = -errno;
+                       LOGD("%s:  mkdir: %s(%d)",__FUNCTION__, strerror(-err), -err);
+               }
+               return -1;
+       }
+
+       closedir(dp);
+       return 0;
+}
+
+static void mac_rand(char *btmac)
+{
+       int ran;
+       int i;
+       unsigned int seed;
+       struct timeval tv;
+
+       memcpy(btmac, BD_PREFIX, 5);
+       i = gettimeofday(&tv, NULL);
+
+       if (i < 0) {
+               LOGD("Fail to call gettimeofday()");
+               seed = time(NULL);
+       } else
+               seed = (unsigned int)tv.tv_usec;
+
+       for (i = 5; i < BD_ADDR_LEN; i++) {
+               if (i == 7) {
+                       btmac[i] = '\n';
+                       continue;
+               }
+               ran = rand_r(&seed) % 16;
+               if (ran < 10)
+                       ran += 0x30;
+               else
+                       ran += 0x57;
+               btmac[i] = ran;
+       }
+       LOGD("Random number is\r\n");
+       for (i = 0; i < BD_ADDR_LEN; i++) {
+               LOGD("%c", btmac[i]);
+       }
+       LOGD("\r\n");
+}
+
+static void write_btmac2file(char *btmac)
+{
+       int fd;
+       int ret;
+       fd = open(DATMISC_MAC_ADDR_PATH, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
+       LOGD("write_btmac2file open file, fd=%d", fd);
+       if(fd >= 0) {
+               if(chmod(DATMISC_MAC_ADDR_PATH,0666) != -1){
+                       ret = write(fd, btmac, strlen(btmac));
+                       if (ret < strlen(btmac)) {
+                               LOGD("Fail to write %s", DATMISC_MAC_ADDR_PATH);
+                               close(fd);
+                               return;
+                       }
+               }
+               close(fd);
+       }else{
+               LOGD("write bt mac to file failed!!");
+       }
+}
+
+uint8 ConvertHexToBin(
+               uint8        *hex_ptr,     // in: the hexadecimal format string
+               uint16       length,       // in: the length of hexadecimal string
+               uint8        *bin_ptr      // out: pointer to the binary format string
+               ){
+       uint8        *dest_ptr = bin_ptr;
+       uint32        i = 0;
+       uint8        ch;
+
+       for(i=0; i<length; i+=2){
+               // the bit 8,7,6,5
+               ch = hex_ptr[i];
+               // digital 0 - 9
+               if (ch >= '0' && ch <= '9')
+                       *dest_ptr =(uint8)((ch - '0') << 4);
+               // a - f
+               else if (ch >= 'a' && ch <= 'f')
+                       *dest_ptr = (uint8)((ch - 'a' + 10) << 4);
+               // A - F
+               else if (ch >= 'A' && ch <= 'F')
+                       *dest_ptr = (uint8)((ch -'A' + 10) << 4);
+               else{
+                       return 0;
+               }
+
+               // the bit 1,2,3,4
+               ch = hex_ptr[i+1];
+               // digtial 0 - 9
+               if (ch >= '0' && ch <= '9')
+                       *dest_ptr |= (uint8)(ch - '0');
+               // a - f
+               else if (ch >= 'a' && ch <= 'f')
+                       *dest_ptr |= (uint8)(ch - 'a' + 10);
+               // A - F
+               else if (ch >= 'A' && ch <= 'F')
+                       *dest_ptr |= (uint8)(ch -'A' + 10);
+               else{
+                       return 0;
+               }
+
+               dest_ptr++;
+       }
+
+       return 1;
+}
+
+static int read_mac_address(char *file_name, uint8 *addr) {
+       char buf[MAC_ADDR_FILE_LEN] = {0};
+       uint32 addr_t[MAC_ADDR_LEN] = {0};
+       int i = 0;
+
+
+#if 1
+       int fd = open(file_name, O_RDONLY, 0666);
+       LOGD("%s read file: %s", __func__, file_name);
+       if (fd < 0) {
+               LOGD("%s open %s error reason: %s", __func__, file_name, strerror(errno));
+               return -1;
+       }
+       if (read(fd, buf, BD_ADDR_LEN) < 0) {
+               LOGD("%s read %s error reason: %s", __func__, file_name, strerror(errno));
+               goto done;
+       }
+       if (sscanf(buf, "%02X%02X\n%02X\n%02X%02X%02X", &addr_t[0], &addr_t[1], &addr_t[2], &addr_t[3], &addr_t[4], &addr_t[5]) < 0) {
+               LOGD("%s sscanf %s error reason: %s", __func__, file_name, strerror(errno));
+               goto done;
+       }
+
+       for (i = 0; i < MAC_ADDR_LEN; i++) {
+               addr[i] = addr_t[i] & 0xFF;
+       }
+       LOGD("%s %s addr: [%02X:%02X:%02X:%02X:%02X:%02X]", __func__, file_name, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+
+done:
+       close(fd);
+#endif
+       return 0;
+}
+
+static void mac_address_stream_compose(uint8 *addr) {
+       uint8 tmp, i, j;
+       for (i = 0, j = MAC_ADDR_LEN - 1; (i < MAC_ADDR_LEN / 2) && (i != j); i++, j--) {
+               tmp = addr[i];
+               addr[i] = addr[j];
+               addr[j] = tmp;
+       }
+}
+
+#if 0
+/*
+ * random bluetooth mac address
+ */
+static void random_mac_addr(uint8 *addr) {
+       int fd, randseed, ret, mac_rd;
+       uint8 addr_t[MAC_ADDR_LEN] = {0};
+
+       LOGD("%s", __func__);
+       /* urandom seed build */
+       fd = open("/dev/urandom", O_RDONLY);
+       if (fd < 0){
+               LOGD("%s: open urandom fail", __func__);
+       } else {
+               ret = read(fd, &randseed, sizeof(randseed));
+               LOGD("%s urandom:0x%08X", __func__, randseed);
+               close(fd);
+       }
+
+       /* time seed build */
+       if (fd < 0 || ret < 0) {
+               struct timeval tt;
+               if (gettimeofday(&tt, (struct timezone *)0) > 0) {
+                       randseed = (unsigned int) tt.tv_usec;
+               } else {
+                       randseed = (unsigned int) time(NULL);
+               }
+               LOGD("urandom fail, using system time for randseed");
+       }
+
+       LOGD("%s: randseed = %u",__func__, randseed);
+       srand(randseed);
+       mac_rd = rand();
+
+       addr_t[0] = 0x40; /* FOR */
+       addr_t[1] = 0x45; /* SPRD */
+       addr_t[2] = 0xDA; /* ADDR */
+       addr_t[3] = (uint8)(mac_rd & 0xFF);
+       addr_t[4] = (uint8)((mac_rd >> 8) & 0xFF);
+       addr_t[5] = (uint8)((mac_rd >> 16) & 0xFF);
+
+       memcpy(addr, addr_t, MAC_ADDR_LEN);
+       LOGD("%s: MAC ADDR: [%02X:%02X:%02X:%02X:%02X:%02X]",__func__, addr_t[0], addr_t[1], addr_t[2], addr_t[3], addr_t[4], addr_t[5]);
+}
+#endif
+static void get_mac_address(uint8 *addr){
+       int ret = -1;
+       uint8 addr_t[6] = {0};
+       char bt_mac[BD_ADDR_LEN] = {0, };
+
+       LOGD("%s", __func__);
+       /* check misc mac file exist */
+       ret = access(DATMISC_MAC_ADDR_PATH, F_OK);
+       if (ret != 0) {
+               LOGD("%s %s miss", __func__, DATMISC_MAC_ADDR_PATH);
+
+               /* Try to make bt address file */
+               create_mac_folder();
+
+               mac_rand(bt_mac);
+               LOGD("bt random mac=%s",bt_mac);
+               write_btmac2file(bt_mac);
+
+       }
+
+       /* read mac file */
+       read_mac_address(DATMISC_MAC_ADDR_PATH, addr_t);
+
+       /* compose mac stream */
+       mac_address_stream_compose(addr_t);
+
+       memcpy(addr, addr_t, MAC_ADDR_LEN);
+
+}
+
+
+/*
+ * hci command preload stream,  special order
+ */
+static void pskey_stream_compose(uint8 * buf, BT_PSKEY_CONFIG_T *bt_par) {
+       int i = 0;
+       uint8 *p = buf;
+
+       LOGD("%s", __func__);
+
+       UINT24_TO_STREAM(p, bt_par->pskey_cmd);
+       UINT8_TO_STREAM(p, (uint8)(PSKEY_PREAMBLE_SIZE & 0xFF));
+
+       UINT8_TO_STREAM(p, bt_par->g_dbg_source_sink_syn_test_data);
+       UINT8_TO_STREAM(p, bt_par->g_sys_sleep_in_standby_supported);
+       UINT8_TO_STREAM(p, bt_par->g_sys_sleep_master_supported);
+       UINT8_TO_STREAM(p, bt_par->g_sys_sleep_slave_supported);
+
+       UINT32_TO_STREAM(p, bt_par->default_ahb_clk);
+       UINT32_TO_STREAM(p, bt_par->device_class);
+       UINT32_TO_STREAM(p, bt_par->win_ext);
+
+       for (i = 0; i < 6; i++) {
+               UINT32_TO_STREAM(p, bt_par->g_aGainValue[i]);
+       }
+       for (i = 0; i < 5; i++) {
+               UINT32_TO_STREAM(p, bt_par->g_aPowerValue[i]);
+       }
+
+       for (i = 0; i < 16; i++) {
+               UINT8_TO_STREAM(p, bt_par->feature_set[i]);
+       }
+       for (i = 0; i < 6; i++) {
+               UINT8_TO_STREAM(p, bt_par->device_addr[i]);
+       }
+
+       UINT8_TO_STREAM(p, bt_par->g_sys_sco_transmit_mode);
+       UINT8_TO_STREAM(p, bt_par->g_sys_uart0_communication_supported);
+       UINT8_TO_STREAM(p, bt_par->edr_tx_edr_delay);
+       UINT8_TO_STREAM(p, bt_par->edr_rx_edr_delay);
+
+       UINT16_TO_STREAM(p, bt_par->g_wbs_nv_117);
+
+       UINT32_TO_STREAM(p, bt_par->is_wdg_supported);
+
+       UINT32_TO_STREAM(p, bt_par->share_memo_rx_base_addr);
+       //UINT32_TO_STREAM(p, bt_par->share_memo_tx_base_addr);
+       UINT16_TO_STREAM(p, bt_par->g_wbs_nv_118);
+       UINT16_TO_STREAM(p, bt_par->g_nbv_nv_117);
+
+       UINT32_TO_STREAM(p, bt_par->share_memo_tx_packet_num_addr);
+       UINT32_TO_STREAM(p, bt_par->share_memo_tx_data_base_addr);
+
+       UINT32_TO_STREAM(p, bt_par->g_PrintLevel);
+
+       UINT16_TO_STREAM(p, bt_par->share_memo_tx_block_length);
+       UINT16_TO_STREAM(p, bt_par->share_memo_rx_block_length);
+       UINT16_TO_STREAM(p, bt_par->share_memo_tx_water_mark);
+       //UINT16_TO_STREAM(p, bt_par->share_memo_tx_timeout_value);
+       UINT16_TO_STREAM(p, bt_par->g_nbv_nv_118);
+
+       UINT16_TO_STREAM(p, bt_par->uart_rx_watermark);
+       UINT16_TO_STREAM(p, bt_par->uart_flow_control_thld);
+       UINT32_TO_STREAM(p, bt_par->comp_id);
+       UINT16_TO_STREAM(p, bt_par->pcm_clk_divd);
+
+
+       for (i = 0; i < 8; i++) {
+               UINT32_TO_STREAM(p, bt_par->reserved[i]);
+       }
+}
+
+void sprd_get_pskey(BT_PSKEY_CONFIG_T * pskey_t) {
+       BT_PSKEY_CONFIG_T pskey;
+       uint8 buf[180] = {0};
+
+       LOGD("%s", __func__);
+       memset(&pskey, 0 , sizeof(BT_PSKEY_CONFIG_T));
+       if (bt_getPskeyFromFile(&pskey) < 0 ) {
+               LOGD("%s bt_getPskeyFromFile failed", __func__);
+               memcpy(pskey_t, &bt_para_setting, sizeof(BT_PSKEY_CONFIG_T));
+               return;
+       }
+
+       memset(buf, 0, PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE);
+
+       /* get bluetooth mac address */
+       get_mac_address(pskey.device_addr);
+
+       /* compose pskey hci command pkt */
+       pskey_stream_compose(buf, &pskey);
+
+       memcpy(pskey_t, &pskey, sizeof(BT_PSKEY_CONFIG_T));
+}
+
+#define HCI_HDR_LEN    3
+
+int sprd_config_init(int fd, char *bdaddr, struct termios *ti)
+{
+       int ret = 0,r=0;
+       unsigned char resp[30] = {0};
+       BT_PSKEY_CONFIG_T bt_para_tmp;
+       uint8 data_tmp[30] = {'a'};
+       static int index = 0;
+       uint8 *buf = NULL;
+       uint8 hci_len = 0;
+       uint8 is_expected_hci_evt = 0;
+#if 0
+       char buffer;
+       int btsleep_fd_sprd = -1;
+#endif
+       LOGD("sprd_config_init");
+
+#if 0
+       uart_fd = open(UART_INFO_PATH, O_WRONLY);
+       if(uart_fd > 0)
+       {
+               buffer = '2';
+               if (write(uart_fd, &buffer, 1) < 0)
+               {
+                       LOGD("%s write(%s) failed: %s (%d) 2", __func__,
+                                       UART_INFO_PATH, strerror(errno),errno);
+               }
+
+               close(uart_fd);
+       }
+#endif
+
+#if 0
+       btsleep_fd_sprd = open(VENDOR_BTWRITE_PROC_NODE, O_WRONLY);
+       if (btsleep_fd_sprd < 0)
+       {
+               LOGD("%s open(%s) for write failed: %s (%d)", __func__,
+                               VENDOR_BTWRITE_PROC_NODE, strerror(errno), errno);
+       }
+       else
+       {
+               buffer = '1';
+               if (write(btsleep_fd_sprd, &buffer, 1) < 0)
+               {
+                       LOGD("%s write(%s) failed: %s (%d)", __func__,
+                                       VENDOR_BTWRITE_PROC_NODE, strerror(errno),errno);
+               }
+       }
+#endif
+
+       ret = bt_getPskeyFromFile(&bt_para_tmp);
+       if (ret < 0) {
+               LOGD("init_sprd_config bt_getPskeyFromFile failed\n");
+               memcpy(&bt_para_tmp, &bt_para_setting, sizeof(BT_PSKEY_CONFIG_T));
+       }
+
+       buf = (uint8 *)malloc(PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE);
+       if (buf == NULL) {
+               LOGD("%s alloc stream memory failed", __func__);
+               return -1;
+       }
+       memset(buf, 0, PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE);
+
+       /* get bluetooth mac address */
+       get_mac_address(bt_para_tmp.device_addr);
+
+       /* compose pskey hci command pkt */
+       pskey_stream_compose(buf, &bt_para_tmp);
+
+       ret = write(fd, buf, PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE);
+       LOGD("write pskey ret = %d", ret);
+
+       free(buf);
+       buf = NULL;
+
+       if (ret < 0) {
+               LOGD("%s write pskey stream failed", __func__);
+               return -1;
+       }
+
+       memset(data_tmp, 0xff, sizeof(data_tmp));
+       while (1) {
+               r = read(fd, resp, 1);
+
+               if (r <= 0)
+                       return -1;
+               else{
+                       data_tmp[index] = resp[0];
+                       LOGD("recive from controller 0x%x", data_tmp[index]);
+                       ++index;
+               }
+
+               if (index >= 6) {
+                       hci_len = data_tmp[2]+HCI_HDR_LEN;
+
+                       if ((data_tmp[0] == 0x04) && (data_tmp[1] == 0xe) &&
+                               (data_tmp[2] == 0xa) &&(data_tmp[3] == 0x1) &&
+                               (data_tmp[4] == 0xa0) &&(data_tmp[5] == 0xfc)) {
+                                       LOGD("read response ok \n");
+                                       is_expected_hci_evt = 1;
+                               } else {
+                                       LOGD("this is not what we expect HCI evt\n");
+                                       is_expected_hci_evt = 0;
+                               }
+
+                       if (index == hci_len) {
+                               index = 0;
+                               memset(data_tmp, 0x0, sizeof(data_tmp));
+
+                               if(is_expected_hci_evt)
+                                       break;
+                       }
+               }
+       }
+       return 0;
+}
diff --git a/tools/hciattach_sprd.h b/tools/hciattach_sprd.h
new file mode 100755 (executable)
index 0000000..6e59408
--- /dev/null
@@ -0,0 +1,82 @@
+#ifndef HCIATTACH_SPRD_H__
+#define HCIATTACH_SPRD_H__
+
+#define MAC_ERROR              "FF:FF:FF:FF:FF:FF"
+
+#define BT_MAC_FILE            "/productinfo/btmac.txt"
+//#define GET_BTMAC_ATCMD      "AT+SNVM=0,401"
+//#define GET_BTPSKEY_ATCMD    "AT+SNVM=0,415"
+//#define SET_BTMAC_ATCMD      "AT+SNVM=1,401"
+#define BT_RAND_MAC_LENGTH   17
+
+// used to store BT pskey structure and default values
+#define BT_PSKEY_STRUCT_FILE "/system/lib/modules/pskey_bt.txt"
+//#define BT_PSKEY_FILE        "/system/lib/modules/pskey_bt.txt"
+
+
+typedef unsigned char uint8;
+typedef unsigned int uint32;
+typedef unsigned short uint16;
+
+#define BT_ADDRESS_SIZE    6
+
+
+typedef struct SPRD_BT_PSKEY_INFO_T{
+       uint32   pskey_cmd;
+
+       uint8    g_dbg_source_sink_syn_test_data;
+       uint8    g_sys_sleep_in_standby_supported;
+       uint8    g_sys_sleep_master_supported;
+       uint8    g_sys_sleep_slave_supported;
+
+       uint32  default_ahb_clk;
+       uint32  device_class;
+       uint32  win_ext;
+
+       uint32  g_aGainValue[6];
+       uint32  g_aPowerValue[5];
+
+       uint8    feature_set[16];
+       uint8    device_addr[6];
+
+       uint8    g_sys_sco_transmit_mode; //true tramsmit by uart, otherwise by share memory
+       uint8    g_sys_uart0_communication_supported; //true use uart0, otherwise use uart1 for debug
+       uint8    edr_tx_edr_delay;
+       uint8    edr_rx_edr_delay;
+
+       uint16  g_wbs_nv_117;
+
+       uint32  is_wdg_supported;
+
+       uint32  share_memo_rx_base_addr;
+
+       // uint32  share_memo_tx_base_addr;
+       uint16  g_wbs_nv_118;
+       uint16  g_nbv_nv_117;
+
+       uint32  share_memo_tx_packet_num_addr;
+       uint32  share_memo_tx_data_base_addr;
+
+       uint32  g_PrintLevel;
+
+       uint16  share_memo_tx_block_length;
+       uint16  share_memo_rx_block_length;
+       uint16  share_memo_tx_water_mark;
+
+       //uint16  share_memo_tx_timeout_value;
+       uint16  g_nbv_nv_118;
+
+       uint16  uart_rx_watermark;
+       uint16  uart_flow_control_thld;
+       uint32  comp_id;
+       uint16  pcm_clk_divd;
+
+       uint32  reserved[8];
+}BT_PSKEY_CONFIG_T;
+
+
+#endif /* HCIATTACH_SPRD_H__ */
+
+
+
+
index 828dd61..b962677 100755 (executable)
 
 #define TI_MANUFACTURER_ID     13
 
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#ifdef __TI_PATCH__
+#define FIRMWARE_DIRECTORY1    "/mnt/mmc/"
+#define FIRMWARE_DIRECTORY2    "/usr/etc/bluetooth/"
+#else
 #define FIRMWARE_DIRECTORY     "/lib/firmware/ti-connectivity/"
+#endif
 
 #define ACTION_SEND_COMMAND    1
 #define ACTION_WAIT_EVENT      2
@@ -197,7 +203,24 @@ static const char *get_firmware_name(const uint8_t *respond)
        if (version & 0x8000)
                maj_ver |= 0x0008;
 
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#ifdef __TI_PATCH__
+       FILE *fp;
+       sprintf(firmware_file_name, FIRMWARE_DIRECTORY1 "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver);
+
+       if ((fp = fopen(firmware_file_name, "r")) == NULL ) {
+               extern int firmware_path;
+               if (firmware_path)
+                       sprintf(firmware_file_name, FIRMWARE_DIRECTORY2 "TIInit_edutm_%d.%d.%d.bts", chip, maj_ver, min_ver);
+               else
+                       sprintf(firmware_file_name, FIRMWARE_DIRECTORY2 "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver);
+       }
+       else {
+               fclose(fp);
+       }
+#else
        sprintf(firmware_file_name, FIRMWARE_DIRECTORY "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver);
+#endif
 
        return firmware_file_name;
 }
index 8a97cc4..b4a3856 100755 (executable)
@@ -93,7 +93,14 @@ static void print_pkt_type(struct hci_dev_info *di)
 
 static void print_link_policy(struct hci_dev_info *di)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char *str;
+       str = hci_lptostr(di->link_policy);
+       printf("\tLink policy: %s\n", str);
+       bt_free(str);
+#else
        printf("\tLink policy: %s\n", hci_lptostr(di->link_policy));
+#endif
 }
 
 static void print_link_mode(struct hci_dev_info *di)
index af8f592..b460daa 100755 (executable)
@@ -59,10 +59,27 @@ enum {
 /* Default options */
 static int  snap_len = SNAP_LEN;
 static int  mode = PARSE;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
 static char *dump_file = NULL;
+#endif
 static char *pppdump_file = NULL;
 static char *audio_file = NULL;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define DUMP_MAX_SIZE  10000000        /* 10MB */
+#define DUMP_MAX_COUNT 2
+#define NAME_MAX       255
+
+struct dump_file {
+       char *filename;
+       int postfix_width;
+       unsigned int max_size;
+       int max_count;
+};
+
+struct dump_file df = {NULL, 1, DUMP_MAX_SIZE, DUMP_MAX_COUNT};
+#endif
+
 struct hcidump_hdr {
        uint16_t        len;
        uint8_t         in;
@@ -101,6 +118,10 @@ struct pktlog_hdr {
 } __attribute__ ((packed));
 #define PKTLOG_HDR_SIZE (sizeof(struct pktlog_hdr))
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static int open_new_dumpfile(unsigned long flags);
+#endif
+
 static inline int read_n(int fd, char *buf, int len)
 {
        int t = 0, w;
@@ -148,6 +169,9 @@ static int process_frames(int dev, int sock, int fd, unsigned long flags)
        char *buf;
        char ctrl[100];
        int len, hdr_size = HCIDUMP_HDR_SIZE;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       int written = 0;
+#endif
 
        if (sock < 0)
                return -1;
@@ -261,10 +285,28 @@ static int process_frames(int dev, int sock, int fd, unsigned long flags)
                                dh->ts_usec = htobl(frm.ts.tv_usec);
                        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                       if (mode == WRITE && df.max_size != 0 &&
+                           written + frm.data_len + hdr_size > df.max_size) {
+                               close(fd);
+                               fd = open_new_dumpfile(flags);
+                               if (fd < 0)
+                                       return -1;
+                               written = 0;
+                       }
+
+                       len = write_n(fd, buf, frm.data_len + hdr_size);
+                       if (len  < 0) {
+                               perror("Write error");
+                               return -1;
+                       }
+                       written += len;
+#else
                        if (write_n(fd, buf, frm.data_len + hdr_size) < 0) {
                                perror("Write error");
                                return -1;
                        }
+#endif
                        break;
 
                default:
@@ -399,8 +441,10 @@ static void read_dump(int fd)
 
                if (err < 0)
                        goto failed;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
                if (!err)
                        goto done;
+#endif
 
                frm.ptr = frm.data;
                frm.len = frm.data_len;
@@ -516,6 +560,38 @@ static int open_file(char *file, int mode, unsigned long flags)
        return fd;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static int open_new_dumpfile(unsigned long flags)
+{
+       char filename[NAME_MAX + 1];
+       char new_filename[NAME_MAX + 1];
+       int i;
+
+       if (df.max_count <= 1)
+               return open_file(df.filename, WRITE, flags);
+
+       for (i = df.max_count - 2; i >= 0; i--) {
+               if (i == 0) {
+                       snprintf(filename, sizeof(filename), "%s", df.filename);
+               } else {
+                       snprintf(filename, sizeof(filename), "%s.%0*d",
+                                       df.filename, df.postfix_width, i);
+               }
+
+               if (access(filename, F_OK) < 0)
+                       continue;
+
+               snprintf(new_filename, sizeof(new_filename), "%s.%0*d",
+                               df.filename, df.postfix_width, i + 1);
+
+               if (rename(filename, new_filename) < 0)
+                       perror("rename failed");
+       }
+
+       return open_file(df.filename, WRITE, flags);
+}
+#endif
+
 static int open_socket(int dev, unsigned long flags)
 {
        struct sockaddr_hci addr;
@@ -635,6 +711,10 @@ static void usage(void)
        "  -D, --pppdump=file         Extract PPP traffic\n"
        "  -A, --audio=file           Extract SCO audio data\n"
        "  -Y, --novendor             No vendor commands or events\n"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       "  -s  --dump-size=size       Maximum save-dump's file size. The unit is million bytes. Use this with -w. (default: 1,000,000 bytes)\n"
+       "  -c  --dump-count=count     Specified count's dump files will be generated at most. Use this with -w. (default: 4)\n"
+#endif
        "  -h, --help                 Give this help list\n"
        "  -v, --version              Give version information\n"
        "      --usage                Give a short usage message\n"
@@ -661,6 +741,10 @@ static struct option main_options[] = {
        { "pppdump",            1, 0, 'D' },
        { "audio",              1, 0, 'A' },
        { "novendor",           0, 0, 'Y' },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { "dump-size",          1, 0, 's' },
+       { "dump-count",         1, 0, 'c' },
+#endif
        { "help",               0, 0, 'h' },
        { "version",            0, 0, 'v' },
        { 0 }
@@ -676,9 +760,15 @@ int main(int argc, char *argv[])
        int opt, pppdump_fd = -1, audio_fd = -1;
        uint16_t obex_port;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       while ((opt = getopt_long(argc, argv,
+                               "i:l:p:m:w:r:taxXRC:H:O:P:S:D:A:Ys:c:hv",
+                               main_options, NULL)) != -1) {
+#else
        while ((opt = getopt_long(argc, argv,
                                "i:l:p:m:w:r:taxXRC:H:O:P:S:D:A:Yhv",
                                main_options, NULL)) != -1) {
+#endif
                switch(opt) {
                case 'i':
                        if (strcasecmp(optarg, "none") && strcasecmp(optarg, "system"))
@@ -701,12 +791,20 @@ int main(int argc, char *argv[])
 
                case 'w':
                        mode = WRITE;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                       df.filename = strdup(optarg);
+#else
                        dump_file = strdup(optarg);
+#endif
                        break;
 
                case 'r':
                        mode = READ;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+                       df.filename = strdup(optarg);
+#else
                        dump_file = strdup(optarg);
+#endif
                        break;
 
                case 't':
@@ -765,6 +863,21 @@ int main(int argc, char *argv[])
                        flags |= DUMP_NOVENDOR;
                        break;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               case 's':
+                       df.max_size = atoi(optarg) * 1000000;
+                       break;
+
+               case 'c':
+               {
+                       int i;
+                       df.max_count = atoi(optarg);
+                       for (i = df.max_count / 10; i; i /= 10)
+                               df.postfix_width++;
+                       break;
+               }
+#endif
+
                case 'v':
                        printf("%s\n", VERSION);
                        exit(0);
@@ -807,13 +920,24 @@ int main(int argc, char *argv[])
                flags |= DUMP_VERBOSE;
                init_parser(flags, filter, defpsm, defcompid,
                                                        pppdump_fd, audio_fd);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               read_dump(open_file(df.filename, mode, flags));
+#else
                read_dump(open_file(dump_file, mode, flags));
+#endif
                break;
 
        case WRITE:
                flags |= DUMP_BTSNOOP;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               printf("Maximum size of one file : %u, Rotated file count : %d",
+                               df.max_size, df.max_count);
+               process_frames(device, open_socket(device, flags),
+                               open_file(df.filename, mode, flags), flags);
+#else
                process_frames(device, open_socket(device, flags),
                                open_file(dump_file, mode, flags), flags);
+#endif
                break;
        }
 
index 02c4ebe..0b6f499 100755 (executable)
@@ -1098,6 +1098,66 @@ static void cmd_epinq(int dev_id, int argc, char **argv)
        hci_close_dev(dd);
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+/* Send arbitrary ACL data */
+static struct option data_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *data_help =
+       "Usage:\n"
+       "\tcmd <handle> <data>\n"
+       "Example:\n"
+       "\tcmd 0x0064 0x41 0x42 0x43 0x44\n";
+
+static void cmd_data(int dev_id, int argc, char **argv)
+{
+       unsigned char buf[HCI_MAX_ACL_SIZE], *ptr = buf;
+       struct hci_filter flt;
+       int i, opt, len, dd;
+       uint16_t handle;
+
+       for_each_opt(opt, data_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", data_help);
+                       return;
+               }
+       }
+       helper_arg(2, -1, &argc, &argv, data_help);
+
+       if (dev_id < 0)
+               dev_id = hci_get_route(NULL);
+
+       handle = atoi(argv[0]);
+
+       for (i = 1, len = 0; i < argc && len < (int) sizeof(buf); i++, len++)
+               *ptr++ = (uint8_t) strtol(argv[i], NULL, 16);
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("Device open failed");
+               exit(EXIT_FAILURE);
+       }
+
+       /* Setup filter */
+       hci_filter_clear(&flt);
+       hci_filter_all_events(&flt);
+       if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+               perror("HCI filter setup failed");
+               exit(EXIT_FAILURE);
+       }
+
+       if (hci_send_data(dd, handle, len, buf) < 0) {
+               perror("Send failed");
+               exit(EXIT_FAILURE);
+       }
+
+       hci_close_dev(dd);
+}
+#endif
+
 /* Send arbitrary HCI commands */
 
 static struct option cmd_options[] = {
@@ -3388,6 +3448,9 @@ static struct {
        { "spinq",    cmd_spinq,   "Start periodic inquiry"               },
        { "epinq",    cmd_epinq,   "Exit periodic inquiry"                },
        { "cmd",      cmd_cmd,     "Submit arbitrary HCI commands"        },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { "acl",      cmd_data,    "Submit arbitrary ACL data"            },
+#endif
        { "con",      cmd_con,     "Display active connections"           },
        { "cc",       cmd_cc,      "Create connection to remote device"   },
        { "dc",       cmd_dc,      "Disconnect from remote device"        },
index 6208e8a..9d48e66 100755 (executable)
@@ -149,6 +149,7 @@ static void adv_tx_power_callback(const void *data, uint8_t size,
 
        cmd.data[0] = 0x02;             /* Field length */
        cmd.data[1] = 0x01;             /* Flags */
+       cmd.data[2] = 0x02;             /* LE General Discoverable Mode */
        cmd.data[2] |= 0x04;            /* BR/EDR Not Supported */
 
        cmd.data[3] = 0x1a;             /* Field length */
index 1819423..98f7353 100755 (executable)
@@ -69,9 +69,17 @@ enum {
        LSENDRECV,
        CSENDRECV,
        INFOREQ,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       CONFIGREQ,
+#endif
        PAIRING,
 };
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define L2CAP_DEFAULT_RETRANS_TO       2000    /* 2 seconds */
+#define L2CAP_DEFAULT_MONITOR_TO       12000   /* 12 seconds */
+#endif
+
 static unsigned char *buf;
 
 /* Default mtu */
@@ -95,6 +103,9 @@ static long buffer_size = 2048;
 static bdaddr_t bdaddr;
 static unsigned short psm = 0;
 static unsigned short cid = 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static uint16_t dcid = 0x0000;
+#endif
 
 /* Default number of frames to send (-1 = infinite) */
 static int num_frames = -1;
@@ -1103,6 +1114,146 @@ static void multi_connect_mode(int argc, char *argv[])
        }
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void l2cap_add_conf_opt(void **ptr, uint8_t type, uint8_t len, unsigned long val)
+{
+       l2cap_conf_opt *opt = *ptr;
+
+       printf("type 0x%2.2x len %u val 0x%lx \n", type, len, val);
+       opt->type = htobs(type);
+       opt->len  = htobs(len);
+
+       switch (opt->len) {
+       case 1:
+               *((uint8_t *) opt->val) = val;
+               break;
+       case 2:
+               bt_put_le16(val, opt->val);
+               break;
+       case 4:
+               bt_put_le32(val, opt->val);
+               break;
+       default:
+               memcpy(opt->val, (void *) val, len);
+               break;
+       }
+
+       *ptr += L2CAP_CONF_OPT_SIZE + len;
+}
+
+static int l2cap_build_conf_req(void *data)
+{
+       l2cap_conf_req *req = data;
+       l2cap_conf_rfc rfc;
+       void *ptr = req->data;
+
+       req->dcid = htobs(dcid);
+       req->flags = htobs(0x0000);
+
+       switch (rfcmode) {
+       case L2CAP_MODE_BASIC:
+               rfc.mode                        = htobs(L2CAP_MODE_BASIC);
+               rfc.txwin_size          = htobs(0);
+               rfc.max_transmit        = htobs(0);
+               rfc.retrans_timeout = htobs(0);
+               rfc.monitor_timeout = htobs(0);
+               rfc.max_pdu_size        = htobs(0);
+
+               l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+                                  (unsigned long) &rfc);
+
+               break;
+
+       case L2CAP_MODE_ERTM:
+               rfc.mode                        = htobs(L2CAP_MODE_ERTM);
+               rfc.txwin_size          = htobs(txwin_size);
+               rfc.max_transmit        = htobs(max_transmit);
+               rfc.retrans_timeout = htobs(L2CAP_DEFAULT_RETRANS_TO);
+               rfc.monitor_timeout = htobs(L2CAP_DEFAULT_MONITOR_TO);
+               rfc.max_pdu_size        = htobs(imtu);
+
+               /* TODO: Enable FCS, FOC options if required */
+               l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+                                  (unsigned long) &rfc);
+               break;
+
+       case L2CAP_MODE_STREAMING:
+               rfc.mode                        = htobs(L2CAP_MODE_STREAMING);
+               rfc.txwin_size          = htobs(txwin_size);
+               rfc.max_transmit        = htobs(max_transmit);
+               rfc.retrans_timeout = htobs(L2CAP_DEFAULT_RETRANS_TO);
+               rfc.monitor_timeout = htobs(L2CAP_DEFAULT_MONITOR_TO);
+               rfc.max_pdu_size        = htobs(imtu);
+
+               /* TODO: Enable FCS, FOC options if required */
+               l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+                                  (unsigned long) &rfc);
+
+               break;
+       default:
+               return L2CAP_CONF_REQ_SIZE;
+       }
+       return ptr - data;
+}
+
+static void config_request(char *svr)
+{
+       unsigned char buf[48];
+       l2cap_cmd_hdr *cmd = (l2cap_cmd_hdr *) buf;
+       uint8_t *req_buf = (uint8_t *) (buf + L2CAP_CMD_HDR_SIZE);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint16_t mtu;
+       uint32_t channels, mask = 0x0000;
+#endif
+       struct sockaddr_l2 addr;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+       int sk, err;
+#else
+       int sk;
+#endif
+       int data_len = 0;
+
+       sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+       if (sk < 0) {
+               perror("Can't create socket");
+               return;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, &bdaddr);
+       addr.l2_bdaddr_type = bdaddr_type;
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Can't bind socket");
+               goto failed;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       str2ba(svr, &addr.l2_bdaddr);
+       addr.l2_bdaddr_type = bdaddr_type;
+
+       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) {
+               perror("Can't connect socket");
+               goto failed;
+       }
+
+       memset(buf, 0, sizeof(buf));
+       cmd->code  = L2CAP_CONF_REQ;
+       cmd->ident = 141;
+       data_len = l2cap_build_conf_req(req_buf);
+       cmd->len = htobs(data_len);
+
+       if (send(sk, buf, L2CAP_CMD_HDR_SIZE + data_len, 0) < 0) {
+               perror("Can't send info request");
+               goto failed;
+       }
+failed:
+       close(sk);
+}
+#endif
+
 static void info_request(char *svr)
 {
        unsigned char buf[48];
@@ -1314,6 +1465,9 @@ static void usage(void)
                "\t-c connect, disconnect, connect, ...\n"
                "\t-m multiple connects\n"
                "\t-p trigger dedicated bonding\n"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               "\t-o configuration request\n"
+#endif
                "\t-z information request\n");
 
        printf("Options:\n"
@@ -1343,6 +1497,9 @@ static void usage(void)
                "\t[-M] become master\n"
                "\t[-T] enable timestamps\n"
                "\t[-V type] address type (help for list, default = bredr)\n"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               "\t[-f DCID] destination CID\n"
+#endif
                "\t[-e seq] initial sequence value (default = 0)\n");
 }
 
@@ -1353,8 +1510,13 @@ int main(int argc, char *argv[])
 
        bacpy(&bdaddr, BDADDR_ANY);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       while ((opt = getopt(argc, argv, "a:b:cde:f:g:i:mnopqrstuwxyz"
+               "AB:C:D:EF:GH:I:J:K:L:MN:O:P:Q:RSTUV:W:X:Y:Z:")) != EOF) {
+#else
        while ((opt = getopt(argc, argv, "a:b:cde:g:i:mnpqrstuwxyz"
                "AB:C:D:EF:GH:I:J:K:L:MN:O:P:Q:RSTUV:W:X:Y:Z:")) != EOF) {
+#endif
                switch (opt) {
                case 'r':
                        mode = RECV;
@@ -1383,6 +1545,13 @@ int main(int argc, char *argv[])
                        need_addr = 1;
                        break;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               case 'o':
+                       mode = CONFIGREQ;
+                       need_addr = 1;
+                       break;
+#endif
+
                case 'n':
                        mode = CONNECT;
                        need_addr = 1;
@@ -1560,6 +1729,13 @@ int main(int argc, char *argv[])
 
                        break;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               case 'f':
+                       dcid = atoi(optarg);
+                       printf("dcid %d", dcid);
+                       break;
+#endif
+
                case 'e':
                        seq_start = atoi(optarg);
                        break;
@@ -1669,6 +1845,12 @@ int main(int argc, char *argv[])
                        info_request(argv[optind]);
                        exit(0);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+               case CONFIGREQ:
+                       config_request(argv[optind]);
+                       exit(0);
+#endif
+
                case PAIRING:
                        do_pairing(argv[optind]);
                        exit(0);
diff --git a/tools/parse_companies.pl b/tools/parse_companies.pl
new file mode 100755 (executable)
index 0000000..6dc358e
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/perl
+
+# parse companies from
+# https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
+
+use strict;
+# use URI::Encode qw(uri_decode);
+
+my %known_entities = (
+    'nbsp' => ' ',
+    'eacute' => 'é',
+    'auml' => 'ä',
+);
+
+# better to use URI::Encode if you have it
+sub uri_decode {
+    my $name = $_[0];
+    foreach my $entity (keys %known_entities) {
+        my $to = $known_entities{$entity};
+        $name =~ s/&$entity;/$to/g;
+    }
+    foreach my $entity (map { lc $_ } $name =~ /&([^;]+);/g) {
+        if ($entity ne 'amp') {
+            print "Unable to convert &$entity;, giving up\n";
+            exit 1;
+        }
+    }
+    $name =~ s/&amp;/&/ig;
+    $name =~ s/&nbsp;/ /ig;
+    return $name;
+}
+
+# never parse HTML with regex!
+# except when you should
+
+my $identifier;
+my $next_is_name = 0;
+
+while (<>) {
+    s/\xe2\x80\x8b//g; # kill zero width space
+
+    # grab identifier (in hex)
+    if (/\<td.*(0x[0-9A-F]{4})/i) {
+        $identifier = $1;
+        $next_is_name = 1;
+
+    # next <td> should be company name
+    } elsif ($next_is_name && m|\<td.*\>(.*)\</td\>|) {
+        my $name = uri_decode($1);
+        $name =~ s/^\s+//g; # kill leading
+        $name =~ s/\s+$//g; # and trailing space
+        my $id = hex($identifier);
+        if ($id != 65535) {
+            print "\tcase $id:\n";
+            print "\t\treturn \"$name\";\n";
+        }
+        $next_is_name = 0;
+    }
+}
index 788aef0..6bd5b2f 100755 (executable)
@@ -179,6 +179,18 @@ typedef struct {
 } __attribute__ ((packed)) l2cap_conf_opt;
 #define L2CAP_CONF_OPT_SIZE 2
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+typedef struct {
+       uint8_t    mode;
+       uint8_t    txwin_size;
+       uint8_t    max_transmit;
+       uint16_t   retrans_timeout;
+       uint16_t   monitor_timeout;
+       uint16_t   max_pdu_size;
+} __attribute__ ((packed)) l2cap_conf_rfc ;
+#define L2CAP_CONF_RFC_SIZE 9
+#endif
+
 #define L2CAP_CONF_MTU         0x01
 #define L2CAP_CONF_FLUSH_TO    0x02
 #define L2CAP_CONF_QOS         0x03
diff --git a/tools/pskey_get.c b/tools/pskey_get.c
new file mode 100755 (executable)
index 0000000..db4d228
--- /dev/null
@@ -0,0 +1,384 @@
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define LOG_TAG "pskey"
+// #include "cutils/log.h"
+
+#include "hciattach_sprd.h"
+//#include "bt_vendor_sprd.h"
+#define BT_PSKEY_TRACE_BUF_SIZE 256
+#define MAX_BOARD_TYPE_LEN 32
+
+#define _FILE_PARSE_DEBUG_
+#define  CMD_ITEM_TABLE(ITEM, MEM_OFFSET, TYPE)    { ITEM,   (unsigned int)( &(  ((BT_PSKEY_CONFIG_T *)(0))->MEM_OFFSET )),   TYPE }
+#define ALOGI(fmt, arg...)  fprintf(stderr, "%s:%d()" fmt "\n", __FILE__,__LINE__, ## arg)
+#define ALOGE(fmt, arg...)  fprintf(stderr, "%s:%d()" fmt "\n", __FILE__,__LINE__, ## arg)
+
+#define PSKEY_PATH "/usr/lib/firmware/scx35_pikeavivaltove_3M_MARLIN_connectivity_configure.ini"
+
+typedef struct
+{
+       char item[64];
+       uint32  par[32];
+       int  num;
+}cmd_par;
+
+typedef struct
+{
+       char *item;
+       unsigned int mem_offset;
+       int type;
+}cmd_par_table;
+
+static cmd_par_table g_pskey_table[] =
+{
+       CMD_ITEM_TABLE("pskey_cmd", pskey_cmd, 4),
+
+       CMD_ITEM_TABLE("g_dbg_source_sink_syn_test_data", g_dbg_source_sink_syn_test_data, 1),
+       CMD_ITEM_TABLE("g_sys_sleep_in_standby_supported", g_sys_sleep_in_standby_supported, 1),
+       CMD_ITEM_TABLE("g_sys_sleep_master_supported", g_sys_sleep_master_supported, 1),
+       CMD_ITEM_TABLE("g_sys_sleep_slave_supported", g_sys_sleep_slave_supported, 1),
+
+       CMD_ITEM_TABLE("default_ahb_clk", default_ahb_clk, 4),
+       CMD_ITEM_TABLE("device_class", device_class, 4),
+       CMD_ITEM_TABLE("win_ext", win_ext, 4),
+
+       CMD_ITEM_TABLE("g_aGainValue", g_aGainValue, 4),
+       CMD_ITEM_TABLE("g_aPowerValue", g_aPowerValue, 4),
+
+       CMD_ITEM_TABLE("feature_set", feature_set, 1),
+       CMD_ITEM_TABLE("device_addr", device_addr, 1),
+
+       CMD_ITEM_TABLE("g_sys_sco_transmit_mode", g_sys_sco_transmit_mode, 1), //true tramsmit by uart, otherwise by share memory
+       CMD_ITEM_TABLE("g_sys_uart0_communication_supported", g_sys_uart0_communication_supported, 1), //true use uart0, otherwise use uart1 for debug
+       CMD_ITEM_TABLE("edr_tx_edr_delay", edr_tx_edr_delay, 1),
+       CMD_ITEM_TABLE("edr_rx_edr_delay", edr_rx_edr_delay, 1),
+
+       CMD_ITEM_TABLE("g_wbs_nv_117", g_wbs_nv_117, 2),
+
+
+       CMD_ITEM_TABLE("is_wdg_supported", is_wdg_supported, 4),
+
+       CMD_ITEM_TABLE("share_memo_rx_base_addr", share_memo_rx_base_addr, 4),
+       //CMD_ITEM_TABLE("share_memo_tx_base_addr", share_memo_tx_base_addr, 4),
+
+       CMD_ITEM_TABLE("g_wbs_nv_118", g_wbs_nv_118, 2),
+       CMD_ITEM_TABLE("g_nbv_nv_117", g_nbv_nv_117, 2),
+
+
+       CMD_ITEM_TABLE("share_memo_tx_packet_num_addr", share_memo_tx_packet_num_addr, 4),
+       CMD_ITEM_TABLE("share_memo_tx_data_base_addr", share_memo_tx_data_base_addr, 4),
+
+       CMD_ITEM_TABLE("g_PrintLevel", g_PrintLevel, 4),
+
+       CMD_ITEM_TABLE("share_memo_tx_block_length", share_memo_tx_block_length, 2),
+       CMD_ITEM_TABLE("share_memo_rx_block_length", share_memo_rx_block_length, 2),
+       CMD_ITEM_TABLE("share_memo_tx_water_mark", share_memo_tx_water_mark, 2),
+       //CMD_ITEM_TABLE("share_memo_tx_timeout_value", share_memo_tx_timeout_value, 2),
+       CMD_ITEM_TABLE("g_nbv_nv_118", g_nbv_nv_118, 2),
+
+       CMD_ITEM_TABLE("uart_rx_watermark", uart_rx_watermark, 2),
+       CMD_ITEM_TABLE("uart_flow_control_thld", uart_flow_control_thld, 2),
+       CMD_ITEM_TABLE("comp_id", comp_id, 4),
+       CMD_ITEM_TABLE("pcm_clk_divd", pcm_clk_divd, 2),
+
+
+       CMD_ITEM_TABLE("bt_reserved", reserved, 4)
+};
+
+static int bt_getFileSize(char *file)
+{
+       struct stat temp;
+       stat(file, &temp);
+       return temp.st_size;
+}
+
+static int bt_find_type(char key)
+{
+       if( (key >= 'a' && key <= 'w') || (key >= 'y' && key <= 'z') || (key >= 'A' && key <= 'W') || (key >= 'Y' && key <= 'Z') || ('_' == key) )
+               return 1;
+       if( (key >= '0' && key <= '9') || ('-' == key) )
+               return 2;
+       if( ('x' == key) || ('X' == key) || ('.' == key) )
+               return 3;
+       if( (key == '\0') || ('\r' == key) || ('\n' == key) || ('#' == key) )
+               return 4;
+       return 0;
+}
+
+static void bt_getCmdOneline(unsigned char *str, cmd_par *cmd)
+{
+       int i, j, bufType, cType, flag;
+       char tmp[BT_PSKEY_TRACE_BUF_SIZE];
+       char c;
+       bufType = -1;
+       cType = 0;
+       flag = 0;
+       memset( cmd, 0, sizeof(cmd_par) );
+       for(i = 0, j = 0; ; i++)
+       {
+               c = str[i];
+               cType = bt_find_type(c);
+               if( (1 == cType) || ( 2 == cType) || (3 == cType)  )
+               {
+                       tmp[j] = c;
+                       j++;
+                       if(-1 == bufType)
+                       {
+                               if(2 == cType)
+                                       bufType = 2;
+                               else
+                                       bufType = 1;
+                       }
+                       else if(2 == bufType)
+                       {
+                               if(1 == cType)
+                                       bufType = 1;
+                       }
+                       continue;
+               }
+               if(-1 != bufType)
+               {
+                       tmp[j] = '\0';
+
+                       if((1 == bufType) && (0 == flag) )
+                       {
+                               strcpy(cmd->item, tmp);
+                               flag = 1;
+                       }
+                       else
+                       {
+                               /* compatible with  HEX */
+                               if (tmp[0] == '0' && (tmp[1] == 'x' || tmp[1] == 'X')) {
+                                       cmd->par[cmd->num] = strtoul(tmp, 0, 16) & 0xFFFFFFFF;
+                                       cmd->num++;
+                               } else {
+                                       cmd->par[cmd->num] = strtoul(tmp, 0, 10) & 0xFFFFFFFF;
+                                       cmd->num++;
+                               }
+                       }
+                       bufType = -1;
+                       j = 0;
+               }
+               if(0 == cType )
+                       continue;
+               if(4 == cType)
+                       return;
+       }
+       return;
+}
+
+static int bt_getDataFromCmd(cmd_par_table *pTable, cmd_par *cmd,  void *pData)
+{
+       int i;
+       unsigned char  *p;
+       if( (1 != pTable->type)  && (2 != pTable->type) && (4 != pTable->type) )
+               return -1;
+       p = (unsigned char *)(pData) + pTable->mem_offset;
+#ifdef _FILE_PARSE_DEBUG_
+       char tmp[BT_PSKEY_TRACE_BUF_SIZE] = {0};
+       char string[16] = {0};
+       sprintf(tmp, "###[pskey]%s, offset:%d, num:%d, value:   ", pTable->item, pTable->mem_offset, cmd->num);
+       for(i=0; i<cmd->num; i++)
+       {
+               memset(string, 0, 16);
+               sprintf(string, "0x%x, ", cmd->par[i] );
+               strcat(tmp, string);
+       }
+       ALOGI("%s\n", tmp);
+#endif
+       for(i = 0; i < cmd->num;  i++)
+       {
+               if(1 == pTable->type)
+                       *((unsigned char *)p + i) = (unsigned char)(cmd->par[i]);
+               else if(2 == pTable->type)
+                       *((unsigned short *)p + i) = (unsigned short)(cmd->par[i]);
+               else if(4 == pTable->type)
+                       *( (unsigned int *)p + i) = (unsigned int)(cmd->par[i]);
+               else
+                       ALOGE("%s, type err\n", __func__);
+       }
+       return 0;
+}
+
+static cmd_par_table *bt_cmd_table_match(cmd_par *cmd)
+{
+       int i;
+       cmd_par_table *pTable = NULL;
+       int len = sizeof(g_pskey_table) / sizeof(cmd_par_table);
+       if(NULL == cmd->item)
+               return NULL;
+       for(i = 0; i < len; i++)
+       {
+               if(NULL == g_pskey_table[i].item)
+                       continue;
+               if( 0 != strcmp( g_pskey_table[i].item, cmd->item ) )
+                       continue;
+               pTable = &g_pskey_table[i];
+               break;
+       }
+       return pTable;
+}
+
+
+static int bt_getDataFromBuf(void *pData, unsigned char *pBuf, int file_len)
+{
+       int i, p;
+       cmd_par cmd;
+       cmd_par_table *pTable = NULL;
+       if((NULL == pBuf) || (0 == file_len) || (NULL == pData) )
+               return -1;
+       for(i = 0, p = 0; i < file_len; i++)
+       {
+               if( ('\n' == *(pBuf + i)) || ( '\r' == *(pBuf + i)) || ( '\0' == *(pBuf + i) )   )
+               {
+                       if(5 <= (i - p) )
+                       {
+                               bt_getCmdOneline((pBuf + p), &cmd);
+                               pTable = bt_cmd_table_match(&cmd);
+                               if(NULL != pTable)
+                               {
+                                       bt_getDataFromCmd(pTable, &cmd, pData);
+                               }
+                       }
+                       p = i + 1;
+               }
+
+       }
+       return 0;
+}
+
+static int bt_dumpPskey(BT_PSKEY_CONFIG_T *p)
+{
+       ALOGI("pskey_cmd: 0x%08X", p->pskey_cmd);
+
+       ALOGI("g_dbg_source_sink_syn_test_data: 0x%02X", p->g_dbg_source_sink_syn_test_data);
+       ALOGI("g_sys_sleep_in_standby_supported: 0x%02X", p->g_sys_sleep_in_standby_supported);
+       ALOGI("g_sys_sleep_master_supported: 0x%02X", p->g_sys_sleep_master_supported);
+       ALOGI("g_sys_sleep_slave_supported: 0x%02X", p->g_sys_sleep_slave_supported);
+
+       ALOGI("default_ahb_clk: %d", p->default_ahb_clk);
+       ALOGI("device_class: 0x%08X", p->device_class);
+       ALOGI("win_ext: 0x%08X", p->win_ext);
+
+       ALOGI("g_aGainValue: 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X", p->g_aGainValue[0], p->g_aGainValue[1], p->g_aGainValue[2], p->g_aGainValue[3], p->g_aGainValue[4], p->g_aGainValue[5]);
+       ALOGI("g_aPowerValue: 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X", p->g_aPowerValue[0], p->g_aPowerValue[1], p->g_aPowerValue[2], p->g_aPowerValue[3], p->g_aPowerValue[4]);
+
+
+       ALOGI("feature_set(0~7): 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->feature_set[0], p->feature_set[1], p->feature_set[2], 
+                       p->feature_set[3], p->feature_set[4], p->feature_set[5], p->feature_set[6], p->feature_set[7]);
+       ALOGI("feature_set(8~15): 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->feature_set[8], p->feature_set[9], p->feature_set[10], 
+                       p->feature_set[11], p->feature_set[12], p->feature_set[13], p->feature_set[14], p->feature_set[15]);
+       ALOGI("device_addr: 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->device_addr[0], p->device_addr[1], p->device_addr[2], p->device_addr[3], p->device_addr[4], p->device_addr[5]);
+
+       ALOGI("g_sys_sco_transmit_mode: 0x%02X", p->g_sys_sco_transmit_mode);
+       ALOGI("g_sys_uart0_communication_supported: 0x%02X", p->g_sys_uart0_communication_supported);
+       ALOGI("edr_tx_edr_delay: %d", p->edr_tx_edr_delay);
+       ALOGI("edr_rx_edr_delay: %d", p->edr_rx_edr_delay);
+
+       ALOGI("g_wbs_nv_117 : 0x%04X", p->g_wbs_nv_117 );
+
+       ALOGI("is_wdg_supported: 0x%08X", p->is_wdg_supported);
+
+       ALOGI("share_memo_rx_base_addr: 0x%08X", p->share_memo_rx_base_addr);
+       //ALOGI("share_memo_tx_base_addr: 0x%08X", p->share_memo_tx_base_addr);
+       ALOGI("g_wbs_nv_118 : 0x%04X", p->g_wbs_nv_118 );
+       ALOGI("g_nbv_nv_117 : 0x%04X", p->g_nbv_nv_117 );
+
+
+       ALOGI("share_memo_tx_packet_num_addr: 0x%08X", p->share_memo_tx_packet_num_addr);
+       ALOGI("share_memo_tx_data_base_addr: 0x%08X", p->share_memo_tx_data_base_addr);
+
+       ALOGI("g_PrintLevel: 0x%08X", p->g_PrintLevel);
+
+       ALOGI("share_memo_tx_block_length: 0x%04X", p->share_memo_tx_block_length);
+       ALOGI("share_memo_rx_block_length: 0x%04X", p->share_memo_rx_block_length);
+       ALOGI("share_memo_tx_water_mark: 0x%04X", p->share_memo_tx_water_mark);
+       //ALOGI("share_memo_tx_timeout_value: 0x%04X", p->share_memo_tx_timeout_value);
+       ALOGI("g_nbv_nv_118 : 0x%04X", p->g_nbv_nv_118 );
+
+       ALOGI("uart_rx_watermark: %d", p->uart_rx_watermark);
+       ALOGI("uart_flow_control_thld: %d", p->uart_flow_control_thld);
+       ALOGI("comp_id: 0x%08X", p->comp_id);
+       ALOGI("pcm_clk_divd : 0x%04X", p->pcm_clk_divd );
+
+
+       ALOGI("reserved(0~7): 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->reserved[0], p->reserved[1], p->reserved[2], 
+                       p->reserved[3], p->reserved[4], p->reserved[5], p->reserved[6], p->reserved[7]);
+       return 0;
+}
+#if 0
+static int bt_get_config_ver(unsigned char *pBuf, int len)
+{
+       int i, p;
+       cmd_par cmd;
+       int ret = -1;
+       for(i = 0, p = 0; i < len; i++)
+       {
+               if( ('\n' == *(pBuf + i)) || ( '\r' == *(pBuf + i)) || ( '\0' == *(pBuf + i) )   )
+               {
+                       if(5 <= (i - p) )
+                       {
+                               bt_getCmdOneline((pBuf + p), &cmd);
+                               if( 0 == strcmp(cmd.item, "version") )
+                               {
+                                       ret = cmd.par[0];
+                                       break;
+                               }
+                               memset(&cmd, 0, sizeof(cmd_par) );
+                       }
+                       p = i + 1;
+               }
+
+       }
+       return ret;
+}
+#endif
+int bt_getPskeyFromFile(void *pData)
+{
+       int ret = -1;
+       int fd;
+       unsigned char *pBuf = NULL;
+       int len;
+
+       ALOGI("begin to bt_getPskeyFromFile");
+       fd = open(PSKEY_PATH, O_RDONLY, 0644);
+       if(-1 != fd)
+       {
+               len = bt_getFileSize(PSKEY_PATH);
+               pBuf = (unsigned char *)malloc(len);
+               ret = read(fd, pBuf, len);
+               if(-1 == ret)
+               {
+                       ALOGE("%s read %s ret:%d\n", __FUNCTION__, PSKEY_PATH, ret);
+                       free(pBuf);
+                       close(fd);
+                       return -1;
+               }
+               close(fd);
+       }
+       else
+       {
+               ALOGE("%s open %s ret:%d\n", __FUNCTION__, PSKEY_PATH, fd);
+               return -1;
+       }
+
+       ret = bt_getDataFromBuf(pData, pBuf, len);
+       if(-1 == ret)
+       {
+               free(pBuf);
+               return -1;
+       }
+       ALOGI("begin to dumpPskey");
+       bt_dumpPskey((BT_PSKEY_CONFIG_T *)pData);
+       free(pBuf);
+       return 0;
+}
+
+
index b1cbcfd..0881c6f 100755 (executable)
@@ -1219,7 +1219,11 @@ static int add_sp(sdp_session_t *session, svc_info_t *si)
        sdp_set_service_classes(&record, svclass_id);
 
        sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       profile.version = 0x0102;
+#else
        profile.version = 0x0100;
+#endif
        profiles = sdp_list_append(0, &profile);
        sdp_set_profile_descs(&record, profiles);
 
index 9b54426..57df7fb 100755 (executable)
@@ -544,7 +544,11 @@ static const char *test_table[] = {
 
 static void run_command(char *cmdname, char *home)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char *argv[10], *envp[3];
+#else
        char *argv[9], *envp[3];
+#endif
        int pos = 0, idx = 0;
        int serial_fd;
        pid_t pid, dbus_pid, daemon_pid;
diff --git a/tools/update_compids.sh b/tools/update_compids.sh
new file mode 100755 (executable)
index 0000000..95c961d
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/bash
+# Download the list of company IDs from bluetooth.org and generate a diff which
+# can be applied to source tree to update bt_compidtostr(). Usage:
+#
+# 1) ./tools/update_compids.sh | git apply -p0
+# 2) Inspect changes to make sure they are sane
+# 3) git commit -m "lib: Update list of company identifiers" lib/bluetooth.c
+#
+# Requires html2text: http://www.mbayer.de/html2text/
+#
+set -e -u
+
+tmpdir=$(mktemp -d)
+trap "rm -rf $tmpdir" EXIT
+
+mkdir $tmpdir/lib
+cp lib/bluetooth.c $tmpdir/lib/bluetooth.c.orig
+cp lib/bluetooth.c $tmpdir/lib/bluetooth.c
+
+cd $tmpdir
+
+path=en-us/specification/assigned-numbers/company-identifiers
+# Use "iconv -c" to strip unwanted unicode characters
+# Fixups:
+# - strip <input> tags of type "checkbox" because html2text generates UTF-8 for
+#   them in some distros even when using -ascii (e.g. Fedora)
+# - replace "&#160;" (non-breaking space) with whitespace manually, because
+#   some versions incorrectly convert it into "\xC2\xA0"
+curl https://www.bluetooth.org/$path | iconv -c -f utf8 -t ascii | \
+    sed '/<input.*type="checkbox"/d; s/&#160;/ /g' | \
+    html2text -ascii -width 160 -o identifiers.txt >/dev/null
+
+# Some versions of html2text do not replace &amp; (e.g. Fedora)
+sed -i 's/&amp;/\&/g' identifiers.txt
+
+sed -n '/^const char \*bt_compidtostr(int compid)/,/^}/p' \
+    lib/bluetooth.c > old.c
+
+echo -e 'const char *bt_compidtostr(int compid)\n{\n\tswitch (compid) {' > new.c
+cat identifiers.txt |
+    perl -ne 'm/^(\d+)\s+0x[0-9a-f]+\s+(.*)/i &&
+        print "\tcase $1:\n\t\treturn \"$2\";\n"' >> new.c
+if ! grep -q "return \"" new.c; then
+    echo "ERROR: could not parse company IDs from bluetooth.org" >&2
+    exit 1
+fi
+if [ -n "$(tr -d '[:print:]\t\n' < new.c)" ]; then
+    echo -n "ERROR: invalid non-ASCII characters found while parsing" >&2
+    echo -n " company IDs. Please identify offending sequence and fix" >&2
+    echo " tools/update_compids.sh accordingly." >&2
+    exit 1
+fi
+echo -e '\tcase 65535:\n\t\treturn "internal use";' >> new.c
+echo -e '\tdefault:\n\t\treturn "not assigned";\n\t}\n}' >> new.c
+
+diff -Naur old.c new.c | patch -sp0 lib/bluetooth.c
+diff -Naur lib/bluetooth.c.orig lib/bluetooth.c
diff --git a/tools/valgrind.supp b/tools/valgrind.supp
new file mode 100755 (executable)
index 0000000..9efb6f1
--- /dev/null
@@ -0,0 +1,27 @@
+{
+   ecb_bind
+   Memcheck:Param
+   socketcall.bind(my_addr.sa_data)
+   fun:bind
+   fun:ecb_aes_setup
+}
+{
+   cmac_bind
+   Memcheck:Param
+   socketcall.bind(my_addr.sa_data)
+   fun:bind
+   fun:cmac_aes_setup
+}
+{
+   logging_open
+   Memcheck:Param
+   socketcall.bind(my_addr.rc_bdaddr)
+   fun:bind
+   fun:logging_open
+}
+{
+   bind
+   Memcheck:Param
+   socketcall.bind(my_addr.rc_channel)
+   fun:bind
+}