Project import
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..bc6fea3
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,110 @@
+#
+#    Copyright (c) 2010-2011 Nest, Inc.
+#    All rights reserved.
+#
+#    This document is the property of Nest. It is considered
+#    confidential and proprietary information.
+#
+#    This document may not be reproduced or transmitted in any form,
+#    in whole or in part, without the express written permission of
+#    Nest.
+#
+#    Description:
+#      This file is the makefile for BlueZ, the official Linux
+#      Bluetooth protocol stack.
+#
+
+
+include pre.mak
+
+PackageName		:= bluez
+
+PackageSourceDir	:= repo
+
+PackageBuildMakefile	= $(call GenerateBuildPaths,Makefile)
+
+CleanPaths		+= $(PackageLicenseFile)
+
+SOURCEDIRS                      = $(PackageSourceDir)
+$(PackageSourceDir)_RULE_TARGET = $(BuildDirectory)/configure
+
+all: $(PackageDefaultGoal)
+
+# Generate the package license contents.
+
+$(PackageSourceDir)/LICENSE: $(BuildDirectory)/source
+
+$(PackageLicenseFile): $(PackageSourceDir)/LICENSE
+	$(copy-result)
+
+# Prepare the sources.
+
+.PHONY: source
+source: $(BuildDirectory)/source
+$(BuildDirectory)/source: | $(PackageSourceDir) $(BuildDirectory)
+	$(Verbose)$(call create-links,$(CURDIR)/$(PackageSourceDir),$(BuildDirectory))
+	$(Verbose)touch $@
+
+# Generate the package's build makefile
+
+# Configure the source for building.
+
+.PHONY: configure
+configure: $(BuildDirectory)/configure
+$(BuildDirectory)/configure: $(BuildDirectory)/source | $(PackageSourceDir) $(BuildDirectory)
+	$(Verbose)cd $(BuildDirectory) && ./bootstrap
+	$(Verbose)cd $(BuildDirectory) && ./configure \
+	CC="$(CC) $(CPPOPTFLAGS)" CXX="$(CXX) $(CPPOPTFLAGS)" AR=$(AR) NM=$(NM) RANLIB=$(RANLIB) STRIP=$(STRIP) INSTALL="$(INSTALL) $(INSTALLFLAGS)" \
+	CPPFLAGS="$(call ToolGenerateDefineArgument,FIRMWARE_DIR=\\\"/lib/firmware/\\\") \
+	          $(call ToolGenerateIncludeArgument,$(LinuxIncludePath))" \
+	DBUS_CFLAGS="$(call ToolGenerateIncludeArgument,$(DbusIncludePaths))" \
+	GLIB_CFLAGS="$(call ToolGenerateIncludeArgument,$(GlibIncludePaths))" \
+	UDEV_CFLAGS="$(call ToolGenerateIncludeArgument,$(UdevIncludePaths))" \
+	DBUS_LIBS="$(call GenerateLibraryArgument,$(DbusLibraryPath))" \
+	GLIB_LIBS="$(call GenerateLibraryArgument,$(GlibLibraryPath))" \
+	UDEV_LIBS="$(call GenerateLibraryArgument,$(UdevLibraryPath))" \
+	PKG_CONFIG_PATH="$(GlibPkgConfigPath):$(DbusPkgConfigPath):$(UdevPkgConfigPath)" \
+	--build=$(HostTuple) \
+	--host=$(TargetTuple) \
+	--prefix=/usr \
+	--sysconfdir=/etc \
+	--localstatedir=/var \
+	--disable-cups \
+	--disable-monitor \
+	--disable-client \
+	--disable-systemd \
+	--disable-obex \
+	$(if $(BUILD_FEATURE_BLUEZ_LIB), --enable-library) \
+	$(NULL)
+	$(Verbose)touch $@
+
+# Build the source.
+#
+# We have to unset MAKEFLAGS since they confuse the package build otherwise.
+
+.PHONY: build
+build: $(BuildDirectory)/build
+$(BuildDirectory)/build: $(BuildDirectory)/configure
+	$(Verbose)unset MAKEFLAGS && \
+	$(MAKE) $(JOBSFLAG) -C $(BuildDirectory) \
+	all
+	$(Verbose)touch $@
+
+# Stage the build to a temporary installation area.
+#
+# We have to unset MAKEFLAGS since they confuse the package build otherwise.
+
+$(BuildDirectory)/stage: $(BuildDirectory)/build | $(ResultDirectory)
+	$(Verbose)unset MAKEFLAGS && \
+	$(MAKE) $(JOBSFLAG) -C $(BuildDirectory) \
+	DESTDIR=$(ResultDirectory) \
+	install
+	$(Verbose)touch $@
+
+stage: $(BuildDirectory)/stage
+
+clean:
+	$(Verbose)$(RM) $(RMFLAGS) -r $(BuildDirectory)
+	$(Verbose)$(RM) $(RMFLAGS) -r $(ResultDirectory)
+
+include post.mak
diff --git a/repo/.mailmap b/repo/.mailmap
new file mode 100644
index 0000000..a7e36e3
--- /dev/null
+++ b/repo/.mailmap
@@ -0,0 +1,11 @@
+Luiz Augusto von Dentz <luiz.dentz-von@nokia.com>	<luiz.dentz-von@nokia.com>
+Vinicius Costa Gomes <vinicius.gomes@openbossa.org>	<vinicius.gomes@openbossa.org>
+Elvis Pfützenreuter <epx@signove.com>			<epx@signove.com>
+Santiago Carot-Nemesio <scarot@libresoft.es>		<scarot@libresoft.es>
+José Antonio Santos Cadenas <santoscadenas@gmail.com>	<santoscadenas@gmail.com>
+Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>	<Waldemar.Rymarkiewicz@tieto.com>
+Alok Barsode <alokbarsode@gmail.com>			<alok@greatbear.(none)>
+André Dieb Martins <andre.dieb@signove.com>		<andre.dieb@signove.com>
+Tedd Ho-Jeong An <tedd.an@intel.com>			<tedd.an@intel.com>
+Martin Xu <martin.xu@linux.intel.com>			<martin.xu@linux.intel.com>
+Marie Janssen <jamuraa@chromium.org>			<jamuraa@chromium.org>
diff --git a/repo/AUTHORS b/repo/AUTHORS
new file mode 100644
index 0000000..16fb14c
--- /dev/null
+++ b/repo/AUTHORS
@@ -0,0 +1,101 @@
+Maxim Krasnyansky <maxk@qualcomm.com>
+Marcel Holtmann <marcel@holtmann.org>
+Stephen Crane <steve.crane@rococosoft.com>
+Jean Tourrilhes <jt@hpl.hp.com>
+Jan Beutel <j.beutel@ieee.org>
+Ilguiz Latypov <ilatypov@superbt.com>
+Thomas Moser <thomas.moser@tmoser.ch>
+Nils Faerber <nils@kernelconcepts.de>
+Martin Leopold <martin@leopold.dk>
+Wolfgang Heidrich <wolfgang.heidrich@esk.fhg.de>
+Fabrizio Gennari <fabrizio.gennari@philips.com>
+Brad Midgley <bmidgley@xmission.com>
+Henryk Ploetz <henryk@ploetzli.ch>
+Philip Blundell <pb@nexus.co.uk>
+Johan Hedberg <johan.hedberg@intel.com>
+Claudio Takahasi <claudio.takahasi@indt.org.br>
+Eduardo Rocha <eduardo.rocha@indt.org.br>
+Denis Kenzior <denis.kenzior@trolltech.com>
+Frederic Dalleau <frederic.dalleau@access-company.com>
+Frederic Danis <frederic.danis@access-company.com>
+Luiz Augusto von Dentz <luiz.dentz@gmail.com>
+Fabien Chevalier <fabchevalier@free.fr>
+Ohad Ben-Cohen <ohad@bencohen.org>
+Daniel Gollub <dgollub@suse.de>
+Tom Patzig <tpatzig@suse.de>
+Kai Vehmanen <kai.vehmanen@nokia.com>
+Vinicius Gomes <vinicius.gomes@openbossa.org>
+Alok Barsode <alok.barsode@azingo.com>
+Bastien Nocera <hadess@hadess.net>
+Albert Huang <albert@csail.mit.edu>
+Glenn Durfee <gdurfee@google.com>
+David Woodhouse <david.woodhouse@intel.com>
+Christian Hoene <hoene@uni-tuebingen.de>
+Pekka Pessi <pekka.pessi@nokia.com>
+Siarhei Siamashka <siarhei.siamashka@nokia.com>
+Nick Pelly <npelly@google.com>
+Lennart Poettering <lennart@poettering.net>
+Gustavo Padovan <gustavo@padovan.org>
+Marc-Andre Lureau <marc-andre.lureau@nokia.com>
+Bea Lam <bea.lam@nokia.com>
+Zygo Blaxell <zygo.blaxell@xandros.com>
+Forrest Zhao <forrest.zhao@intel.com>
+Scott Talbot <psyc@stalbot.com>
+Ilya Rubtsov <lusyaru@gmail.com>
+Mario Limonciello <mario_limonciello@dell.com>
+Filippo Giunchedi <filippo@esaurito.net>
+Jaikumar Ganesh <jaikumar@google.com>
+Elvis Pfutzenreuter <epx@signove.com>
+Santiago Carot-Nemesio <scarot@libresoft.es>
+José Antonio Santos Cadenas <jcaden@libresoft.es>
+Francisco Alecrim <francisco.alecrim@openbossa.org>
+Daniel Orstadius <daniel.orstadius@gmail.com>
+Anderson Briglia <anderson.briglia@openbossa.org>
+Anderson Lizardo <anderson.lizardo@openbossa.org>
+Bruna Moreira <bruna.moreira@openbossa.org>
+Brian Gix <bgix@codeaurora.org>
+Andre Guedes <andre.guedes@openbossa.org>
+Sheldon Demario <sheldon.demario@openbossa.org>
+Lucas De Marchi <lucas.demarchi@profusion.mobi>
+Szymon Janc <szymon.janc@codecoup.pl>
+Syam Sidhardhan <s.syam@samsung.com>
+Paulo Alcantara <pcacjr@gmail.com>
+Jefferson Delfes <jefferson.delfes@openbossa.org>
+Andrzej Kaczmarek <andrzej.kaczmarek@codecoup.pl>
+Eder Ruiz Maria <eder.ruiz@openbossa.org>
+Mikel Astiz <mikel.astiz@bmw-carit.de>
+Chan-yeol Park <chanyeol.park@samsung.com>
+João Paulo Rechi Vita <jprvita@gmail.com>
+Larry Junior <larry.junior@openbossa.org>
+Raymond Liu <raymond.liu@intel.com>
+Radoslaw Jablonski <ext-jablonski.radoslaw@nokia.com>
+Rafal Michalski <michalski.raf@gmail.com>
+Dmitriy Paliy <dmitriy.paliy@nokia.com>
+Bartosz Szatkowski <bulislaw@linux.com>
+Lukasz Pawlik <lucas.pawlik@gmail.com>
+Slawomir Bochenski <lkslawek@gmail.com>
+Wayne Lee <waynelee@qualcomm.com>
+Ricky Yuen <ryuen@qualcomm.com>
+Takashi Sasai <sasai@sm.sony.co.jp>
+Andre Dieb Martins <andre.dieb@signove.com>
+Cristian Rodríguez <crrodriguez@opensuse.org>
+Alex Deymo <deymo@chromium.org>
+Petri Gynther <pgynther@google.com>
+Scott James Remnant <scott@netsplit.com>
+Jakub Tyszkowski <jakub.tyszkowski@tieto.com>
+Grzegorz Kołodziejczyk <grzegorz.kolodziejczyk@tieto.com>
+Marcin Krąglak <marcin.kraglak@tieto.com>
+Łukasz Rymanowski <lukasz.rymanowski@codecoup.pl>
+Jerzy Kasenberg <jerzy.kasenberg@tieto.com>
+Arman Uguray <armansito@chromium.org>
+Artem Rakhov <arakhov@chromium.org>
+Mike Ryan <mikeryan@lacklustre.net>
+David Herrmann <dh.herrmann@gmail.com>
+Jacob Siverskog <jacob@teenageengineering.com>
+Sebastian Chłąd <sebastian.chlad@tieto.com>
+Alex Gal <a.gal@miip.ca>
+Loic Poulain <loic.poulain@intel.com>
+Gowtham Anandha Babu <gowtham.ab@samsung.com>
+Bharat Panda <bharat.panda@samsung.com>
+Marie Janssen <jamuraa@chromium.org>
+Jaganath Kanakkassery <jaganath.k@samsung.com>
diff --git a/repo/COPYING b/repo/COPYING
new file mode 100644
index 0000000..6d45519
--- /dev/null
+++ b/repo/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/repo/COPYING.LIB b/repo/COPYING.LIB
new file mode 100644
index 0000000..1f7c8cc
--- /dev/null
+++ b/repo/COPYING.LIB
@@ -0,0 +1,504 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/repo/ChangeLog b/repo/ChangeLog
new file mode 100644
index 0000000..c7a4fc0
--- /dev/null
+++ b/repo/ChangeLog
@@ -0,0 +1,2107 @@
+ver 5.37:
+	Fix issue with registering external profiles.
+	Fix issue with connecting external profiles.
+	Fix issue with GATT service changed handling.
+	Fix issue with not emitting GattServices update.
+	Convert to unified HID over GATT profile support.
+	Convert to KeyboardDisplay as default IO capability.
+	Install btattach utility by default.
+
+ver 5.36:
+	Fix issue with PBAP headers for size query.
+	Fix issue with AVRCP current player handling.
+	Fix issue with device information handling.
+	Fix issue with device disconnect handling.
+	Fix issue with duplicate connect handling.
+	Fix issue with attribute claiming for drivers.
+
+ver 5.35:
+	Fix issue with connected devices after discovery.
+	Fix issue with profile support and LTK loading.
+	Fix issue with AVRCP events for volume control.
+	Fix issue with OBEX session owner handling.
+	Fix issue with HID over GATT setup failures.
+	Fix issue with GATT notification registration.
+	Fix issue with GATT cache validation feature.
+	Add support for persistent GATT database.
+	Add support for controller enabling option.
+
+ver 5.34:
+	Fix issue with GATT profiles and auto-connect.
+	Fix issue with missing GoepL2CapPsm SDP data.
+	Fix issue with suspending AVDTP endpoints.
+	Fix issue with audio service state on disconnect.
+	Add support for AVRCP Set Addressed Player feature.
+	Add support for AVRCP Get Folder Items feature.
+	Add support for Android 5.1 HFP WBS callbacks.
+
+ver 5.33:
+	Fix issue with memory leak in GATT database.
+	Fix issue with AVDTP set configuration handling.
+	Fix issue with AVDTP discover procedure.
+	Fix issue with not emitting Paired property.
+
+ver 5.32:
+	Fix issue with OPP GET request path handling.
+	Fix issue with ATT information request errors.
+	Fix issue with advertising instance numbers.
+	Fix issue with overwriting SDP record cache.
+	Fix issue with new connections during disconnect.
+	Add support for GATT security auto-elevation.
+
+ver 5.31:
+	Fix issue with crash in networking interface.
+	Fix issue with crash when creating endless GATT loops.
+	Fix issue with memory leak when connecting services.
+	Fix issue with memory leak creating new D-Bus proxy.
+	Fix issue with profile connections from remote devices.
+	Fix issue with GATT over BR/EDR and MTU notification.
+	Fix issue with HID and dual mode remote devices.
+	Fix issue with handling A2DP vendor codec setup.
+	Fix issue with AVRCP and syncing player state.
+	Fix issue with GATT secondary discovery handling.
+	Fix issue with wrong characteristic allocation.
+	Add support for handling BNEP setup response.
+	Add support for setting GATT database security flags.
+	Add support for setting discovery filters interface.
+	Add support for user controlled advertising interface.
+	Update Android qualification documentation to PTS 6.1 release.
+
+ver 5.30:
+	Fix compilation error in C++ due to inline function.
+	Fix issue with missing storage of device information.
+	Fix issue with GATT client and gaps in service handles.
+	Fix issue with AVDTP discovery callback crashing.
+	Fix issue with AVCTP channel handling in case of conflicts.
+	Fix issue with AVRCP target and get capabilities command.
+	Add experimental support for LE advertising manager API.
+	Add support for Android 5.1 GATT MTU exchange API.
+
+ver 5.29:
+	Fix issue with AVCTP initial key repeat timeout.
+	Fix issue with Android application disconnect handling.
+	Fix issue with Android support and service notifications.
+	Fix issue with Android support and Exchange MTU Request.
+	Fix issue with Android HFP support and AT+CMER handling.
+	Fix issue with Android HFP support and SLC setup.
+	Fix issue with Android HFP support and call hold status.
+	Fix issue with Android HFP support and indicator handling.
+	Fix issue with Android HFP support and SCO/eSCO disconnection.
+	Fix issue with Android HID over GATT support and battery service.
+	Fix issue with GATT sending Exchange MTU Request for BR/EDR.
+	Fix issue with GATT notification support without CCC.
+	Fix issue with GATT object life-time after disconnects.
+	Fix issue with GATT notification handling API.
+	Add experimental support for GATT client D-Bus API.
+	Add experimental support for GATT server D-Bus API.
+	Add support for Multi Profile Specification.
+	Update Android qualification documentation to PTS 6.0 release.
+
+ver 5.28:
+	Fix issue with GATT device discovery and probing.
+	Fix issue with bearer selection for dual-mode devices.
+	Fix issue with device removal while connected.
+	Fix issue with device name setting from inquiry response.
+	Fix issue with missing termination of name characteristic.
+	Fix issue with UTF-8 length handling for device name.
+	Fix issue with AVCTP key auto release handling.
+	Fix issue with AVCTP key press repetition handling.
+	Fix issue with payload sizes and GATT notifications.
+	Fix issue with memory corruption and GATT notifications.
+	Add support for HID proxy switching and CSR 8510 A10 devices.
+	Add support for Broadcom hex2hcd conversion utility.
+
+ver 5.27:
+	Fix issue with endian handling and management interface.
+	Fix issue with pending GATT operations when disconnecting.
+	Fix issue with 128-bit UUID conversions for HID over GATT.
+	Add support for Android 5.0 SELinux policies.
+
+ver 5.26:
+	Fix issue with handling A2DP XCASE connection state.
+	Fix issue with crash and A2DP configuration failures.
+	Fix issue with crash during OBEX session shutdown.
+	Add support for version 1.2 of Phonebook Access Profile.
+	Add support for HID over GATT get and set report handling.
+	Add support for Low Energy Secure Connections feature.
+	Add support for Bluetooth 4.2 commands and events.
+	Add support for Android 5.0 Bluetooth features.
+
+ver 5.25:
+	Fix issue with SCO connection after codec negotiation.
+	Fix issue with GATT and secondary service discovery.
+	Fix issue with GATT write descriptor callback.
+	Fix issue with MAP supported features bits.
+	Add support for MAP local time and timezone offset.
+	Add support for PBAP speed-dial and favorites folders.
+	Add support for PBAP speed-dial and identifier filters.
+	Add support for controller mode configuration option.
+	Add initial support for Android Lollipop features.
+
+ver 5.24:
+	Fix issue with storing of connection parameters.
+	Add support for Phonebook Access Profile 1.2 features.
+	Add support for Message Access Profile 1.2 event reports.
+	Add support for Android Bluetooth configuration options.
+
+ver 5.23:
+	Fix issue with concurrent authorization requests.
+	Fix issue with HID report identifier mismatch.
+	Fix issue with crash when receiving uHID events.
+	Fix issue with crash and OBEX disconnect handling.
+	Fix issue with OBEX client transfers and suspend.
+	Fix issue with parsing of MAP application parameters.
+	Fix issue with devices rejecting AVRCP GetCapabilities.
+	Add support for kernel whitelist and Android Bluetooth.
+
+ver 5.22:
+	Fix issue with UHID_OUTPUT events mapping.
+	Fix issue with UHID_FEATURE events handling.
+	Fix issue with UINT32_MAX overflow and AVRCP.
+	Fix issue when dirent type DT_UNKNOWN is returned.
+	Add support for kernel whitelist filtering feature.
+	Add support for Android Bluetooth GATT over BR/EDR.
+
+ver 5.21:
+	Fix issue with SDP requests and wrong PDU size.
+	Fix issue with handling passive scanning triggers.
+	Add support for storing and loading connection parameters.
+	Add support for kernel background auto-connection feature.
+	Add support for Android Bluetooth Scan Parameters feature.
+	Add support for Android Bluetooth Device Information feature.
+	Add support for Android Bluetooth Health Device interface.
+
+ver 5.20:
+	Fix issue with LED handling of PS3 controllers.
+	Add support for Android Bluetooth GATT server interface.
+	Add support for Android Bluetooth HID over GATT feature.
+	Add support for Android Bluetooth multi-profile feature.
+	Add support for Android Bluetooth aptX audio integration.
+
+	Note: aptX codec not included
+
+ver 5.19:
+	Fix issue with OBEX Put-Delete and Create-Empty methods.
+	Fix issue with AVRCP browsable/searchable player properties.
+	Fix issue with handling multiple default agents.
+	Fix issue with handling unpair event per bearer.
+	Fix issue with HID over GATT report ID presence.
+	Add support for HID protocol handling in userspace.
+	Add support for Bluetooth reconnection policy framework.
+	Add support for Android Bluetooth SCO over HCI transport.
+	Add support for Android Bluetooth audio quality control.
+	Add support for Android Bluetooth Low Energy only mode.
+
+ver 5.18:
+	Fix issue with identifying LE single mode devices.
+	Fix issue with L2CAP and RFCOMM peer address lookup.
+	Add support for handling OBEX authentication procedure.
+	Add support for Android Bluetooth GATT client interface.
+
+ver 5.17:
+	Fix issue with not resetting OBEX SRM setup.
+	Fix issue with BR/EDR devices and auto-connect list.
+	Fix issue with bonding complete detection as peripheral.
+	Fix issue with not updating bearer timestamp of connections.
+	Fix issue with paired property for multiple bearers.
+	Add support for Android Bluetooth Handsfree interface.
+	Add support for Android Bluetooth Wideband speech.
+
+ver 5.16:
+	Fix issue with HID over GATT physical location.
+	Fix issue with HID over GATT unique identifier.
+	Fix issue with missing paired property notification.
+	Fix issue with endianess of long term key storage.
+	Add support for storing signature resolving keys.
+	Add support for Android Bluetooth AVRCP interface.
+
+ver 5.15:
+	Fix issue with LE enabling and background scanning.
+	Fix issue with HID over GATT input device name.
+	Fix issue with storage of slave long term keys.
+	Add support for handling identity resolving keys.
+	Add support for Android Bluetooth A2DP interface.
+	Add support for Android Bluetooth audio interface.
+
+ver 5.14:
+	Fix issue with marking PS3 controllers as trusted.
+	Fix issue with authorization of PS3 controllers.
+	Add support for DualShock 4 controller detection.
+	Add support for legacy pairing emulation.
+	Add support for secure simple pairing emulation.
+	Add support for automated pairing testing.
+	Add support for RFCOMM protocol testing.
+	Add support for HCI controller testing.
+
+ver 5.13:
+	Fix issue with PS3 controller detection.
+	Add support for data transfers to L2CAP testing tool.
+	Add support for delay reporting to AVDTP testing tool.
+	Add support for Android Bluetooth Core interface.
+	Add support for Android Bluetooth Socket interface.
+	Add support for Android Bluetooth HID Host interface.
+	Add support for Android Bluetooth PAN interface.
+
+ver 5.12:
+	Fix issue with missing reply to DisconnectProfile.
+	Fix issue with icon property and class of device changes.
+	Fix issue with HID devices when SDP record is not available.
+	Fix issue with handling auto-pairing of printers.
+	Fix issue with agent authorization handling.
+	Add support for PS3 controller setup and pairing.
+	Add support for LE L2CAP CoC test capabilities.
+	Add support for AVDTP qualification test cases.
+	Add support for SMP cryptographic test cases.
+
+ver 5.11:
+	Fix issue with connection attempt when not powered.
+	Fix issue with assigning player to AVRCP target role.
+	Fix issue with OBEX default cache directory.
+	Fix issue with SDP search error handling.
+	Fix issue with processing of SDP records.
+	Fix issue with HID to HCI switching utility.
+	Fix issue with mgmt end-to-end testing tool.
+	Fix issue with L2CAP end-to-end testing tool.
+	Add support for SMP end-to-end testing tool.
+	Add support for more Wii controllers.
+
+ver 5.10:
+	Fix issue with discoverable timeout handling.
+	Fix issue with MAP messages and record version.
+	Fix issue with MAP messages and status events.
+	Fix issue with MAP messages and relative folders.
+	Fix issue with MAP messages and type property signals.
+	Fix issue with transfer size for OBEX GET operations.
+	Fix issue with AVRCP service class identifier.
+	Fix issue with AVRCP tracking seeked signal.
+	Add support for OBEX command line client.
+
+ver 5.9:
+	Fix issue with network service and adapter removal.
+	Fix issue with misleading OBEX error messages.
+	Fix issue with OBEX transport reference handling.
+	Fix issue with memory leak with MAP event handler.
+	Fix issue with missing MAP property changed signal.
+	Fix issue with message type property values.
+	Fix issue with empty UUID list for devices.
+	Fix issue with profile agent cancel method.
+	Remove dependency on USB library.
+
+ver 5.8:
+	Fix issue with missing OBEX session properties.
+	Fix issue with missing SDP service refresh.
+	Fix issue with SDP attribute range check.
+	Fix issue with priority for SDP transactions.
+	Fix issue with service discovery after pairing.
+	Fix issue with race condition in service list.
+	Fix issue with input service state transition.
+	Fix issue with default authorization for profiles.
+	Fix issue with AVRCP browsing channel connections.
+	Add support for AVRCP role agnostic sessions.
+
+ver 5.7:
+	Fix issue with missing UUID discovery during pairing.
+	Fix issue with broken patch for SDP range check handling.
+	Fix issue with AVRCP usage of UID=0 for paused/stopped.
+	Add support MAP notification dispatching.
+
+ver 5.6:
+	Fix issue with incoming connections without SDP record.
+	Fix issue with canceling ongoing device connections.
+	Fix issue with handling failed connection attempts.
+	Fix issue with pending resume during A2DP open failures.
+	Fix issue with registering AVRCP unsupported notification.
+	Fix issue with listing available AVRCP target settings.
+	Fix issue with missing error for OBEX SetPath commands.
+	Fix issue with missing OBEX session command queue.
+	Fix issue with retrieving multiple MAP event reports.
+	Add support for command line player utility.
+
+ver 5.5:
+	Fix issue with race condition between SDP and properties.
+	Fix issue with handling storage of private device addresses.
+	Fix issue with NFC out-of-band pairing and power states.
+	Fix issue with short name during device update handling.
+	Fix issue with handling AVRCP without A2DP being present.
+	Add support for handling AVRCP pass-through operations.
+	Add support for automatically reconnecting HID devices.
+	Add support for automatically pairing of devices.
+
+ver 5.4:
+	Fix issue with invalid memory access and SDP service search.
+	Add support for available player changed event for controller.
+	Add support for UIDs changed event for AVRCP controller.
+	Add support for mandatory AVRCP pass-through operations.
+	Add support for Message Notification Service (MNS) server.
+	Add support for agent methods within command line client.
+
+ver 5.3:
+	Fix issue with registering invalid profiles.
+	Fix issue with inconsistent A2DP transport state.
+	Fix issue with A2DP resume while in configured state.
+	Fix issue with buffer overflow when processing SDP response.
+	Fix issue with missing range check for SDP attribute response.
+	Fix issue with missing validation of SDP data elements.
+	Fix issue with missing fallback to static hostname.
+	Fix issue with default adapter assignment.
+
+ver 5.2:
+	Fix issue with connection handling for Low Energy.
+	Fix issue with broken device discovery handling.
+	Fix issue with invalid memory access within A2DP.
+	Fix issue with handling empty path name of SetPath.
+	Fix issue with handling Message Access Profile filters.
+	Fix issue with handling network service unregistration.
+	Fix issue with not handling bogus device pairing results.
+	Fix issue with initial service discovery and profile manager.
+	Add support for AVRCP volume notifications.
+	Add support for AVRCP browsing commands.
+
+ver 5.1:
+	Fix issue with crash when removing OBEX session.
+	Fix issue with HID device disconnected from kernel.
+	Fix issue with buffer overflow when parsing HID SDP record.
+	Fix issue with SDP_TEXT_STR16 and SDP_URL_STR16 parsing.
+	Add support for integration with systemd's hostname daemon.
+	Add support for separate adapter alias property.
+	Add support for adapter and device modalias properties.
+	Add support for official BlueZ device information.
+	Add support for asynchronous management interface handling.
+	Add tool for testing management interface compliance.
+	Add tool for testing SDP qualification requirements.
+	Add tool for testing various EIR and AD data records.
+
+ver 5.0:
+	Introduce D-Bus Properties and ObjectManager interfaces.
+	Add support for generic profile interface.
+	Add support for global agent interface.
+	Add support for integrated OBEX daemon.
+	Add support for integrated hcidump utility.
+	Add support for Bluetooth tracing and monitor utility.
+	Add support for Bluetooth command line client utility.
+	Remove support for Handsfree gateway handling.
+	Remove support for GStreamer A2DP and SBC elements.
+	Disable default installation of Bluetooth library.
+
+ver 4.101:
+	Fix issue with missing BlueZ service file.
+	Fix issue with aborting A2DP setup during AVDTP start.
+	Fix issue with handling of multiple A2DP indication.
+	Fix issue with handling AVDTP abort with invalid SEID.
+	Fix issue with rejecting AVDTP abort commands.
+	Add support for handling AVDTP command collision.
+
+ver 4.100:
+	Fix issue with crashing when SCO connection fails.
+	Fix issue with HFP gateway failing on first GSM connection.
+	Fix issue with AVRCP and handling of vendor commands.
+	Fix issue with handling AVRCP subunit info command.
+	Fix issue with missing capability for AVRCP track reached end.
+	Fix issue with AVDTP signaling and GStreamer SBC NULL check.
+	Fix issue with AVDTP Reconfigure Reject message.
+	Fix issue with incorrect EIR length parsing.
+	Fix issue with SDP disconnect for HIDSDPDisable.
+	Fix issue with SDP interoperability with Mac OS X Lion.
+	Fix issue with reverse SDP discovery with some devices.
+	Fix issue with discovering state during power off operation.
+	Add support for AVRCP Volume Changed notifications.
+	Add support for AVRCP Set Absolute Volume handling.
+	Add support for display legacy PIN code agent method.
+	Add support for multiple media transports per endpoint.
+	Add support for discovering device information characteristics.
+	Add support for vendor source for Device ID setting.
+	Add support for immediate alert server.
+	Add support for link loss server.
+
+	Notes:
+	This version requires D-Bus 1.4 or later.
+	This version requires GLib 2.28 or later.
+
+ver 4.99:
+	Fix issue with missing retries for BNEP connection setup.
+	Fix issue with not showing name if first EIR has no details.
+	Fix issue with running SDP discovery for LE devices.
+	Add support for GATT using 128-bit Bluetooth UUIDs.
+	Add support for retrieving key size information.
+	Add support for storing Long Term Keys.
+	Add support for Proximity Reporter API.
+	Add support for KeyboardDisplay IO capability.
+	Add support for version 1.0 of management API.
+	Add support for monitoring interface.
+
+ver 4.98:
+	Fix issue with adapter list upon initialization failure.
+	Fix issue with missing legacy property for Low Energy.
+	Fix issue with missing EIR information handling.
+	Fix issue with device address type tracking.
+	Fix issue with alert level characteristic.
+	Fix issue with headset shutdown handling.
+	Fix issue with Wiimote address handling.
+	Add support for advanced l2test options.
+	Add support for attribute protocol and multiple adapters.
+
+ver 4.97:
+	Update support for proximity profile.
+	Fix issue with SBC audio decoding quality.
+	Fix multiple issues with HFP support.
+	Fix multiple issues with A2DP support.
+	Fix multiple issues with AVDTP support.
+	Fix multiple issues with AVRCP support.
+	Add support for AVRCP meta-data transfer.
+	Add support for Bluetooth based thermometers.
+
+ver 4.96:
+	Fix issue with race condition in AVDTP stream start.
+	Fix issue with global adapter offline switching.
+	Fix issue with pairing and No Bonding devices.
+	Add support for Nintendo Wii Remote pairing.
+
+ver 4.95:
+	Fix issue with AVCTP replies with invalid PID.
+	Fix issue with AVRCP and unknown packet types.
+	Fix issue with AVRCP not using NOT_IMPLEMENTED correctly.
+	Fix issue with AVDTP discovery if all endpoints are in use.
+	Fix issue with invalid memory writes and media support.
+	Fix issue with not removing device alias and unbonding.
+	Fix issue with device disconnects and offline mode handling.
+	Add support for setting adapter name based on machine-info.
+	Add support for systemd service configuration.
+
+ver 4.94:
+	Fix issue with invalid read of memory in various modules.
+	Fix issue with buffer overflow when sending AVDTP commands.
+	Fix issue with response to vendor dependent AVRCP commands.
+	Fix issue with headset when not able to reply with ERROR.
+	Fix issue with crash when creating a device from storage.
+	Fix issue with handling non UTF-8 devices names.
+	Add support for improved discovery procedure.
+
+ver 4.93:
+	Fix issue with property type and Health Main channel.
+	Fix issue with crash when removing devices.
+	Add support for hid2hci and udev integration.
+
+ver 4.92:
+	Fix issue with handling of A2DP suspend response.
+	Fix issue with crashing when acquiring A2DP stream.
+	Fix issue with missing check for valid SCO before shutdown.
+	Fix issue with waiting for POLLERR when disconnecting SCO.
+	Fix issue with disconnect after primary service discovery.
+	Fix issue with attribute interface registration.
+	Add support for primary services over BR/EDR.
+	Add support for flushable packets of A2DP media.
+
+ver 4.91:
+	Fix issue with LMP version string and hciconfig.
+	Fix issue with missing discovery signal when scanning.
+	Fix issue with wrong state and canceling name resolving.
+	Fix issue with missing check during adapter initialization.
+	Fix issue with missing protocol not supported error and A2DP.
+	Fix issue with crash during driver unregistering and A2DP.
+	Fix issue with crash when receiving AVDTP close command.
+	Fix issue with remote SEP handling when A2DP codec changes.
+	Fix issue with SCO hangup handling and state changes.
+	Fix issue with security level and MCAP instances.
+	Fix issue with memory leak and HDP data channels.
+	Add support for discover characteristics by UUID to gatttool.
+	Add initial support for Out-of-Band association model.
+	Add initial support for SIM Access Profile.
+
+ver 4.90:
+	Fix issue with setting of global mode property.
+	Fix issue with handling of RequestSession responses.
+	Fix issue with TP_BNEP_CTRL_BV_01_C qualification test.
+	Fix issue with too short AVDTP request timeout.
+	Add support for SIM Access Profile manager.
+	Add support for new UUID utility functions.
+	Add support for attribute server notifications.
+	Add support for client characteristic configuration.
+	Update support for interactive GATT utility.
+
+ver 4.89:
+	Fix issue with name resolving when discovery is suspended.
+	Fix issue with parsing flags of advertising report.
+	Fix issue with SEP handling if interface is disabled.
+	Fix issue with device object creation on disconnect event.
+	Fix issue with indicators whenever the driver is initialized.
+	Fix issue with call indicator when parsing call info reply.
+	Fix issue with crash and allowed GATT MTU was too large.
+	Add support for SDP record of Primary GATT services.
+	Add support for interactive mode for GATT utility.
+
+ver 4.88:
+	Fix issue with HID channel reference count handling.
+	Fix issue with daemon exit on badly formatted AT+VTS.
+	Fix issue with crash while parsing of endpoint properties.
+	Fix issue with possible crash on AVDTP Suspend request timeout.
+	Fix issue with stopping inquiry before adapter is initialized.
+	Fix issue with creating device object when connection fails.
+	Fix issue with sending HCIDEVUP when adapter is already up.
+	Fix issue with handling bonding IO channel closing.
+	Fix agent cancellation in security mode 3 situations.
+	Update pairing code to support management interface.
+
+ver 4.87:
+	Fix issue with initialization when adapter is already up.
+	Fix issue with attribute server MTU and incoming connections.
+	Fix issue with duplicate characteristics after discovery.
+
+ver 4.86:
+	Revert wrong fix for SDP PDU size error response.
+	Fix various memory leaks in A2DP and AVDTP support.
+	Add Routing property to MediaTransport interface
+	Add proper tracking mechanism to NREC status.
+	Add READ_BLOB_REQUEST support to attribute server.
+
+ver 4.85:
+	Fix issue with event mask setting for older adapters.
+	Fix issue with device creation and pairing failures.
+	Add support for telephony support via oFono.
+	Add support for characteristic security level.
+	Update support for service registration.
+
+ver 4.84:
+	Fix issue with wrong parameters and device found signals.
+	Fix issue with leaking EIR data if RSSI does not change.
+	Fix issue with adapter initialization state.
+	Fix issue with closing of SDP server sockets.
+
+ver 4.83:
+	Fix issue with already connected HFP/HSP endpoints.
+	Fix missing reply when create device is canceled.
+	Fix memory leak within the attribute server.
+	Fix memory leak with unused extended inquiry name.
+	Fix setting paired state when device->authr is false.
+	Fix clearing authentication request for renewed keys.
+	Add support for storing link keys in runtime memory.
+	Update support for primary service discovery.
+
+ver 4.82:
+	Fix crash with mmap of files with multiples of page size.
+	Fix HFP response and hold (AT+BTRH) command response.
+	Fix device creation error response when powered off.
+	Fix device removal when connecting/browsing fails.
+	Add initial attribute permission implementation.
+	Add AVDTP SRC stream send buffer size verification.
+	Add support for setting link policy based on features.
+
+ver 4.81:
+	Fix issue with telephony driver initialization.
+	Fix issue with adapter services list initialization.
+	Fix crash after simultaneous authentication requests.
+	Add support for primary service search on device creation.
+
+ver 4.80:
+	Fix legacy link key storing for some buggy adapters.
+	Fix invalid memory access when EIR field length is zero.
+	Fix adapter initialization to wait for kernel HCI commands.
+	Fix initialization of adapters which are already up.
+	Fix possible race condition when initializing adapters.
+	Fix possible crashes when attempting to connect AVDTP.
+	Fix not aborting sink stream configuration on disconnect.
+	Fix not indicating disconnected state when connecting to AVDTP.
+	Fix not dropping AVDTP session when canceling stream setup.
+	Fix AVDTP abort not being send when the state is idle.
+	Fix regression with Low Energy and interleave discovery.
+	Add a new configuration option to disable Low Energy support.
+	Add iwmmxt optimization for SBC for ARM PXA series CPUs.
+	Update support for GATT Primary Service Discovery.
+	Update MCAP and HDP support.
+
+ver 4.79:
+	Fix issue with adapter initialization race condition.
+	Update new Bluetooth Management interface support.
+
+ver 4.78:
+	Fix various issues with AVDTP timer handling.
+	Fix various issues with handling of mode changes.
+	Fix issue with audio disconnect watch in connecting state.
+	Fix issue with handling call waiting indicators in telephony.
+	Fix issue with handling UUID parameter and RegisterEndpoint.
+	Add initial support for Bluetooth Management interface.
+	Add support for Application property to HealthChannel.
+
+ver 4.77:
+	Fix issue with device name and accessing already freed memory.
+	Fix issue with handling CHLD=0 command for handsfree.
+	Fix issue with manager properties and no adapters.
+	Fix issue with properties and broken service records.
+	Fix issue with A2DP playback and sample rate changes.
+	Update MCAP and HDP support.
+
+ver 4.76:
+	Fix issue in telephony driver with hanging up held call.
+	Fix issue in telephony driver with notifications when on hold.
+	Fix issue with blocking on setconf confirmation callback.
+	Fix issue with not always signaling new streams as sinks.
+	Fix issue with errors in case of endpoint request timeout.
+	Fix issue with HFP/HSP microphone and speaker gain values.
+	Add source if the device attempt to configure local sink stream.
+	Add PSM option for GATT/ATT over BR/EDR on gatttool.
+	Add support for GATT/ATT Attribute Write Request.
+	Update MCAP and HDP support.
+
+ver 4.75:
+	Fix use of uninitialized variable on legacy pairing.
+	Fix mismatch of attribute protocol opcode.
+
+ver 4.74:
+	Fix regression for Legacy Pairing.
+	Fix wrong PSM value for attribute protocol.
+	Fix issue with RSSI field in advertising reports.
+	Add support for Add BR/EDR and LE interleaved discovery.
+	Add support for GATT write characteristic value option.
+	Add support for specifying download address for AR300x.
+
+ver 4.73:
+	Fix problem with EIR data when setting the name.
+	Fix reading local name from command complete event.
+	Fix registering local endpoints with disabled socket interface.
+	Add support for more HCI operations using ops infrastructure.
+	Add support for GATT characteristic hierarchy.
+	Add support for GATT indications.
+
+ver 4.72:
+	Fix memory leak while connecting BTIO channels.
+	Fix crash with GStreamer plugin if SBC is not supported.
+	Fix issue with GATT server stop sending notifications.
+	Fix issue with GATT and dealing with the minimum MTU size.
+	Fix issue with file descriptor leak in GATT client.
+	Add support for UUID 128-bit handling in attribute client.
+	Add support for encoders/decoders for MTU Exchange.
+	Add support for the MTU Exchange procedure to the server.
+	Add support for a per channel MTU to the ATT server.
+	Add support for Characteristic interface.
+	Add support for new Media API and framework.
+	Add initial support for HDP plugin.
+
+ver 4.71:
+	Fix compilation when SBC support in not enabled.
+	Fix crash with RequestSession and application disconnects.
+	Fix memory leak and possible crash when removing audio device.
+	Fix issue with closing stream of locked sep when reconfiguring.
+	Fix issue where discovery could interfere with bonding.
+	Fix issue with Connected status when PS3 BD remote connects.
+	Fix issue with lifetime of fake input devices.
+	Add support for compile time option of oui.txt path.
+	Add support for printing IEEE1284 device ID for CUPS.
+	Add plugin for setting adapter class via DMI.
+	Add more features for attribute protocol and profile.
+	Add initial support for MCAP.
+
+ver 4.70:
+	Fix incoming call indication handling when in WAITING state.
+	Fix various SDP related qualification test case issues.
+	Fix logic to write EIR when SDP records are changed.
+	Fix UTF-8 validity check for remote names in EIR.
+	Add support for UUID-128 extended inquiry response.
+	Add service UUIDs from EIR to the DeviceFound signal.
+	Add fast connectable feature for Handsfree profile.
+	Add HCI command and event definitions for AMP support.
+	Add firmware download support for Qualcommh devices.
+	Add host level support for Atheros AR300x device.
+	Add initial support of ATT and GATT for basic rate.
+
+ver 4.69:
+	Fix issue with calling g_option_context_free() twice.
+	Fix inconsistencies with initial LE commands and events.
+	Add support for telephony ClearLastNumber method.
+	Add support for network server interface.
+
+ver 4.68:
+	Fix initialization of adapters in RAW mode.
+	Fix signal strength for HFP in Maemo's telephony support.
+	Add support for following the radio state via Maemo's MCE.
+	Add initial set of LE commands and events definitions.
+	Add mode option for L2CAP sockets to the BtIO API.
+
+ver 4.67:
+	Fix issue with authentication reply when bonding already completed.
+	Fix issue with not canceling authentication when bonding fails.
+	Fix issue with changed combination keys and temporary storage.
+	Fix issue with sdp_get_supp_feat library function.
+	Fix issue with missing unblock on device removal.
+	Fix issue with not waiting for mode change completion.
+	Add ARMv6 optimized version of analysis filter for SBC encoder.
+
+ver 4.66:
+	Fix regression with full debug enabling via SIGUSR2.
+	Fix redundant speaker/microphone gains being sent.
+	Fix not emitting PropertyChanged for SpeakerGain/MicrophoneGain.
+	Fix issue with storage usage when a record is not found in memory.
+	Fix issue with DiscoverServices not retrieving any records.
+	Fix audio profile disconnection order to match whitepaper.
+	Fix auto-accept confirmation when local agent has NoInputNoOutput.
+	Fix remote just-works SSP when MITM protection is required.
+	Fix performing dedicated bonding without MITM requirement.
+	Add support for storing debug link keys in runtime memory.
+
+ver 4.65:
+	Fix issues with general bonding being default setting now.
+	Fix driver removal upon device removal.
+	Add new "Blocked" property to device objects.
+	Add hciconfig support for blacklisting.
+	Add support for dynamic debug feature.
+
+ver 4.64:
+	Fix invalid memory access in headset_get_nrec function.
+	Fix issue with disconnect event on higher protocol layers.
+	Fix issue with list parsing in sdp_set_supp_features function.
+	Fix device object reference counting for SDP browse requests.
+	Add missing memory checks whenever memory is allocated for SDP.
+	Add support for exporting local services via D-Bus.
+	Add more L2CAP Enhanced Retransmission test options.
+
+ver 4.63:
+	Fix avdtp_abort not canceling pending requests.
+	Fix stale connection when abort gets rejected.
+
+ver 4.62:
+	Fix accidental symbol breakage with inquiry transmit power.
+	Fix using invalid data from previous headset connection.
+	Fix double free on AVDTP Abort response.
+	Fix possible crash while verifying AVDTP version.
+	Fix missing inuse flag when AVDTP stream is configured.
+	Add support for Bluetooth controller types.
+
+ver 4.61:
+	Fix issues with Read Inquiry Response Transmit Power Level.
+	Fix possible invalid read when removing a temporary device.
+	Fix mode restoration when remember_powered is false.
+	Fix conference call releasing in telephony-maemo.
+	Fix segmentation fault with authorization during headset disconnects.
+	Add support for handling unanswered AVDTP request on disconnect.
+	Add support for handling Inquiry Response Transmit Power Level.
+	Add support for caching of remote host features.
+	Add preliminary voice dialing support for HSP.
+
+ver 4.60:
+	Fix voice mailbox number reading from SIM.
+	Fix some races with D-Bus mainloop integration.
+	Add helpers for D-Bus signal watches.
+
+ver 4.59:
+	Add values for Bluetooth 4.0 specification.
+	Add SDP functions for HDP support.
+	Add test scripts for input and audio.
+	Fix missing close on BtIO create_io function.
+	Fix sending incorrect AVDTP commands after timeout occurs.
+	Fix timer removal when device disconnects unexpectedly.
+	Fix Extended Inquiry Response record for Device ID.
+
+ver 4.58:
+	Fix crash when adapter agent exists during authentication.
+	Fix CK-20W quirks for play and pause events.
+
+ver 4.57:
+	Fix unloading of drivers for uninitialized adapters.
+	Fix debug message to use requested and not opened SEID.
+	Fix codec selection for GStreamer plugin.
+	Fix deleting of SDP records during service updates.
+	Fix deleting of SDP records when a device is removed.
+	Fix handling when the SDP record is modified on remote device.
+	Fix potential buffer overflow by using snprintf instead of sprintf.
+	Fix const declarations for some storage function parameters.
+
+ver 4.56:
+	Add missing values from Bluetooth 3.0 specification.
+	Add proper tracking of device paired status.
+	Fix tracking of devices without permanently stored link key.
+	Fix issue with link key removal after connection failures.
+	Fix legacy pairing information based on remote host features.
+	Fix off-by-one issue with AVDTP capability parsing.
+	Fix AVRCP, AVCTP, AVDTP, A2DP and HFP version numbers.
+	Fix agent canceling before calling agent_destroy.
+	Fix service record parsing with an empty UUID list.
+	Fix various SDP related memory leaks.
+
+ver 4.55:
+	Add support for POSIX capabilities dropping.
+	Add special quirk for the Nokia CK-20W car kit.
+	Fix error code handling for AVDTP SetConfiguration response.
+	Fix updating out of range list when RSSI hasn't changed.
+	Fix various memory leaks and unnecessary error checks.
+
+ver 4.54:
+	Add introspection interface to output of introspection calls.
+	Fix stream handling when media transport disconnects prematurely.
+	Fix command timeout handling when there's no stream.
+	Fix headset_suspend_stream behavior for invalid states
+	Fix issue with AVDTP ABORTING state transition.
+	Fix issue with AVDTP suspend while closing.
+
+ver 4.53:
+	Fix issue with telephony connection state notifications.
+	Fix AVDTP stream leak for invalid media transport config.
+	Fix audio connection authorization handling with timeouts.
+	Fix race condition in authorizing audio connections.
+	Fix device authorized setting for AVRCP-only connections.
+	Fix duplicate attempts from device to connect signal channel.
+
+ver 4.52:
+	Add AVCTP support to test utility.
+	Fix AVDTP Abort when transport closes before response.
+	Fix authorization when the audio profiles are slow to connect.
+	Fix potential AVDTP reference leaks.
+
+ver 4.51:
+	Add utility for basic AVDTP testing.
+	Add support for configuring L2CAP FCS option.
+	Fix discovery mode for CUPS 1.4.x and later.
+	Fix global state tracking of audio service.
+	Fix last issues with the new build system.
+
+ver 4.50:
+	Fix issue with missing manual pages in distribution.
+	Fix issue with the configuration and state directories.
+	Fix issue with creating include directory.
+	Fix dependencies of include file generation.
+
+ver 4.49:
+	Add simple test program for basic GAP testing.
+	Add support for confirmation requests to agent example.
+	Add support for full non-recursive build.
+	Add five millisecond delay for Simple Pairing auto-accept.
+	Fix Class of Device setting when InitiallyPowered=false.
+
+ver 4.48:
+	Add library function for comparing UUID values.
+	Add support for creating all plugins as builtins.
+	Add support for async handling of service class changes.
+	Add support for source interface to audio IPC.
+	Fix device name settings when device is off or down.
+	Fix issue with enabled SCO server when not necessary.
+	Fix missing D-Bus access policy for CUPS backend.
+	Fix discovery results of CUPS backend.
+	Fix initialization handling of Maemo telephony.
+
+ver 4.47:
+	Add support for RFKILL unblock handling.
+	Add support for serial proxy configurations.
+	Add support for caching service class updates.
+	Fix issues with updating SDP service records.
+	Fix usage of limited discoverable mode.
+	Remove deprecated methods and signals for AudioSource.
+
+ver 4.46:
+	Add support for A2DP sink role.
+	Fix clearing svc_cache before the adapter is up.
+	Fix various pointer after free usages.
+	Fix various memory leaks.
+
+ver 4.45:
+	Fix UDEV_DATADIR fallback if pkg-config fails.
+	Fix adapter cleanup and setup prototypes.
+	Fix double-free with out-of-range devices.
+	Fix inband ring setting to be per-headset.
+	Fix handling of Maemo CSD startup.
+
+ver 4.44:
+	Add some missing manual pages.
+	Fix missing number prefix when installing udev rules.
+	Fix program prefix used in Bluetooth udev rules.
+	Fix three-way calling indicator order.
+	Fix downgrade/upgrade of callheld indicator.
+	Fix +CIEV sending when indicator value changes.
+	Fix signal handling for Maemo telephony driver.
+	Fix parsing issues with messages from Maemo CSD.
+	Fix issue with duplicate active calls.
+
+ver 4.43:
+	Add support for udev based on-demand startup.
+	Fix verbose error reporting of CUPS backend.
+	Fix various string length issues.
+	Fix issues with Maemo telephony driver.
+	Fix another device setup and temporary flag issue.
+	Fix and update example agent implementation.
+
+ver 4.42:
+	Add TI WL1271 to Texas Instruments chip list.
+	Add special udev mode to bluetoothd.
+	Fix regression when there is no agent registered.
+	Fix error return when bonding socket hang up.
+	Fix SCO server socket for HFP handsfree role.
+	Fix shutdown on SCO socket before closing.
+	Fix shutdown on A2DP audio stream channel before closing.
+	Fix issue with asserting on AVDTP reference count bugs.
+	Fix authorization denied issue with certain headsets.
+	Fix AVRCP UNITINFO and SUBUNIT INFO responses.
+	Fix discovery cancel issues in case SDP discovery fails.
+
+ver 4.41:
+	Fix pairing even if the ACL gets dropped before successful SDP.
+	Fix regression which caused device to be removed after pairing.
+	Fix HSP record fetching when remote device doesn't support it.
+	Fix SDP discovery canceling when clearing hs->pending.
+	Fix headset never connecting on the first attempt.
+	Fix headset state tracking if bt_search_service() fails.
+	Fix maximum headset connection count check.
+	Fix AVDTP Discover timeout handling.
+	Fix also UI_SET_KEYBIT for the new pause and play key codes.
+
+ver 4.40:
+	Add telephony driver for oFono telephony stack.
+	Add support for Dell specific HID proxy switching.
+	Add support for running hid2hci from udev.
+	Add mapping for AVRCP Play and Pause to dedicated key codes.
+	Fix AVRCP keycodes to better match existing X keymap support.
+	Fix various quoting issues within telephony support.
+	Fix memory allocation issue when generating PDUs for SDP.
+	Fix race condition on device removal.
+	Fix non-cancelable issue with CreateDevice method.
+	Fix non-working CancelDiscovery method call.
+
+ver 4.39:
+	Add workaround for dealing with unknown inquiry complete.
+	Fix discovering when using software scheduler.
+	Fix wrong NoInputNoOutput IO capability string.
+	Fix race condition with agent during pairing.
+	Fix agent cancellation for security mode 3 acceptor failure.
+	Fix temporary flag removal when device creation fails.
+	Fix hciattach to use ppoll instead of poll.
+	Fix service class update when adapter is down.
+	Fix service classes race condition during startup.
+	Fix release of audio client before freeing the device.
+
+ver 4.38:
+	Add support for builtin plugins.
+	Add framework for adapter operations.
+	Add constants for Enhanced Retransmission modes.
+	Fix HCI socket leak in device_remove_bonding.
+	Fix various format string issues.
+	Fix crashes with various free functions.
+	Fix issues with Headset and A2DP drivers to load again.
+	Fix sending AVRCP button released passthrough messages
+	Fix bug which prevent input devices to work after restart.
+	Fix issue with interpretation of UUID-128 as channel.
+
+ver 4.37:
+	Add version value for Bluetooth 3.0 devices.
+	Add additional L2CAP extended feature mask bits.
+	Add support for loading plugins in priority order.
+	Add support for more detailed usage of disconnect watches.
+	Add support for AVRCP volume control.
+	Add saturated clipping of SBC decoder output to 16-bit.
+	Fix potentially infinite recursion of adapter_up.
+	Fix SCO handling in the case of an incoming call.
+	Fix input service to use confirm callback.
+	Fix cleanup of temporary device entries from storage.
+
+ver 4.36:
+	Add proper tracking of AVCTP connect attempts.
+	Add support to channel pattern in Serial interface.
+	Fix A2DP sink crash if removing device while connecting.
+	Fix error handling if HFP indicators aren't initialized.
+	Fix segfault while handling an incoming SCO connection.
+	Fix Serial.Disconnect to abort connection attempt.
+
+ver 4.35:
+	Add support for Handsfree profile headset role.
+	Add additional checks for open SEIDs from clients.
+	Fix device removal while audio IPC client is connected.
+	Fix device removal when an authorization request is pending.
+	Fix incoming AVDTP connect while authorization in progress.
+	Fix disconnection timers for audio support.
+	Fix various potential NULL pointer deferences.
+	Fix callheld indicator value for multiple calls.
+	Fix voice number type usage.
+	Fix GDBus watch handling.
+
+ver 4.34:
+	Add support for version checks of plugins.
+	Add support for class property on adapter interface.
+	Add support for second SDP attempt after connection reset.
+	Add support for more detailed audio states.
+	Add support for HFP+A2DP auto connection feature.
+	Add support for new and improved audio IPC.
+	Add program for testing audio IPC interface.
+	Fix various AVDTP qualification related issues.
+	Fix broken SDP AttributeIdList parsing.
+	Fix invalid memory access of SDP URL handling.
+	Fix local class of device race conditions.
+	Fix issue with periodic inquiry on startup.
+	Fix missing temporary devices in some situations.
+	Fix SBC alignment issue for encoding with four subbands.
+
+ver 4.33:
+	Add Paired property to the DeviceFound signals.
+	Add support for Headset profile 1.2 version.
+	Fix broken network configuration when IPv6 is disabled.
+	Fix network regression that caused disconnection.
+	Fix SDP truncation of strings with NULL values.
+	Fix service discovery handling of CUPS helper.
+
+ver 4.32:
+	Fix broken SDP record handling.
+	Fix SDP data buffer parsing.
+	Fix more SDP memory leaks.
+	Fix read scan enable calls.
+	Fix A2DP stream handling.
+
+ver 4.31:
+	Add support for new BtIO helper library.
+	Fix AVDTP session close issue.
+	Fix SDP memory leaks.
+	Fix various uninitialized memory issues.
+	Fix duplicate signal emissions.
+	Fix property changes request handling.
+	Fix class of device storage handling.
+
+ver 4.30:
+	Add CID field to L2CAP socket address structure.
+	Fix reset of authentication requirements after bonding.
+	Fix storing of link keys when using dedicated bonding.
+	Fix storing of pre-Bluetooth 2.1 link keys.
+	Fix resetting trust settings on every reboot.
+	Fix handling of local name changes.
+	Fix memory leaks in hciconfig and hcitool
+
+ver 4.29:
+	Use AVRCP version 1.0 for now.
+	Decrease AVDTP idle timeout to one second.
+	Delay AVRCP connection when remote device connects A2DP.
+	Add workaround for AVDTP stream setup with broken headsets.
+	Add missing three-way calling feature bit for Handsfree.
+	Fix handsfree callheld indicator updating.
+	Fix parsing of all AT commands within the buffer.
+	Fix authentication replies when disconnected.
+	Fix handling of debug combination keys.
+	Fix handling of changed combination keys.
+	Fix handling of link keys when using no bonding.
+	Fix handling of invalid/unknown authentication requirements.
+	Fix closing of L2CAP raw socket used for dedicated bonding.
+
+ver 4.28:
+	Add AVDTP signal fragmentation support.
+	Add more SBC performance optimizations.
+	Add more SBC audio quality improvements.
+	Use native byte order for audio plugins.
+	Set the adapter alias only after checking the EIR data.
+	Fix auto-disconnect issue with explicit A2DP connections.
+	Fix invalid memory access of ALSA plugin.
+	Fix compilation with -Wsign-compare.
+
+ver 4.27:
+	Add more SBC optimization (MMX and ARM NEON).
+	Add BT_SECURITY and BT_DEFER_SETUP definitions.
+	Add support for deferred connection setup.
+	Add support for fragmentation of data packets.
+	Add option to trigger dedicated bonding.
+	Follow MITM requirements from remote device.
+	Require MITM for dedicated bonding if capabilities allow it.
+	Fix IO capabilities for non-pairing and pairing cases.
+	Fix no-bonding connections in non-bondable mode.
+	Fix new pairing detection with SSP.
+	Fix bonding with pre-2.1 devices and newer kernels.
+	Fix LIAC setting while toggling Pairable property.
+	Fix device creation for incoming security mode 3 connects.
+	Fix crash within A2DP with bogus pointer.
+	Fix issue with sdp_copy_record() function.
+	Fix crash with extract_des() if sdp_uuid_extract() fails.
+
+ver 4.26:
+	Use of constant shift in SBC quantization code.
+	Add possibility to analyze 4 blocks at once in encoder.
+	Fix correct handling of frame sizes in the encoder.
+	Fix for big endian problems in SBC codec.
+	Fix audio client socket to always be non-blocking.
+	Update telephony support for Maemo.
+
+ver 4.25:
+	Fix receiving data over the audio control socket.
+	Fix subbands selection for joint-stereo in SBC encoder.
+	Add new SBC analysis filter function.
+
+ver 4.24:
+	Fix signal emissions when removing adapters.
+	Fix missing adapter signals on exit.
+	Add support for bringing adapters down on exit.
+	Add support for RememberPowered option.
+	Add support for verbose compiler warnings.
+	Add more options to SBC encoder.
+
+ver 4.23:
+	Update audio IPC for better codec handling.
+	Fix bitstream optimization for SBC encoder.
+	Fix length header values of IPC messages.
+	Fix multiple coding style violations.
+	Fix FindDevice to handle temporary devices.
+	Add configuration option for DeviceID.
+	Add support for InitiallyPowered option.
+	Add missing signals for manager properties.
+	Add telephony support for Maemo.
+
+ver 4.22:
+	Add deny statements to D-Bus access policy.
+	Add support for LegacyPairing property.
+	Add support for global properties.
+	Add more commands to telephony testing script.
+	Add sender checks for serial and network interfaces.
+	Remove deprecated methods and signals from input interface.
+	Remove deprecated methods and signals from network interface.
+	Remove OffMode option and always use device down.
+
+ver 4.21:
+	Fix adapter initialization logic.
+	Fix adapter setup and start security manager early.
+	Fix usage issue with first_init variable.
+
+ver 4.20:
+	Cleanup session handling.
+	Cleanup mode setting handling.
+	Fix issue with concurrent audio clients.
+	Fix issue with HFP/HSP suspending.
+	Fix AT result code syntax handling.
+	Add Handsfree support for AT+NREC.
+	Add PairableTimeout adapter property.
+
+ver 4.19:
+	Fix installation of manual pages for old daemons.
+	Fix D-Bus signal emmissions for CreateDevice.
+	Fix issues with UUID probing.
+	Fix +BSRF syntax issue.
+	Add Pairable adapter property.
+	Add sdp_copy_record() library function.
+
+ver 4.18:
+	Fix release before close issue with RFCOMM TTYs.
+	Fix Connected property on input interface.
+	Fix DeviceFound signals during initial name resolving.
+	Fix service discovery handling.
+	Fix duplicate UUID detection.
+	Fix SBC gain mismatch and decoding handling.
+	Add more options to SBC encoder and decoder.
+	Add special any adapter object for service interface.
+	Add variable prefix to adapter and device object paths.
+
+ver 4.17:
+	Fix SBC encoder not writing last frame.
+	Fix missing timer for A2DP suspend.
+	Add more supported devices to hid2hci utility.
+	Add additional functionality to Handsfree support.
+
+ver 4.16:
+	Fix wrong parameter usage of watch callbacks.
+	Fix parameters for callback upon path removal.
+	Fix unloading of adapter drivers.
+
+ver 4.15:
+	Fix various A2DP state machine issues.
+	Fix some issues with the Handsfree error reporting.
+	Fix format string warnings with recent GCC versions.
+	Remove dependency on GModule.
+
+ver 4.14:
+	Fix types of property arrays.
+	Fix potential crash with input devices.
+	Fix PS3 BD remote input event generation.
+	Allow dynamic adapter driver registration.
+	Update udev rules.
+
+ver 4.13:
+	Fix service discovery and UUID handling.
+	Fix bonding issues with Simple Pairing.
+	Fix file descriptor misuse of SCO connections.
+	Fix various memory leaks in the device handling.
+	Fix AVCTP disconnect handling.
+	Fix GStreamer modes for MP3 encoding.
+	Add operator selection to Handsfree support.
+
+ver 4.12:
+	Fix crash with missing icon value.
+	Fix error checks of HAL plugin.
+	Fix SCO server socket cleanup on exit.
+	Fix memory leaks from DBusPendingCall.
+	Fix handling of pending authorization requests.
+	Fix missing protocol UUIDs in record pattern.
+
+ver 4.11:
+	Change SCO server socket into a generic one.
+	Add test script for dummy telephony plugin.
+	Fix uninitialized reply of multiple GetProperties methods.
+
+ver 4.10:
+	Fix memory leaks with HAL messages.
+	Add more advanced handsfree features.
+	Add properties to audio, input and network interfaces.
+	Stop device discovery timer on device removal.
+
+ver 4.9:
+	Fix signals for Powered and Discoverable properties.
+	Fix handling of Alias and Icon properties.
+	Fix duplicate entries for service UUIDs.
+
+ver 4.8:
+	Fix retrieving of formfactor value.
+	Fix retrieving of local and remote extended features.
+	Fix potential NULL pointer dereference during pairing.
+	Fix crash with browsing due to a remotely initated pairing.
+
+ver 4.7:
+	Fix pairing and service discovery logic.
+	Fix crashes during suspend and resume.
+	Fix race condition within devdown mode.
+	Add RequestSession and ReleaseSession methods.
+	Add Powered and Discoverable properties.
+	Add Devices property and deprecate ListDevices.
+	Add workaround for a broken carkit from Nokia.
+
+ver 4.6:
+	Fix Device ID record handling.
+	Fix service browsing and storage.
+	Fix authentication and encryption for input devices.
+	Fix adapter name initialization.
+
+ver 4.5:
+	Fix initialization issue with new adapters.
+	Send HID authentication request without blocking.
+	Hide the verbose SDP debug behind SDP_DEBUG.
+	Add extra UUIDs for service discovery.
+	Add SCO server socket listener.
+	Add authorization support to service plugin.
+
+ver 4.4:
+	Add temporary fix for the CUPS compile issue.
+	Add service-api.txt to distribution.
+	Mention the variable prefix of an object path
+
+ver 4.3:
+	Add dummy driver for telephony support.
+	Add support for discovery sessions.
+	Add service plugin for external services.
+	Various cleanups.
+
+ver 4.2:
+	Avoid memory copies in A2DP write routine.
+	Fix broken logic with Simple Pairing check and old kernels.
+	Allow non-bondable and outgoing SDP without agent.
+	Only remove the bonding for non-temporary devices.
+	Cleanup various unnecessary includes.
+	Make more unexported functions static.
+	Add basic infrastructure for gtk-doc support.
+
+ver 4.1:
+	Add 30 seconds timeout to BNEP connection setup phase.
+	Avoid memory copies in A2DP write routine for ALSA.
+	Make sure to include compat/sdp.h in the distribution.
+
+ver 4.0:
+	Initial public release.
+
+ver 3.36:
+	Add init routines for TI BRF chips.
+	Add extra attributes to the serial port record.
+	Add example record for headset audio gateway record.
+	Use Handsfree version 0x0105 for the gateway role.
+	Fix SDP record registration with specific record handles.
+	Fix BCSP sent/receive handling.
+	Fix various includes for cross-compilation.
+	Allow link mode settings for outgoing connections.
+	Allow bonding during periodic inquiry.
+
+ver 3.35:
+	Add two additional company identifiers.
+	Add UUID-128 support for service discovery.
+	Fix usage of friendly names for service discovery.
+	Fix authorization when experiemental is disabled.
+	Fix uninitialized variable in passkey request handling.
+	Enable output of timestamps for l2test and rctest.
+
+ver 3.34:
+	Replace various SDP functions with safe versions.
+	Add additional length validation for incoming SDP packets.
+	Use safe function versions for SDP client handling.
+	Fix issue with RemoveDevice during discovery procedure.
+	Fix collect for non-persistent service records.
+
+ver 3.33:
+	Add functions for reading and writing the link policy settings.
+	Add definition for authentication requirements.
+	Add support for handling Simple Pairing.
+	Add Simple Pairing support to Agent interface.
+	Add ReleaseMode method to Adapter interface.
+	Add DiscoverServices method to Device interface.
+	Remove obsolete code and cleanup the repository.
+	Move over to use the libgdbus API.
+	Enable PIE by default if supported.
+
+ver 3.32:
+	Add OCF constants for synchronous flow control enabling.
+	Add support for switching HID proxy devices from Dell.
+	Add more Bluetooth client/server helper functions.
+	Add support for input service idle timeout option.
+	Fix BNEP reconnection handling.
+	Fix return value for snd_pcm_hw_params() calls.
+	Use upper-case addresses for object paths.
+	Remove HAL support helpers.
+	Remove inotify support.
+	Remove service daemon activation handling.
+	Remove uneeded D-Bus API extension.
+
+ver 3.31:
+	Create device object for all pairing cases.
+	Convert authorization to internal function calls.
+	Add initial support for Headset Audio Gateway role.
+	Add generic Bluetooth helper functions for GLib.
+	Fix endiannes handling of connection handles.
+	Don't optimize when debug is enabled.
+
+ver 3.30:
+	Convert audio service into a plugin.
+	Convert input service into a plugin.
+	Convert serial service into a plugin.
+	Convert network service into a plugin.
+	Emit old device signals when a property is changed.
+	Fix missing DiscoverDevices and CancelDiscovery methods.
+	Add another company identifier.
+	Add basic support for Bluetooth sessions.
+	Add avinfo utility for AVDTP/A2DP classification.
+	Remove build option for deprecated sdpd binary.
+
+ver 3.29:
+	Introduce new D-Bus based API.
+	Add more SBC optimizations.
+	Add support for PS3 remote devices.
+	Fix alignment trap in SDP server.
+	Fix memory leak in sdp_get_uuidseq_attr function.
+
+ver 3.28:
+	Add support for MCAP UUIDs.
+	Add support for role switch for audio service.
+	Add disconnect timer for audio service.
+	Add disconnect detection to ALSA plugin.
+	Add more SBC optimizations.
+	Fix alignment issue of SDP server.
+	Remove support for SDP parsing via expat.
+
+ver 3.27:
+	Update uinput.h with extra key definitions.
+	Add support for input connect/disconnect callbacks.
+	Add ifdefs around some baud rate definitions.
+	Add another company identifier.
+	Add proper HFP service level connection handling.
+	Add basic headset automatic disconnect support.
+	Add support for new SBC API.
+	Fix SBC decoder noise at high bitpools.
+	Use 32-bit multipliers for further SBC optimization.
+	Check for RFCOMM connection state in SCO connect callback.
+	Make use of parameters selected in ALSA plugin.
+
+ver 3.26:
+	Fix compilation issues with UCHAR_MAX, USHRT_MAX and UINT_MAX.
+	Improve handling of different audio transports.
+	Enable services by default and keep old daemons disabled.
+
+ver 3.25:
+	Add limited support for Handsfree profile.
+	Add limited support for MPEG12/MP3 codec.
+	Add basic support for UNITINFO and SUBUNITINFO.
+	Add more SBC optimizations.
+	Fix external service (un)registration.
+	Allow GetInfo and GetAddress to fail.
+
+ver 3.24:
+	Add definitions for MDP.
+	Add TCP connection support for serial proxy.
+	Add fix for Logitech HID proxy switching.
+	Add missing macros, MIN, MAX, ABS and CLAMP.
+	Add more SBC encoder optimizations.
+	Add initial mechanism to handle headset commands.
+	Fix connecting to handsfree profile headsets.
+	Use proper function for checking signal name.
+
+ver 3.23:
+	Fix remote name request handling bug.
+	Fix key search function to honor the mmap area size.
+	Fix Avahi integration of network service.
+	Add new plugin communication for audio service.
+	Enable basic AVRCP support by default.
+	More optimizations to the SBC library.
+	Create common error definitions.
+
+ver 3.22:
+	Add missing include file from audio service.
+	Add SBC conformance test utility.
+	Add basic uinput support for AVRCP.
+	Fix L2CAP socket leak in audio service.
+	Fix buffer usage in GStreamer plugin.
+	Fix remote name request event handling.
+
+ver 3.21:
+	Add constant for Bluetooth socket options level.
+	Add initial AVRCP support.
+	Add A2DP sink support to GStreamer plugin.
+	Fix interoperability with A2DP suspend.
+	Fix sign error in 8-subband encoder.
+	Fix handling of service classes length size.
+	Store Extended Inquiry Response data information.
+	Publish device id information through EIR.
+	Support higher baud rates for Ericcson based chips.
+
+ver 3.20:
+	Fix GStreamer plugin file type detection.
+	Fix potential infinite loop in inotify support.
+	Fix D-Bus signatures for dict handling.
+	Fix issues with service activation.
+	Fix SDP failure handling of audio service.
+	Fix various memory leaks in input service.
+	Add secure device creation method to input service.
+	Add service information methods to serial service.
+	Add config file support to network service.
+	Add scripting capability to network service.
+	Add special on-mode handling.
+	Add optimization for SBC encoder.
+	Add tweaks for D-Bus 1.1.x libraries.
+	Add support for inquiry transmit power level.
+
+ver 3.19:
+	Limit range of bitpool announced while in ACP side.
+	Use poll instead of usleep to wait for worker thread.
+	Use default event mask from the specification.
+	Add L2CAP mode constants.
+	Add HID proxy support for Logitech diNovo Edge dongle.
+	Add refresh option to re-request device names.
+	Show correct connection link type.
+
+ver 3.18:
+	Don't allocate memory for the Bluetooth base UUID.
+	Implement proper locking for headsets.
+	Fix various A2DP SEP locking issues.
+	Fix and cleanup audio stream handling.
+	Fix stream starting if suspend request is pending.
+	Fix A2DP and AVDTP endianess problems.
+	Add network timeout and retransmission support.
+	Add more detailed decoding of EIR elements.
+
+ver 3.17:
+	Fix supported commands bit calculation.
+	Fix crashes in audio and network services.
+	Check PAN source and destination roles.
+	Only export the needed symbols for the plugins.
+
+ver 3.16:
+	Update company identifier list.
+	Add support for headsets with SCO audio over HCI.
+	Add support for auto-create through ALSA plugin.
+	Add support for ALSA plugin parameters.
+	Add GStreamer plugin with SBC decoder and encoder.
+	Fix network service NAP, GN and PANU servers.
+	Set EIR information from SDP database.
+
+ver 3.15:
+	Add A2DP support to the audio service.
+	Add proxy support to the serial service.
+	Extract main service class for later use.
+	Set service classes value from SDP database.
+
+ver 3.14:
+	Add missing signals for the adapter interface.
+	Add definitions and functions for Simple Pairing.
+	Add basic commands for Simple Pairing.
+	Add correct Simple Pairing and EIR interaction.
+	Add missing properties for remote information.
+	Add EPoX endian quirk to the input service.
+	Fix HID descriptor import and storage functions.
+	Fix handling of adapters in raw mode.
+	Fix remote device listing methods.
+
+ver 3.13:
+	Fix some issues with the headset support.
+	Fix concurrent pending connection attempts.
+	Fix usage of devname instead of netdev.
+	Add identifier for Nokia SyncML records.
+	Add command for reading the CSR chip revision.
+	Add generic CSR radio test support.
+	Update HCI command table.
+
+ver 3.12:
+	Add missing HCI command text descriptions
+	Add missing HCI commands structures.
+	Add missing HCI event structures.
+	Add common bachk() function.
+	Add support for limited discovery mode.
+	Add support for setting of event mask.
+	Add GetRemoteServiceIdentifiers method.
+	Add skeleton for local D-Bus server.
+	Add headset gain control methods.
+	Fix various headset implementation issues.
+	Fix various serial port service issues.
+	Fix various input service issues.
+	Let CUPS plugin discover printers in range.
+	Improve the BCM2035 UART init routine.
+	Ignore connection events for non-ACL links.
+
+ver 3.11:
+	Update API documentation.
+	Minimize SDP root records and browse groups.
+	Use same decoder for text and URL strings.
+	Fix URL data size handling.
+	Fix SDP pattern extraction for XML.
+	Fix network connection persistent state.
+	Add network connection helper methods.
+	Add initial version of serial port support.
+	Add class of device tracking.
+
+ver 3.10.1:
+	Add option to disable installation of manual pages.
+	Fix input service encryption setup.
+	Fix serial service methods.
+	Fix network service connection handling.
+	Provide a simple init script.
+
+ver 3.10:
+	Add initial version of network service.
+	Add initial version of serial service.
+	Add initial version of input service.
+	Add initial version of audio service.
+	Add authorization framework.
+	Add integer based SBC library.
+	Add version code for Bluetooth 2.1 specification.
+	Add ESCO_LINK connection type constant.
+	Export sdp_uuid32_to_uuid128() function.
+
+ver 3.9:
+	Add RemoteDeviceDisconnectRequested signal.
+	Add updated service framework.
+	Add embedded GLib library.
+	Add support for using system GLib library.
+	Create internal SDP server library.
+
+ver 3.8:
+	Sort discovered devices list based on their RSSI.
+	Send DiscoverableTimeoutChanged signal.
+	Fix local and remote name validity checking.
+	Add ListRemoteDevices and ListRecentRemoteDevices methods.
+	Add basic integration of confirmation concept.
+	Add support for service record description via XML.
+	Add support for external commands to the RFCOMM utility.
+	Add experimental service and authorization API.
+	Add functions for registering binary records.
+
+ver 3.7:
+	Fix class of device handling.
+	Fix error replies with pairing and security mode 3.
+	Fix disconnect method for RFCOMM connections.
+	Add match pattern for service searches.
+	Add support for prioritized watches.
+	Add additional PDU length checks.
+	Fix CSRC value for partial responses.
+
+ver 3.6.1:
+	Fix IO channel race conditions.
+	Fix pairing issues on big endian systems.
+	Fix pairing issues with page timeout errors.
+	Fix pairing state for security mode 3 requests.
+	Switch to user as default security manager mode.
+
+ver 3.6:
+	Update D-Bus based RFCOMM interface support.
+	Use L2CAP raw sockets for HCI connection creation.
+	Add periodic discovery support to the D-Bus interface.
+	Add initial support for device names via EIR.
+	Add proper UTF-8 validation of device names.
+	Add support for the J-Three keyboard.
+	Fix issues with the asynchronous API for SDP.
+
+ver 3.5:
+	Fix and cleanup watch functionality.
+	Add support for periodic inquiry mode.
+	Add support for asynchronous SDP requests.
+	Add more request owner tracking.
+	Add asynchronous API for SDP.
+	Document pageto and discovto options.
+
+ver 3.4:
+	Improve error reporting for failed HCI commands.
+	Improve handling of CancelBonding.
+	Fixed bonding reply message when disconnected.
+	Fix UUID128 string lookup handling.
+	Fix malloc() versus bt_malloc() usage.
+
+ver 3.3:
+	Don't change inquiry mode for Bluetooth 1.1 adapters.
+	Add udev rules for Bluetooth serial PCMCIA cards.
+	Add Cancel and Release methods for passkey agents.
+	Add GetRemoteClass method.
+	Convert to using ppoll() and pselect().
+	Initialize allocated memory to zero.
+	Remove bcm203x firmware loader.
+	Remove kernel specific timeouts.
+	Add additional private data field for SDP sessions.
+	Add host controller to host flow control defines.
+	Add host number of completed packets defines.
+	Initialize various memory to zero before usage.
+
+ver 3.2:
+	Only check for the low-level D-Bus library.
+	Update possible device minor classes.
+	Fix timeout for pending reply.
+	Add more Inquiry with RSSI quirks.
+	Sleep only 100 msecs for device detection.
+	Don't send BondingCreated on link key renewal.
+	Allow storing of all UTF-8 remote device names.
+	Create storage filenames with a generic function.
+	Fix handling of SDP strings.
+	Add adapter type for SDIO cards.
+	Add features bit for link supervision timeout.
+
+ver 3.1:
+	Add missing placeholders for feature bits.
+	Fix handling of raw mode devices.
+	Fix busy loop in UUID extraction routine.
+	Remove inquiry mode setting.
+	Remove auth and encrypt settings.
+
+ver 3.0:
+	Implement the new BlueZ D-Bus API.
+	Fix broken behavior with EVT_CMD_STATUS.
+	Add features bit for pause encryption.
+	Add additional EIR error code.
+	Add more company identifiers.
+	Add another Phonebook Access identifier.
+	Update sniff subrating data structures.
+
+ver 2.25:
+	Use %jx instead of %llx for uint64_t and int64_t.
+	Allow null-terminated text strings.
+	Add UUID for N-Gage games.
+	Add UUID for Apple Macintosh Attributes.
+	Add Apple attributes and iSync records.
+	Add definitions for Apple Agent.
+	Add support for the Handsfree Audio Gateway service.
+	Add support for choosing a specific record handle.
+	Add support for dialup/telephone connections.
+	Add definitions for Apple Agent.
+	Add support for record handle on service registration.
+
+ver 2.24:
+	Fix display of SDP text and data strings.
+	Add support for device scan property.
+	Add support for additional access protocols.
+	Update the D-Bus policy configuration file.
+
+ver 2.23:
+	Update the new D-Bus interface.
+	Make dfutool ready for big endian architectures.
+	Add support for AVRCP specific service records.
+	Add support for writing complex BCCMD commands.
+	Add the new BCCMD interface utility.
+	Add MicroBCSP implementation from CSR.
+	Add constants and definitions for sniff subrating.
+	Add support for allocation of binary text elements.
+	Add HCI emulation tool.
+	Add fake HID support for old EPoX presenters.
+	Reject connections from unknown HID devices.
+	Fix service discovery deadlocks with Samsung D600 phones.
+
+ver 2.22:
+	Remove D-Bus 0.23 support.
+	Add initial version of the new D-Bus interface.
+	Add support for extended inquiry response commands.
+	Add support for the Logitech diNovo Media Desktop Laser.
+	Add compile time buffer checks (FORTIFY SOURCE).
+	Decode reserved LMP feature bits.
+	Fix errno overwrite problems.
+	Fix profile descriptor problem with Samsung phones.
+
+ver 2.21:
+	Move create_dirs() and create_file() into the textfile library.
+	Let textfile_put() also replace the last key value pair.
+	Fix memory leaks with textfile_get() usage.
+	Fix infinite loops and false positive matches.
+	Don't retrieve stored link keys for RAW devices.
+	Document the putkey and delkey commands.
+	Show supported commands also in clear text.
+	Support volatile changes of the BD_ADDR for CSR chips.
+	Add support for identification of supported commands.
+	Add missing OCF declarations for the security filter.
+	Add two new company identifiers.
+
+ver 2.20:
+	Add UUIDs for video distribution profile.
+	Add UUIDs for phonebook access profile.
+	Add attribute identifier for supported repositories.
+	Add definitions for extended inquiry response.
+	Add functions for extended inquiry response.
+	Add support for extended inquiry response.
+	Add support for HotSync service record.
+	Add support for ActiveSync service record.
+	Add ActiveSync networking support.
+	Fix D-Bus crashes with new API versions.
+
+ver 2.19:
+	Fix the GCC 4.0 warnings.
+	Fix the routing for dealing with raw devices.
+	Fix off by one memory allocation error.
+	Fix security problem with escape characters in device name.
+	Add per device service record functions.
+	Send D-Bus signals for inquiry results and remote name resolves.
+	Add support for device specific SDP records.
+
+ver 2.18:
+	Support D-Bus 0.23 and 0.33 API versions.
+	Support reading of complex BCCMD values.
+	Support minimum and maximum encryption key length.
+	Add support for reading and writing the inquiry scan type.
+	Add definitions for connection accept timeout and scan enable.
+	Add support for inquiry scan type.
+	Add tool for the CSR BCCMD interface.
+	Add first draft of the Audio/Video control utility.
+	Add disconnect timer support for the A2DP ALSA plugin.
+	Make SBC parameters configurable.
+	Replace non-printable characters in device names.
+	Remove hci_vhci.h header file.
+	Remove hci_uart.h header file.
+
+ver 2.17:
+	Set the storage directory through ${localstatedir}.
+	Add the textfile library for ASCII based file access.
+	Add support for return link keys event.
+	Add support for voice setting configuration.
+	Add support for page scan timeout configuration.
+	Add support for storing and deleting of stored link keys.
+	Add support for searching for services with UUID-128.
+	Add support for retrieving all possible service records.
+	Add support for a raw mode view of service records.
+	Add support for HID information caching in hidd.
+	Add support for authentication in pand and dund.
+	Add support for changing BD_ADDR of CSR chips.
+	Add pskey utility for changing CSR persistent storage values.
+	Add the firmware upgrade utility.
+	Add connection caching for the A2DP ALSA plugin.
+	Add functions for stored link keys.
+	Add definitions for PIN type and unit key.
+	Add SDP_WAIT_ON_CLOSE flag for sdp_connect().
+	Include stdio.h in bluetooth.h header file.
+	Include sys/socket.h in the header files.
+
+ver 2.16:
+	Store link keys in ASCII based file format.
+	Support device name caching.
+	Support zero length data sizes in l2test.
+	Change default l2ping data size to 44 bytes.
+	Hide the server record and the public browse group root.
+	Read BD_ADDR if not set and if it is a raw device.
+	Add SDP language attributes.
+	Add support for browsing the L2CAP group.
+	Add support for stored pin codes for outgoing connections.
+	Add support for local commands and extended features.
+	Add support for reading CSR panic and fault codes.
+	Add config option for setting the inquiry mode.
+	Add OUI decoding support.
+	Use unlimited inquiry responses as default.
+	Use cached device names for PIN request.
+	Use the clock offset when getting the remote names.
+	Add function for reading local supported commands.
+	Add function for reading local extended features.
+	Add function for reading remote extended features.
+	Add function for getting the remote name with a clock offset.
+	Add function for extracting the OUI from a BD_ADDR.
+	Add inquiry info structure with RSSI and page scan mode.
+	Fix buffer allocation for features to string conversion.
+	Support inquiry with unlimited number of responses.
+
+ver 2.15:
+	Enable the RFCOMM service level security.
+	Add deprecated functions for reading the name.
+	Add command for reading the clock offset.
+	Add command for reading the clock.
+	Add function for reading the clock.
+	Add function for reading the local Bluetooth address.
+	Add function for reading the local supported features.
+	Don't configure raw devices.
+	Don't set inquiry scan or page scan on raw devices.
+	Don't show extended information for raw devices.
+	Support L2CAP signal sizes bigger than 2048 bytes.
+	Cleanup of the socket handling code of the test programs.
+	Use better way for unaligned access.
+	Remove sdp_internal.h and its usage.
+
+ver 2.14:
+	Make use of additional connection information.
+	Use library function for reading the RSSI.
+	Use library function for reading the link quality.
+	Use library function for reading the transmit power level.
+	Use library functions for the link supervision timeout.
+	Add tool for changing the device address.
+	Add function for reading the RSSI.
+	Add function for reading the link quality.
+	Add function for reading the transmit power level.
+	Add functions for the link supervision timeout.
+	Remove deprecated functions.
+	Update AM_PATH_BLUEZ macro.
+
+ver 2.13:
+	Use file permission 0600 for the link key file.
+	Add support for HID attribute descriptions.
+	Add support for Device ID attributes.
+	Add Device ID and HID attribute definitions.
+	Update the UUID constants and its translations.
+	Update L2CAP socket option definitions.
+	Update connection information definitions.
+	Various whitespace cleanups.
+
+ver 2.12:
+	Inherit the device specific options from the default.
+	Use --device for selecting the source device.
+	Add --nosdp option for devices with resource limitation.
+	Add support and parameter option for secure mode.
+	Add a lot of build ids and hardware revisions.
+	Add service classes and profile ids for WAP.
+	Add simple AM_PATH_BLUEZ macro.
+	Update UUID translation tables.
+	Correct kernel interface for CMTP and HIDP support.
+
+ver 2.11:
+	Initial support for the kernel security manager.
+	Various cleanups to avoid inclusion of kernel headers.
+	Fix output when the CUPS backend is called without arguments.
+	Fix problems with a 64 bit userland.
+	Use Bluetooth library functions if available.
+	Use standard numbering scheme of SDP record handles.
+	Use bit zero for vendor packets in the filter type bitmask.
+	Add SIM Access types for service discovery.
+	Add more audio/video profile translations.
+	Add another company identifier.
+	Add the missing HCI error codes.
+	Add RFCOMM socket options.
+	Add definition for the SECURE link mode.
+	Add functions for reading and writing the inquiry mode.
+	Add functions for AFH related settings and information.
+	Add version identifier for the Bluetooth 2.0 specification.
+	Add a master option to the hidd.
+	Add support for changing the link key of a connection.
+	Add support for requesting encryption on keyboards.
+	Add support for revision information of Digianswer devices.
+	Add support for the Zoom, IBM and TDK PCMCIA cards.
+	Add checks for the OpenOBEX and the ALSA libraries.
+	Add experimental mRouter support.
+
+ver 2.10:
+	Use a define for the configuration directory.
+	Fix string initialization for flags translation.
+	Fix and extend the unaligned access macros.
+	Make compiling with debug information optional.
+	Don't override CFLAGS from configure.
+	Check for usb_get_busses() and usb_interrupt_read().
+	Add optional support for compiling with PIE.
+	Make installation of the init scripts optional.
+	Make compiling with debug information optional.
+	Don't override CFLAGS from configure.
+
+ver 2.9:
+	Retry SDP connect if busy in the CUPS backend.
+	Use packet type and allow role switch in hcitool.
+	Use the functions from the USB library for hid2hci.
+	Add Broadcom firmware loader.
+	Add EPoX endian quirk for buggy keyboards.
+	Add L2CAP info type and info result definitions.
+	Add value for L2CAP_CONF_RFC_MODE.
+	Change RSSI value to signed instead of unsigned.
+	Allow UUID32 values as protocol identifiers.
+	Update the autoconf/automake scripts.
+
+ver 2.8:
+	Use LIBS and LDADD instead of LDFLAGS.
+	Use HIDP subclass field for HID boot protocol.
+	Set olen before calling getsockopt() in pand.
+	Restore signals for dev-up script.
+	Add PID file support for pand.
+	Add size parameter to expand_name() in hcid.
+	Add support for audio source and audio sink SDP records.
+	Add support for HID virtual cable unplug.
+	Add support for AmbiCom BT2000C card.
+	Add defines and UUID's for audio/video profiles.
+	Add AVDTP protocol identifier.
+	Add HIDP subclass field.
+	Add PKGConfig support.
+	Fix the event code of inquiry with RSSI.
+	Remove dummy SDP library.
+
+ver 2.7:
+	Fix display of decoded LMP features.
+	Update company identifiers.
+	Add AFH related types.
+	Add first bits from EDR prototyping specification.
+	Add support for inquiry with RSSI.
+	Add HCRP related SDP functions.
+	Add HIDP header file.
+	Add support for getting the AFH channel map.
+	Add support for AFH mode.
+	Add support for inquiry mode.
+	Add Bluetooth backend for CUPS.
+	Add the hid2hci utility.
+	Add the hidd utility.
+	Add the pand utility.
+	Add the dund utility.
+	More endian bug fixes.
+	Give udev some time to create the RFCOMM device nodes.
+	Release the TTY if no device node is found.
+	New startup script for the Bluetooth subsystem.
+	Update to the autoconf stuff.
+
+ver 2.6:
+	Change default prefix to /usr.
+	Add manpages for hcid and hcid.conf.
+	Add the sdpd server daemon.
+	Add the sdptool utility.
+	Add the ciptool utility.
+	Add new company identifiers.
+	Add BNEP and CMTP header files.
+	Add the SDP library.
+	Use R2 for default value of pscan_rep_mode.
+
+ver 2.5:
+	Add decoding of Bluetooth 1.2 features.
+	Add link manager version parameter for Bluetooth 1.2.
+	Add new company identifiers.
+	Add D-Bus support for PIN request.
+	Support for transmit power level.
+	Support for park, sniff and hold mode.
+	Support for role switch.
+	Support for reading the clock offset.
+	Support for requesting authentication.
+	Support for setting connection encryption.
+	Show revision information for Broadcom devices.
+	Replace unprintable characters in device name.
+	Use R1 for default value of pscan_rep_mode.
+	Fix some 64-bit problems.
+	Fix some endian problems.
+	Report an error on PIN helper failure.
+	Update bluepin script for GTK2.
+
+ver 2.4:
+	Increase number of inquiry responses.
+	Support for transmit power level.
+	Display all 8 bytes of the features.
+	Add support for reading and writing of IAC.
+	Correct decoding class of device.
+	Use Ericsson revision command for ST Microelectronics devices.
+	Display AVM firmware version with 'revision' command.
+	New code for CSR specific revision information.
+	Support for ST Microelectronics specific initialization.
+	Support for 3Com card version 3.0.
+	Support for TDK, IBM and Socket cards.
+	Support for initial baud rate.
+	Update man pages.
+	Fixes for some memory leaks.
+
+ver 2.3:
+	Added const qualifiers to appropriate function arguments.
+	Minor fixes.
+	CSR firmware version is now displayed by 'revision' command.
+	Voice command is working properly on big endian machines.
+	Added support for Texas Bluetooth modules.
+	Added support for high UART baud rates on Ericsson modules.
+	BCSP initialization fixes.
+	Support for role switch command (hcitool).
+	RFCOMM config file parser fixes.
+	Update man pages.
+	Removed GLib dependency.
+
+ver 2.2:
+	Updated RFCOMM header file.
+	Additional HCI command and event defines.
+	Support for voice settings (hciconfig).
+	Minor hcitool fixes.
+	Improved configure script.
+	Added Headset testing tool.
+	Updated man pages.
+	RPM package.
+
+ver 2.1.1:
+	Resurrect hci_remote_name.
+
+ver 2.1:
+	Added hci_{read, write}_class_of_dev().
+	Added hci_{read, write}_current_iac_lap().
+	Added hci_write_local_name().
+	Added RFCOMM header file.
+	Minor fixes.
+	Improved BCSP initialization (hciattach).
+	Support for displaying link quality (hcitool).
+	Support for changing link supervision timeout (hcitool).
+	New RFCOMM TTY configuration tool (rfcomm).
+	Minor fixes and updates.
+
+ver 2.0:
+	Additional company IDs.
+	BCSP initialization (hciattach).
+	Minor hciconfig fixes.
+
+ver 2.0-pr13:
+	Support for multiple pairing modes.
+	Link key database handling fixes.
+
+ver 2.0-pre12:
+	Removed max link key limit. Keys never expire.
+	Link key database is always updated. Reread PIN on SIGHUP (hcid).
+	Bluetooth script starts SDPd, if installed.
+	Other minor fixes.
+
+ver 2.0-pre11:
+	Improved link key management and more verbose logging (hcid).
+	Fixed scan command (hcitool).
+
+ver 2.0-pre10:
+	Fix hci_inquiry function to return errors and accept user buffers.
+	New functions hci_devba, hci_devid, hci_for_each_dev and hci_get_route.
+	Additional company IDs.
+	Makefile and other minor fixes.
+	Support for reading RSSI, remote name and changing
+	connection type (hcitool). 
+	Device initialization fixes (hcid).
+	Other minor fixes and improvements.
+	Build environment cleanup and fixes.
+
+ver 2.0-pre9:
+	Improved bluepin. Working X authentication.
+	Improved hcitool. New flexible cmd syntax, additional commands.
+	Human readable display of the device features.
+	LMP features to string translation support.
+	Additional HCI command and event defines.
+	Extended hci_filter API.
+
+ver 2.0-pre8:
+	Additional HCI ioctls and defines.
+	All strings and buffers are allocated dynamically.
+	ba2str, str2ba automatically swap bdaddress.
+	Additional hciconfig commands. Support for ACL and SCO MTU ioctls.
+	Support for Inventel and COM1 UART based devices.
+	Minor hcitool fixes.
+	Improved l2test. New L2CAP test modes.
+	Minor fixes and cleanup.
+
+ver 2.0-pre7:
+	Bluetooth libraries and header files is now a separate package.
+	New build environment uses automake and libtool.
+	Massive header files cleanup.
+	Bluetooth utilities is now a separate package.
+	New build environment uses automake.
+	Moved all config files and security data to /etc/bluetooth.
+	Various cleanups.
+
+ver 2.0-pre6:
+	API cleanup and additions.
+	Improved hcitool.
+	l2test minor output fixes.
+	hciattach opt to display list of supported devices.
+
+ver 2.0-pre4:
+	HCI filter enhancements.
+
+ver 2.0-pre3:
+	Cleanup.
+
+ver 2.0-pre2:
+	Additional HCI library functions.
+	Improved CSR baud rate initialization.
+	PCMCIA scripts fixes and enhancements.
+	Documentation update.
+
+ver 2.0-pre1:
+	New UART initialization utility.
+	Hot plugging support for UART based PCMCIA devices.
+	SCO testing utility.
+	New authentication utility (bluepin).
+	Minor fixes and improvements.
diff --git a/repo/HACKING b/repo/HACKING
new file mode 100644
index 0000000..a8fb403
--- /dev/null
+++ b/repo/HACKING
@@ -0,0 +1,130 @@
+Hacking on BlueZ
+****************
+
+Build tools requirements
+========================
+
+When building and testing directly from the repository it is important to
+have at least automake version 1.10 or later installed. All modern
+distributions should default to the latest version, but it seems that
+Debian's default is still an earlier version:
+
+  Check version
+    # dpkg -l '*automake*'
+
+  Install new version
+    # apt-get install automake1.10
+    # update-alternatives --config automake
+
+
+Working with the source code repository
+=======================================
+
+The repository contains two extra scripts that accomplish the bootstrap
+process. One is called "bootstrap" which is the basic scripts that uses the
+autotools scripts to create the needed files for building and installing.
+It makes sure to call the right programs depending on the usage of shared or
+static libraries or translations etc.
+
+The second program is called "bootstrap-configure". This program will make
+sure to properly clean the repository, call the "bootstrap" script and then
+call configure with proper settings for development. It will use the best
+options and pass them over to configure. These options normally include
+the enabling the maintainer mode and the debugging features.
+
+So while in a normal source project the call "./configure ..." is used to
+configure the project with its settings like prefix and extra options. In
+case of bare repositories call "./bootstrap-configure" and it will bootstrap
+the repository and calls configure with all the correct options to make
+development easier.
+
+In case of preparing for a release with "make distcheck", don't use
+bootstrap-configure since it could export development specific settings.
+
+So the normal steps to checkout, build and install such a repository is
+like this:
+
+  Checkout repository
+    # git clone git://git.kernel.org/pub/scm/bluetooth/bluez.git
+    # cd bluez
+
+  Configure and build
+    # ./bootstrap-configure
+    # make
+
+  Configure and build with cgcc (Sparse)
+    # ./bootstrap-configure CC=cgcc
+    # make
+
+  Run unit tests
+    # make check
+
+  Check installation
+    # make install DESTDIR=$PWD/x
+    # find x
+    # rm -rf x
+
+  Check distribution
+    # make distcheck
+
+  Final installation
+    # sudo make install
+
+  Remove autogenerated files
+    # make maintainer-clean
+
+
+Running from within the source code repository
+==============================================
+
+When using "./configure --enable-maintainer-mode" the automake scripts will
+use the plugins directly from within the repository. This removes the need
+to use "make install" when testing "bluetoothd". The "bootstrap-configure"
+automatically includes this option.
+
+  Copy configuration file which specifies the required security policies
+    # sudo cp ./src/bluetooth.conf /etc/dbus-1/system.d/
+
+  Run daemon in foreground with debugging
+    # sudo ./src/bluetoothd -n -d
+
+  Run daemon with valgrind
+   # sudo valgrind --trace-children=yes --track-origins=yes --track-fds=yes \
+   --show-possibly-lost=no --leak-check=full --suppressions=./tools/valgrind.supp \
+   ./src/bluetoothd -n -d
+
+For production installations or distribution packaging it is important that
+the "--enable-maintainer-mode" option is NOT used.
+
+Note multiple arguments to -d can be specified, colon, comma or space
+separated. The arguments are relative source code filenames for which
+debugging output should be enabled; output shell-style globs are
+accepted (e.g.: 'plugins/*:src/main.c').
+
+Submitting patches
+==================
+
+If you fixed a bug or you want to add support for something, patches are
+welcome! In order to ease the inclusion of your patch, it's important to follow
+some rules, otherwise it will likely be rejected by maintainers.
+
+BlueZ rules for submitting patches follow most of the rules used by Linux kernel
+(https://www.kernel.org/doc/Documentation/SubmittingPatches) with some remarks:
+
+1) Do *not* add "Signed-off-by" lines in your commit messages. BlueZ does not
+use them, so including them is actually an error.
+
+2) Be sure to follow the coding style rules of BlueZ. They are listed in
+doc/coding-style.txt.
+
+3) Split your patch according to the top-level directories. E.g.: if you added
+a feature that touches files under 'include/', 'src/' and 'drivers/'
+directories, split in three separated patches, taking care not to
+break compilation.
+
+4) Bug fixes should be sent first as they take priority over new features.
+
+5) The commit message should follow 50/72 formatting which means the header
+should be limited to 50 characters and the description should be wrapped at 72
+characters except if it contains quoted information from debug tools like
+backtraces, compiler errors, etc.
diff --git a/repo/INSTALL b/repo/INSTALL
new file mode 100644
index 0000000..56b077d
--- /dev/null
+++ b/repo/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about.  Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory.  After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).  Here is a another example:
+
+     /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+     Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+
diff --git a/repo/Makefile.am b/repo/Makefile.am
new file mode 100644
index 0000000..3e1d8d0
--- /dev/null
+++ b/repo/Makefile.am
@@ -0,0 +1,505 @@
+
+AM_MAKEFLAGS = --no-print-directory
+
+lib_LTLIBRARIES =
+
+noinst_LIBRARIES =
+
+noinst_LTLIBRARIES =
+
+bin_PROGRAMS =
+
+noinst_PROGRAMS =
+
+dist_man_MANS =
+
+dist_noinst_MANS =
+
+CLEANFILES =
+
+EXTRA_DIST =
+
+libexecdir = @libexecdir@/bluetooth
+
+libexec_PROGRAMS =
+
+includedir = @includedir@/bluetooth
+
+include_HEADERS =
+
+AM_CFLAGS = $(WARNING_CFLAGS) $(MISC_CFLAGS)
+AM_LDFLAGS = $(MISC_LDFLAGS)
+
+if DATAFILES
+dbusdir = @DBUS_CONFDIR@/dbus-1/system.d
+dbus_DATA = src/bluetooth.conf
+
+confdir = $(sysconfdir)/bluetooth
+conf_DATA =
+
+statedir = $(localstatedir)/lib/bluetooth
+state_DATA =
+endif
+
+if SYSTEMD
+systemdsystemunitdir = @SYSTEMD_SYSTEMUNITDIR@
+systemdsystemunit_DATA = src/bluetooth.service
+
+dbussystembusdir = @DBUS_SYSTEMBUSDIR@
+dbussystembus_DATA = src/org.bluez.service
+endif
+
+EXTRA_DIST += src/bluetooth.service.in src/org.bluez.service
+
+plugindir = $(libdir)/bluetooth/plugins
+
+if MAINTAINER_MODE
+build_plugindir = $(abs_top_srcdir)/plugins/.libs
+else
+build_plugindir = $(plugindir)
+endif
+
+
+plugin_LTLIBRARIES =
+
+lib_sources = lib/bluetooth.c lib/hci.c lib/sdp.c
+lib_headers = lib/bluetooth.h lib/hci.h lib/hci_lib.h \
+		lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h \
+		lib/rfcomm.h lib/bnep.h lib/cmtp.h lib/hidp.h
+
+extra_headers = lib/mgmt.h lib/uuid.h lib/a2mp.h lib/amp.h
+extra_sources = lib/uuid.c
+
+local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file)))
+
+BUILT_SOURCES = $(local_headers) src/builtin.h
+
+if LIBRARY
+include_HEADERS += $(lib_headers)
+
+lib_LTLIBRARIES += lib/libbluetooth.la
+
+lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:10:18
+lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
+endif
+
+noinst_LTLIBRARIES += lib/libbluetooth-internal.la
+
+lib_libbluetooth_internal_la_SOURCES = $(lib_headers) $(lib_sources) \
+					$(extra_headers) $(extra_sources)
+
+noinst_LTLIBRARIES += gdbus/libgdbus-internal.la
+
+gdbus_libgdbus_internal_la_SOURCES = gdbus/gdbus.h \
+				gdbus/mainloop.c gdbus/watch.c \
+				gdbus/object.c gdbus/client.c gdbus/polkit.c
+
+noinst_LTLIBRARIES += src/libshared-glib.la src/libshared-mainloop.la
+
+shared_sources = src/shared/io.h src/shared/timeout.h \
+			src/shared/queue.h src/shared/queue.c \
+			src/shared/util.h src/shared/util.c \
+			src/shared/mgmt.h src/shared/mgmt.c \
+			src/shared/crypto.h src/shared/crypto.c \
+			src/shared/ecc.h src/shared/ecc.c \
+			src/shared/ringbuf.h src/shared/ringbuf.c \
+			src/shared/tester.h src/shared/tester.c \
+			src/shared/hci.h src/shared/hci.c \
+			src/shared/hci-crypto.h src/shared/hci-crypto.c \
+			src/shared/hfp.h src/shared/hfp.c \
+			src/shared/uhid.h src/shared/uhid.c \
+			src/shared/pcap.h src/shared/pcap.c \
+			src/shared/btsnoop.h src/shared/btsnoop.c \
+			src/shared/ad.h src/shared/ad.c \
+			src/shared/att-types.h \
+			src/shared/att.h src/shared/att.c \
+			src/shared/gatt-helpers.h src/shared/gatt-helpers.c \
+			src/shared/gatt-client.h src/shared/gatt-client.c \
+			src/shared/gatt-server.h src/shared/gatt-server.c \
+			src/shared/gatt-db.h src/shared/gatt-db.c \
+			src/shared/gap.h src/shared/gap.c
+
+src_libshared_glib_la_SOURCES = $(shared_sources) \
+				src/shared/io-glib.c \
+				src/shared/timeout-glib.c
+
+src_libshared_mainloop_la_SOURCES = $(shared_sources) \
+				src/shared/io-mainloop.c \
+				src/shared/timeout-mainloop.c \
+				src/shared/mainloop.h src/shared/mainloop.c
+
+attrib_sources = attrib/att.h attrib/att-database.h attrib/att.c \
+		attrib/gatt.h attrib/gatt.c \
+		attrib/gattrib.h attrib/gattrib.c \
+		attrib/gatt-service.h attrib/gatt-service.c
+
+btio_sources = btio/btio.h btio/btio.c
+
+gobex_sources = gobex/gobex.h gobex/gobex.c \
+			gobex/gobex-defs.h gobex/gobex-defs.c \
+			gobex/gobex-packet.c gobex/gobex-packet.h \
+			gobex/gobex-header.c gobex/gobex-header.h \
+			gobex/gobex-transfer.c gobex/gobex-debug.h \
+			gobex/gobex-apparam.c gobex/gobex-apparam.h
+
+builtin_modules =
+builtin_sources =
+builtin_nodist =
+
+include Makefile.plugins
+
+if MAINTAINER_MODE
+plugin_LTLIBRARIES += plugins/external-dummy.la
+plugins_external_dummy_la_SOURCES = plugins/external-dummy.c
+plugins_external_dummy_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+				    -no-undefined
+plugins_external_dummy_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden
+endif
+
+libexec_PROGRAMS += src/bluetoothd
+
+src_bluetoothd_SOURCES = $(builtin_sources) \
+			$(attrib_sources) $(btio_sources) \
+			src/bluetooth.ver \
+			src/main.c src/log.h src/log.c \
+			src/backtrace.h src/backtrace.c \
+			src/systemd.h src/systemd.c \
+			src/rfkill.c src/hcid.h src/sdpd.h \
+			src/sdpd-server.c src/sdpd-request.c \
+			src/sdpd-service.c src/sdpd-database.c \
+			src/attrib-server.h src/attrib-server.c \
+			src/gatt-database.h src/gatt-database.c \
+			src/sdp-xml.h src/sdp-xml.c \
+			src/sdp-client.h src/sdp-client.c \
+			src/textfile.h src/textfile.c \
+			src/uuid-helper.h src/uuid-helper.c \
+			src/uinput.h \
+			src/plugin.h src/plugin.c \
+			src/storage.h src/storage.c \
+			src/advertising.h src/advertising.c \
+			src/agent.h src/agent.c \
+			src/error.h src/error.c \
+			src/adapter.h src/adapter.c \
+			src/profile.h src/profile.c \
+			src/service.h src/service.c \
+			src/gatt-client.h src/gatt-client.c \
+			src/device.h src/device.c src/attio.h \
+			src/dbus-common.c src/dbus-common.h \
+			src/eir.h src/eir.c
+src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
+			gdbus/libgdbus-internal.la \
+			src/libshared-glib.la \
+			@BACKTRACE_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ -ldl -lrt
+src_bluetoothd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic \
+				-Wl,--version-script=$(srcdir)/src/bluetooth.ver
+
+src_bluetoothd_DEPENDENCIES = lib/libbluetooth-internal.la \
+				gdbus/libgdbus-internal.la \
+				src/libshared-glib.la \
+				src/bluetooth.service
+
+src_bluetoothd_CFLAGS = $(AM_CFLAGS) -DBLUETOOTH_PLUGIN_BUILTIN \
+					-DPLUGINDIR=\""$(build_plugindir)"\"
+src_bluetoothd_SHORTNAME = bluetoothd
+
+builtin_files = src/builtin.h $(builtin_nodist)
+
+nodist_src_bluetoothd_SOURCES = $(builtin_files)
+
+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 \
+			profiles/input/input.conf profiles/proximity/proximity.conf
+
+test_scripts =
+unit_tests =
+
+include Makefile.tools
+include Makefile.obexd
+include android/Makefile.am
+
+if HID2HCI
+rulesdir = @UDEV_DIR@/rules.d
+
+rules_DATA = tools/97-hid2hci.rules
+
+CLEANFILES += $(rules_DATA)
+endif
+
+EXTRA_DIST += tools/hid2hci.rules
+
+if TEST
+testdir = $(pkglibdir)/test
+test_SCRIPTS = $(test_scripts)
+endif
+
+EXTRA_DIST += $(test_scripts)
+
+EXTRA_DIST += doc/assigned-numbers.txt doc/supported-features.txt \
+				doc/test-coverage.txt \
+				doc/test-runner.txt \
+				doc/settings-storage.txt
+
+EXTRA_DIST += doc/mgmt-api.txt \
+		doc/adapter-api.txt doc/device-api.txt \
+		doc/agent-api.txt doc/profile-api.txt \
+		doc/network-api.txt doc/media-api.txt \
+		doc/health-api.txt doc/sap-api.txt \
+		doc/input-api.txt
+
+EXTRA_DIST += doc/alert-api.txt \
+		doc/proximity-api.txt doc/heartrate-api.txt \
+		doc/thermometer-api.txt doc/cyclingspeed-api.txt \
+		doc/gatt-api.txt doc/advertising-api.txt
+
+EXTRA_DIST += doc/obex-api.txt doc/obex-agent-api.txt
+
+EXTRA_DIST += doc/pics-opp.txt doc/pixit-opp.txt \
+		doc/pts-opp.txt
+
+EXTRA_DIST += tools/magic.btsnoop
+
+AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@ @UDEV_CFLAGS@
+
+AM_CPPFLAGS = -I$(builddir)/lib
+
+
+unit_tests += unit/test-eir
+
+unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/uuid-helper.c
+unit_test_eir_LDADD = src/libshared-glib.la lib/libbluetooth-internal.la \
+								@GLIB_LIBS@
+
+unit_tests += unit/test-uuid
+
+unit_test_uuid_SOURCES = unit/test-uuid.c
+unit_test_uuid_LDADD = src/libshared-glib.la lib/libbluetooth-internal.la \
+								@GLIB_LIBS@
+
+unit_tests += unit/test-textfile
+
+unit_test_textfile_SOURCES = unit/test-textfile.c src/textfile.h src/textfile.c
+unit_test_textfile_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-crc
+
+unit_test_crc_SOURCES = unit/test-crc.c monitor/crc.h monitor/crc.c
+unit_test_crc_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-crypto
+
+unit_test_crypto_SOURCES = unit/test-crypto.c
+unit_test_crypto_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-ecc
+
+unit_test_ecc_SOURCES = unit/test-ecc.c
+unit_test_ecc_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-ringbuf unit/test-queue
+
+unit_test_ringbuf_SOURCES = unit/test-ringbuf.c
+unit_test_ringbuf_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_test_queue_SOURCES = unit/test-queue.c
+unit_test_queue_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-mgmt
+
+unit_test_mgmt_SOURCES = unit/test-mgmt.c
+unit_test_mgmt_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-uhid
+
+unit_test_uhid_SOURCES = unit/test-uhid.c
+unit_test_uhid_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-sdp
+
+unit_test_sdp_SOURCES = unit/test-sdp.c \
+				src/sdpd.h src/sdpd-database.c \
+				src/log.h src/log.c \
+				src/sdpd-service.c src/sdpd-request.c
+unit_test_sdp_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-avdtp
+
+unit_test_avdtp_SOURCES = unit/test-avdtp.c \
+				src/log.h src/log.c \
+				android/avdtp.c android/avdtp.h
+unit_test_avdtp_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-avctp
+
+unit_test_avctp_SOURCES = unit/test-avctp.c \
+				src/log.h src/log.c \
+				android/avctp.c android/avctp.h
+unit_test_avctp_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-avrcp
+
+unit_test_avrcp_SOURCES = unit/test-avrcp.c \
+				src/log.h src/log.c \
+				android/avctp.c android/avctp.h \
+				android/avrcp-lib.c android/avrcp-lib.h
+unit_test_avrcp_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-hfp
+
+unit_test_hfp_SOURCES = unit/test-hfp.c
+unit_test_hfp_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-gdbus-client
+
+unit_test_gdbus_client_SOURCES = unit/test-gdbus-client.c
+unit_test_gdbus_client_LDADD = gdbus/libgdbus-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@ @DBUS_LIBS@
+
+unit_tests += unit/test-gobex-header unit/test-gobex-packet unit/test-gobex \
+			unit/test-gobex-transfer unit/test-gobex-apparam
+
+unit_test_gobex_SOURCES = $(gobex_sources) unit/util.c unit/util.h \
+						unit/test-gobex.c
+unit_test_gobex_LDADD = @GLIB_LIBS@
+
+unit_test_gobex_packet_SOURCES = $(gobex_sources) unit/util.c unit/util.h \
+						unit/test-gobex-packet.c
+unit_test_gobex_packet_LDADD = @GLIB_LIBS@
+
+unit_test_gobex_header_SOURCES = $(gobex_sources) unit/util.c unit/util.h \
+						unit/test-gobex-header.c
+unit_test_gobex_header_LDADD = @GLIB_LIBS@
+
+unit_test_gobex_transfer_SOURCES = $(gobex_sources) unit/util.c unit/util.h \
+						unit/test-gobex-transfer.c
+unit_test_gobex_transfer_LDADD = @GLIB_LIBS@
+
+unit_test_gobex_apparam_SOURCES = $(gobex_sources) unit/util.c unit/util.h \
+						unit/test-gobex-apparam.c
+unit_test_gobex_apparam_LDADD = @GLIB_LIBS@
+
+unit_tests += unit/test-lib
+
+unit_test_lib_SOURCES = unit/test-lib.c
+unit_test_lib_LDADD = src/libshared-glib.la \
+				lib/libbluetooth-internal.la @GLIB_LIBS@
+
+unit_tests += unit/test-gatt
+
+unit_test_gatt_SOURCES = unit/test-gatt.c
+unit_test_gatt_LDADD = src/libshared-glib.la \
+				lib/libbluetooth-internal.la @GLIB_LIBS@
+
+unit_tests += unit/test-hog
+
+unit_test_hog_SOURCES = unit/test-hog.c \
+			$(btio_sources) \
+			profiles/input/hog-lib.h profiles/input/hog-lib.c \
+			profiles/scanparam/scpp.h profiles/scanparam/scpp.c \
+			profiles/battery/bas.h profiles/battery/bas.c \
+			profiles/deviceinfo/dis.h profiles/deviceinfo/dis.c \
+			src/log.h src/log.c \
+			attrib/att.h attrib/att.c \
+			attrib/gatt.h attrib/gatt.c \
+			attrib/gattrib.h attrib/gattrib.c
+unit_test_hog_LDADD = src/libshared-glib.la \
+				lib/libbluetooth-internal.la @GLIB_LIBS@
+
+unit_tests += unit/test-gattrib
+
+unit_test_gattrib_SOURCES = unit/test-gattrib.c attrib/gattrib.c $(btio_sources) src/log.h src/log.c
+unit_test_gattrib_LDADD = lib/libbluetooth-internal.la \
+			src/libshared-glib.la \
+			@GLIB_LIBS@ @DBUS_LIBS@ -ldl -lrt
+
+if MAINTAINER_MODE
+noinst_PROGRAMS += $(unit_tests)
+endif
+
+TESTS = $(unit_tests)
+AM_TESTS_ENVIRONMENT = MALLOC_CHECK_=3 MALLOC_PERTURB_=69
+
+if DBUS_RUN_SESSION
+AM_TESTS_ENVIRONMENT += dbus-run-session --
+endif
+
+if VALGRIND
+LOG_COMPILER = valgrind --error-exitcode=1 --num-callers=30
+LOG_FLAGS = --trace-children=yes --leak-check=full --show-reachable=no \
+		--suppressions=$(srcdir)/tools/valgrind.supp --quiet
+endif
+
+pkgconfigdir = $(libdir)/pkgconfig
+
+if LIBRARY
+pkgconfig_DATA = lib/bluez.pc
+endif
+
+manual_pages = doc/btmon.1
+
+if MANPAGES
+dist_noinst_MANS += $(manual_pages)
+endif
+
+EXTRA_DIST += $(manual_pages:.1=.txt)
+
+DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles --enable-library \
+					--enable-manpages --enable-android \
+					--disable-systemd --disable-udev
+
+DISTCLEANFILES = $(pkgconfig_DATA) $(unit_tests) $(manual_pages)
+
+MAINTAINERCLEANFILES = Makefile.in \
+	aclocal.m4 configure config.h.in config.sub config.guess \
+	ltmain.sh depcomp compile missing install-sh mkinstalldirs test-driver
+
+SED_PROCESS = $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
+		$(SED) -e 's,@libexecdir\@,$(libexecdir),g' \
+		< $< > $@
+
+%.service: %.service.in Makefile
+	$(SED_PROCESS)
+
+%.1: %.txt
+	$(AM_V_GEN)a2x --doctype manpage --format manpage $(srcdir)/$<
+
+src/builtin.h: src/genbuiltin $(builtin_sources)
+	$(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
+
+tools/%.rules:
+	$(AM_V_GEN)cp $(srcdir)/$(subst 97-,,$@) $@
+
+$(lib_libbluetooth_la_OBJECTS): $(local_headers)
+
+lib/bluetooth/%.h: lib/%.h
+	$(AM_V_at)$(MKDIR_P) lib/bluetooth
+	$(AM_V_GEN)$(LN_S) -f "$(abs_top_builddir)"/$< $@
+
+if COVERAGE
+clean-coverage:
+	@lcov --directory $(top_builddir) --zerocounters
+	$(RM) -r coverage $(top_builddir)/coverage.info
+
+coverage: check
+	@lcov --compat-libtool --directory $(top_builddir) --capture \
+				--output-file $(top_builddir)/coverage.info
+	$(AM_V_at)$(MKDIR_P) coverage
+	@genhtml -o coverage/ $(top_builddir)/coverage.info
+
+clean-local: clean-coverage
+	-find $(top_builddir) -name "*.gcno" -delete
+	-find $(top_builddir) -name "*.gcda" -delete
+	$(RM) -r lib/bluetooth
+
+else
+clean-local:
+	-find $(top_builddir) -name "*.gcno" -delete
+	-find $(top_builddir) -name "*.gcda" -delete
+	$(RM) -r lib/bluetooth
+endif
diff --git a/repo/Makefile.obexd b/repo/Makefile.obexd
new file mode 100644
index 0000000..2e33cbc
--- /dev/null
+++ b/repo/Makefile.obexd
@@ -0,0 +1,110 @@
+
+if SYSTEMD
+systemduserunitdir = @SYSTEMD_USERUNITDIR@
+systemduserunit_DATA = obexd/src/obex.service
+
+dbussessionbusdir = @DBUS_SESSIONBUSDIR@
+dbussessionbus_DATA = obexd/src/org.bluez.obex.service
+endif
+
+EXTRA_DIST += obexd/src/obex.service.in obexd/src/org.bluez.obex.service
+
+obex_plugindir = $(libdir)/obex/plugins
+
+obexd_builtin_modules =
+obexd_builtin_sources =
+obexd_builtin_nodist =
+
+obexd_builtin_modules += filesystem
+obexd_builtin_sources += obexd/plugins/filesystem.c obexd/plugins/filesystem.h
+
+obexd_builtin_modules += bluetooth
+obexd_builtin_sources += obexd/plugins/bluetooth.c
+
+if EXPERIMENTAL
+obexd_builtin_modules += pcsuite
+obexd_builtin_sources += obexd/plugins/pcsuite.c
+endif
+
+obexd_builtin_modules += opp
+obexd_builtin_sources += obexd/plugins/opp.c
+
+obexd_builtin_modules += ftp
+obexd_builtin_sources += obexd/plugins/ftp.c obexd/plugins/ftp.h
+
+if OBEX
+obexd_builtin_modules += irmc
+obexd_builtin_sources += obexd/plugins/irmc.c
+
+obexd_builtin_modules += pbap
+obexd_builtin_sources += obexd/plugins/pbap.c \
+				obexd/plugins/vcard.h obexd/plugins/vcard.c \
+				obexd/plugins/phonebook.h \
+				obexd/plugins/phonebook-dummy.c
+endif
+
+obexd_builtin_modules += mas
+obexd_builtin_sources += obexd/plugins/mas.c obexd/src/map_ap.h \
+				obexd/plugins/messages.h \
+				obexd/plugins/messages-dummy.c
+
+obexd_builtin_modules += mns
+obexd_builtin_sources += obexd/client/mns.c obexd/src/map_ap.h \
+				obexd/client/map-event.h
+
+libexec_PROGRAMS += obexd/src/obexd
+
+obexd_src_obexd_SOURCES = $(btio_sources) $(gobex_sources) \
+			$(obexd_builtin_sources) \
+			obexd/src/main.c obexd/src/obexd.h \
+			obexd/src/plugin.h obexd/src/plugin.c \
+			obexd/src/log.h obexd/src/log.c \
+			obexd/src/manager.h obexd/src/manager.c \
+			obexd/src/obex.h obexd/src/obex.c obexd/src/obex-priv.h \
+			obexd/src/mimetype.h obexd/src/mimetype.c \
+			obexd/src/service.h obexd/src/service.c \
+			obexd/src/transport.h obexd/src/transport.c \
+			obexd/src/server.h obexd/src/server.c \
+			obexd/client/manager.h obexd/client/manager.c \
+			obexd/client/session.h obexd/client/session.c \
+			obexd/client/bluetooth.h obexd/client/bluetooth.c \
+			obexd/client/sync.h obexd/client/sync.c \
+			obexd/client/pbap.h obexd/client/pbap.c \
+			obexd/client/ftp.h obexd/client/ftp.c \
+			obexd/client/opp.h obexd/client/opp.c \
+			obexd/client/map.h obexd/client/map.c \
+			obexd/client/map-event.h obexd/client/map-event.c \
+			obexd/client/transfer.h obexd/client/transfer.c \
+			obexd/client/transport.h obexd/client/transport.c \
+			obexd/client/dbus.h obexd/client/dbus.c \
+			obexd/client/driver.h obexd/client/driver.c \
+			obexd/src/map_ap.h
+obexd_src_obexd_LDADD = lib/libbluetooth-internal.la \
+			gdbus/libgdbus-internal.la \
+			@ICAL_LIBS@ @DBUS_LIBS@ @GLIB_LIBS@ -ldl
+
+obexd_src_obexd_LDFLAGS = -Wl,--export-dynamic
+
+obexd_src_obexd_CFLAGS = $(AM_CFLAGS) @GLIB_CFLAGS@ @DBUS_CFLAGS@ \
+				@ICAL_CFLAGS@ -DOBEX_PLUGIN_BUILTIN \
+				-DPLUGINDIR=\""$(obex_plugindir)"\" \
+				-fPIC -D_FILE_OFFSET_BITS=64
+
+obexd_src_obexd_CPPFLAGS = -I$(builddir)/lib -I$(builddir)/obexd/src
+
+obexd_src_obexd_SHORTNAME = obexd
+
+obexd_builtin_files = obexd/src/builtin.h $(obexd_builtin_nodist)
+
+nodist_obexd_src_obexd_SOURCES = $(obexd_builtin_files)
+
+BUILT_SOURCES += obexd/src/builtin.h
+
+obexd/src/plugin.$(OBJEXT): obexd/src/builtin.h
+
+obexd/src/builtin.h: obexd/src/genbuiltin $(obexd_builtin_sources)
+	$(AM_V_GEN)$(srcdir)/obexd/src/genbuiltin $(obexd_builtin_modules) > $@
+
+CLEANFILES += obexd/src/builtin.h $(builtin_files) obexd/src/obex.service
+
+EXTRA_DIST += obexd/src/genbuiltin
diff --git a/repo/Makefile.plugins b/repo/Makefile.plugins
new file mode 100644
index 0000000..f85b642
--- /dev/null
+++ b/repo/Makefile.plugins
@@ -0,0 +1,125 @@
+
+builtin_modules += hostname
+builtin_sources += plugins/hostname.c
+
+builtin_modules += wiimote
+builtin_sources += plugins/wiimote.c
+
+builtin_modules += autopair
+builtin_sources += plugins/autopair.c
+
+builtin_modules += policy
+builtin_sources += plugins/policy.c
+
+if MAINTAINER_MODE
+builtin_modules += gatt_example
+builtin_sources += plugins/gatt-example.c
+endif
+
+if EXPERIMENTAL
+builtin_modules += neard
+builtin_sources += plugins/neard.c
+
+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-dummy.c
+
+noinst_LIBRARIES += profiles/sap/libsap.a
+profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c
+endif
+
+builtin_modules += a2dp
+builtin_sources += profiles/audio/source.h profiles/audio/source.c \
+			profiles/audio/sink.h profiles/audio/sink.c \
+			profiles/audio/a2dp.h profiles/audio/a2dp.c \
+			profiles/audio/avdtp.h profiles/audio/avdtp.c \
+			profiles/audio/media.h profiles/audio/media.c \
+			profiles/audio/transport.h profiles/audio/transport.c \
+			profiles/audio/a2dp-codecs.h
+
+builtin_modules += avrcp
+builtin_sources += profiles/audio/control.h profiles/audio/control.c \
+			profiles/audio/avctp.h profiles/audio/avctp.c \
+			profiles/audio/avrcp.h profiles/audio/avrcp.c \
+			profiles/audio/player.h profiles/audio/player.c
+
+builtin_modules += network
+builtin_sources += profiles/network/manager.c \
+			profiles/network/bnep.h profiles/network/bnep.c \
+			profiles/network/server.h profiles/network/server.c \
+			profiles/network/connection.h \
+			profiles/network/connection.c
+
+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
+
+builtin_modules += hog
+builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \
+			profiles/input/hog-lib.c profiles/input/hog-lib.h \
+			profiles/deviceinfo/dis.c profiles/deviceinfo/dis.h \
+			profiles/battery/bas.c profiles/battery/bas.h \
+			profiles/scanparam/scpp.c profiles/scanparam/scpp.h \
+			profiles/input/suspend.h profiles/input/suspend-none.c
+
+EXTRA_DIST += profiles/input/suspend-dummy.c
+
+if EXPERIMENTAL
+builtin_modules += health
+builtin_sources += profiles/health/mcap.h profiles/health/mcap.c \
+			profiles/health/hdp_main.c profiles/health/hdp_types.h \
+			profiles/health/hdp_manager.h \
+			profiles/health/hdp_manager.c \
+			profiles/health/hdp.h profiles/health/hdp.c \
+			profiles/health/hdp_util.h profiles/health/hdp_util.c
+endif
+
+builtin_modules += gap
+builtin_sources += profiles/gap/gas.c
+
+builtin_modules += scanparam
+builtin_sources += profiles/scanparam/scan.c
+
+builtin_modules += deviceinfo
+builtin_sources += profiles/deviceinfo/deviceinfo.c
+
+if EXPERIMENTAL
+builtin_modules += alert
+builtin_sources += profiles/alert/server.c
+
+builtin_modules += time
+builtin_sources += profiles/time/server.c
+
+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
+
+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
+
+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@
+endif
diff --git a/repo/Makefile.tools b/repo/Makefile.tools
new file mode 100644
index 0000000..e79b53b
--- /dev/null
+++ b/repo/Makefile.tools
@@ -0,0 +1,421 @@
+
+if CLIENT
+bin_PROGRAMS += client/bluetoothctl
+
+client_bluetoothctl_SOURCES = client/main.c \
+					client/display.h client/display.c \
+					client/agent.h client/agent.c \
+					client/gatt.h client/gatt.c \
+					monitor/uuid.h monitor/uuid.c
+client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ \
+				-lreadline
+endif
+
+if MONITOR
+bin_PROGRAMS += monitor/btmon
+
+monitor_btmon_SOURCES = monitor/main.c monitor/bt.h \
+				monitor/display.h monitor/display.c \
+				monitor/hcidump.h monitor/hcidump.c \
+				monitor/ellisys.h monitor/ellisys.c \
+				monitor/control.h monitor/control.c \
+				monitor/packet.h monitor/packet.c \
+				monitor/vendor.h monitor/vendor.c \
+				monitor/lmp.h monitor/lmp.c \
+				monitor/crc.h monitor/crc.c \
+				monitor/ll.h monitor/ll.c \
+				monitor/l2cap.h monitor/l2cap.c \
+				monitor/sdp.h monitor/sdp.c \
+				monitor/avctp.h monitor/avctp.c \
+				monitor/avdtp.h monitor/avdtp.c \
+				monitor/a2dp.h monitor/a2dp.c \
+				monitor/rfcomm.h monitor/rfcomm.c \
+				monitor/bnep.h monitor/bnep.c \
+				monitor/uuid.h monitor/uuid.c \
+				monitor/hwdb.h monitor/hwdb.c \
+				monitor/keys.h monitor/keys.c \
+				monitor/analyze.h monitor/analyze.c \
+				monitor/intel.h monitor/intel.c \
+				monitor/broadcom.h monitor/broadcom.c
+monitor_btmon_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-mainloop.la @UDEV_LIBS@
+endif
+
+if EXPERIMENTAL
+noinst_PROGRAMS += emulator/btvirt emulator/b1ee emulator/hfp \
+					peripheral/btsensor tools/3dsp \
+					tools/mgmt-tester tools/gap-tester \
+					tools/l2cap-tester tools/sco-tester \
+					tools/smp-tester tools/hci-tester \
+					tools/rfcomm-tester tools/bnep-tester \
+					tools/userchan-tester
+
+emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
+				emulator/serial.h emulator/serial.c \
+				emulator/server.h emulator/server.c \
+				emulator/vhci.h emulator/vhci.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c \
+				emulator/phy.h emulator/phy.c \
+				emulator/amp.h emulator/amp.c \
+				emulator/le.h emulator/le.c
+emulator_btvirt_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
+
+emulator_b1ee_SOURCES = emulator/b1ee.c
+emulator_b1ee_LDADD = src/libshared-mainloop.la
+
+emulator_hfp_SOURCES = emulator/hfp.c
+emulator_hfp_LDADD = src/libshared-mainloop.la
+
+peripheral_btsensor_SOURCES = peripheral/main.c \
+				peripheral/efivars.h peripheral/efivars.c \
+				peripheral/attach.h peripheral/attach.c \
+				peripheral/log.h peripheral/log.c \
+				peripheral/gap.h peripheral/gap.c \
+				peripheral/gatt.h peripheral/gatt.c
+peripheral_btsensor_LDADD = src/libshared-mainloop.la \
+				lib/libbluetooth-internal.la
+
+tools_3dsp_SOURCES = tools/3dsp.c monitor/bt.h
+tools_3dsp_LDADD = src/libshared-mainloop.la
+
+tools_mgmt_tester_SOURCES = tools/mgmt-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_mgmt_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+tools_l2cap_tester_SOURCES = tools/l2cap-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_rfcomm_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+tools_bnep_tester_SOURCES = tools/bnep-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_bnep_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+tools_smp_tester_SOURCES = tools/smp-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_smp_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+tools_gap_tester_SOURCES = tools/gap-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_gap_tester_LDADD =  lib/libbluetooth-internal.la \
+				gdbus/libgdbus-internal.la \
+				src/libshared-glib.la \
+				@GLIB_LIBS@ @DBUS_LIBS@
+
+tools_sco_tester_SOURCES = tools/sco-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_sco_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h
+tools_hci_tester_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+tools_userchan_tester_SOURCES = tools/userchan-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_userchan_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+endif
+
+if TOOLS
+bin_PROGRAMS += tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \
+			tools/rfcomm tools/rctest tools/l2test tools/l2ping \
+			tools/sdptool tools/ciptool tools/bccmd \
+			tools/bluemoon tools/hex2hcd tools/mpris-proxy
+
+tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
+						tools/hciattach_st.c \
+						tools/hciattach_ti.c \
+						tools/hciattach_tialt.c \
+						tools/hciattach_ath3k.c \
+						tools/hciattach_qualcomm.c \
+						tools/hciattach_intel.c \
+						tools/hciattach_bcm43xx.c
+tools_hciattach_LDADD = lib/libbluetooth-internal.la
+
+tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c
+tools_hciconfig_LDADD = lib/libbluetooth-internal.la
+
+tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c
+tools_hcitool_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
+
+tools_hcidump_SOURCES = tools/hcidump.c \
+				tools/parser/parser.h tools/parser/parser.c \
+				tools/parser/lmp.c \
+				tools/parser/hci.c \
+				tools/parser/l2cap.h tools/parser/l2cap.c \
+				tools/parser/amp.c \
+				tools/parser/smp.c \
+				tools/parser/att.c \
+				tools/parser/sdp.h tools/parser/sdp.c \
+				tools/parser/rfcomm.h tools/parser/rfcomm.c \
+				tools/parser/bnep.c \
+				tools/parser/cmtp.c \
+				tools/parser/hidp.c \
+				tools/parser/hcrp.c \
+				tools/parser/avdtp.c \
+				tools/parser/avctp.c \
+				tools/parser/avrcp.c \
+				tools/parser/sap.c \
+				tools/parser/obex.c \
+				tools/parser/capi.c \
+				tools/parser/ppp.c \
+				tools/parser/tcpip.c \
+				tools/parser/ericsson.c \
+				tools/parser/csr.c \
+				tools/parser/bpa.c
+tools_hcidump_LDADD = lib/libbluetooth-internal.la
+
+tools_rfcomm_LDADD = lib/libbluetooth-internal.la
+
+tools_rctest_LDADD = lib/libbluetooth-internal.la
+
+tools_l2test_LDADD = lib/libbluetooth-internal.la
+
+tools_l2ping_LDADD = lib/libbluetooth-internal.la
+
+tools_sdptool_SOURCES = tools/sdptool.c src/sdp-xml.h src/sdp-xml.c
+tools_sdptool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_ciptool_LDADD = lib/libbluetooth-internal.la
+
+tools_bccmd_SOURCES = tools/bccmd.c tools/csr.h tools/csr.c \
+			tools/csr_hci.c tools/csr_usb.c \
+			tools/csr_h4.c tools/csr_3wire.c \
+			tools/csr_bcsp.c tools/ubcsp.h tools/ubcsp.c
+tools_bccmd_LDADD = lib/libbluetooth-internal.la
+
+tools_bluemoon_SOURCES = tools/bluemoon.c monitor/bt.h
+tools_bluemoon_LDADD = src/libshared-mainloop.la
+
+tools_hex2hcd_SOURCES = tools/hex2hcd.c
+
+tools_mpris_proxy_SOURCES = tools/mpris-proxy.c
+tools_mpris_proxy_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
+
+dist_man_MANS += tools/hciattach.1 tools/hciconfig.1 \
+			tools/hcitool.1 tools/hcidump.1 \
+			tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \
+			tools/sdptool.1 tools/ciptool.1 tools/bccmd.1
+else
+EXTRA_DIST += tools/hciattach.1 tools/hciconfig.1 \
+			tools/hcitool.1 tools/hcidump.1 \
+			tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \
+			tools/sdptool.1 tools/ciptool.1 tools/bccmd.1
+endif
+
+if HID2HCI
+udevdir = @UDEV_DIR@
+
+udev_PROGRAMS = tools/hid2hci
+
+tools_hid2hci_LDADD = @UDEV_LIBS@
+
+dist_man_MANS += tools/hid2hci.1
+else
+EXTRA_DIST += tools/hid2hci.1
+endif
+
+if EXPERIMENTAL
+bin_PROGRAMS += tools/btattach
+
+noinst_PROGRAMS += tools/bdaddr tools/avinfo tools/avtest \
+			tools/scotest tools/amptest tools/hwdb \
+			tools/hcieventmask tools/hcisecfilter \
+			tools/btinfo \
+			tools/btsnoop tools/btproxy \
+			tools/btiotest tools/bneptest tools/mcaptest \
+			tools/cltest tools/oobtest tools/seq2bseq \
+			tools/nokfw tools/create-image \
+			tools/eddystone tools/ibeacon \
+			tools/btgatt-client tools/btgatt-server \
+			tools/test-runner tools/check-selftest
+
+tools_bdaddr_SOURCES = tools/bdaddr.c src/oui.h src/oui.c
+tools_bdaddr_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
+
+tools_avinfo_LDADD = lib/libbluetooth-internal.la
+
+tools_avtest_LDADD = lib/libbluetooth-internal.la
+
+tools_scotest_LDADD = lib/libbluetooth-internal.la
+
+tools_amptest_LDADD = lib/libbluetooth-internal.la
+
+tools_hwdb_LDADD = lib/libbluetooth-internal.la
+
+tools_hcieventmask_LDADD = lib/libbluetooth-internal.la
+
+tools_btinfo_SOURCES = tools/btinfo.c monitor/bt.h
+tools_btinfo_LDADD = src/libshared-mainloop.la
+
+tools_btattach_SOURCES = tools/btattach.c monitor/bt.h
+tools_btattach_LDADD = src/libshared-mainloop.la
+
+tools_btsnoop_SOURCES = tools/btsnoop.c
+tools_btsnoop_LDADD = src/libshared-mainloop.la
+
+tools_btproxy_SOURCES = tools/btproxy.c monitor/bt.h
+tools_btproxy_LDADD = src/libshared-mainloop.la
+
+tools_btiotest_SOURCES = tools/btiotest.c btio/btio.h btio/btio.c
+tools_btiotest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_mcaptest_SOURCES = tools/mcaptest.c \
+				btio/btio.h btio/btio.c \
+				src/log.c src/log.h \
+				profiles/health/mcap.h profiles/health/mcap.c
+tools_mcaptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ -lrt
+
+tools_bneptest_SOURCES = tools/bneptest.c \
+				btio/btio.h btio/btio.c \
+				src/log.h src/log.c \
+				profiles/network/bnep.h profiles/network/bnep.c
+tools_bneptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_cltest_SOURCES = tools/cltest.c
+tools_cltest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
+
+tools_oobtest_SOURCES = tools/oobtest.c
+tools_oobtest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
+
+tools_seq2bseq_SOURCES = tools/seq2bseq.c
+
+tools_nokfw_SOURCES = tools/nokfw.c
+
+tools_create_image_SOURCES = tools/create-image.c
+
+tools_eddystone_SOURCES = tools/eddystone.c monitor/bt.h
+tools_eddystone_LDADD = src/libshared-mainloop.la
+
+tools_ibeacon_SOURCES = tools/ibeacon.c monitor/bt.h
+tools_ibeacon_LDADD = src/libshared-mainloop.la
+
+tools_btgatt_client_SOURCES = tools/btgatt-client.c src/uuid-helper.c
+tools_btgatt_client_LDADD = src/libshared-mainloop.la \
+						lib/libbluetooth-internal.la
+
+tools_btgatt_server_SOURCES = tools/btgatt-server.c src/uuid-helper.c
+tools_btgatt_server_LDADD = src/libshared-mainloop.la \
+						lib/libbluetooth-internal.la
+
+dist_man_MANS += tools/btattach.1
+
+EXTRA_DIST += tools/bdaddr.1
+else
+EXTRA_DIST += tools/btattach.1
+endif
+
+if READLINE
+noinst_PROGRAMS += attrib/gatttool tools/btmgmt \
+			tools/obex-client-tool tools/obex-server-tool \
+			tools/bluetooth-player tools/obexctl
+
+attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
+				attrib/gattrib.c btio/btio.c \
+				attrib/gatttool.h attrib/interactive.c \
+				attrib/utils.c src/log.c client/display.c \
+				client/display.h
+attrib_gatttool_LDADD = lib/libbluetooth-internal.la \
+			src/libshared-glib.la @GLIB_LIBS@ -lreadline
+
+tools_obex_client_tool_SOURCES = $(gobex_sources) $(btio_sources) \
+						tools/obex-client-tool.c
+tools_obex_client_tool_LDADD = lib/libbluetooth-internal.la \
+						@GLIB_LIBS@ -lreadline
+
+tools_obex_server_tool_SOURCES = $(gobex_sources) $(btio_sources) \
+						tools/obex-server-tool.c
+tools_obex_server_tool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_bluetooth_player_SOURCES = tools/bluetooth-player.c \
+				client/display.h client/display.c
+tools_bluetooth_player_LDADD = gdbus/libgdbus-internal.la \
+				@GLIB_LIBS@ @DBUS_LIBS@ -lreadline
+
+tools_obexctl_SOURCES = tools/obexctl.c \
+				client/display.h client/display.c
+tools_obexctl_LDADD = gdbus/libgdbus-internal.la \
+				@GLIB_LIBS@ @DBUS_LIBS@ -lreadline
+
+tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c client/display.c
+tools_btmgmt_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la \
+				-lreadline
+endif
+
+if EXPERIMENTAL
+noinst_PROGRAMS += tools/gatt-service
+
+tools_gatt_service_SOURCES = tools/gatt-service.c
+tools_gatt_service_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ gdbus/libgdbus-internal.la
+
+noinst_PROGRAMS += profiles/iap/iapd
+
+profiles_iap_iapd_SOURCES = profiles/iap/main.c
+profiles_iap_iapd_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
+endif
+
+if CUPS
+cupsdir = $(libdir)/cups/backend
+
+cups_PROGRAMS = profiles/cups/bluetooth
+
+profiles_cups_bluetooth_SOURCES = profiles/cups/main.c \
+					profiles/cups/cups.h \
+					profiles/cups/sdp.c \
+					profiles/cups/spp.c \
+					profiles/cups/hcrp.c
+
+profiles_cups_bluetooth_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ \
+				lib/libbluetooth-internal.la \
+				gdbus/libgdbus-internal.la
+endif
+
+test_scripts += test/sap_client.py test/bluezutils.py \
+		test/dbusdef.py test/monitor-bluetooth test/list-devices \
+		test/test-discovery test/test-manager test/test-adapter \
+		test/test-device test/simple-agent \
+		test/simple-endpoint test/test-sap-server \
+		test/test-proximity test/test-network \
+		test/test-thermometer test/test-profile test/test-health \
+		test/test-health-sink test/service-record.dtd \
+		test/service-did.xml test/service-spp.xml test/service-opp.xml \
+		test/service-ftp.xml test/simple-player test/test-nap \
+		test/test-heartrate test/test-alert test/test-hfp \
+		test/test-cyclingspeed test/opp-client test/ftp-client \
+		test/pbap-client test/map-client test/example-advertisement \
+		test/example-gatt-server test/example-gatt-client \
+		test/test-gatt-profile
diff --git a/repo/NEWS b/repo/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/repo/NEWS
diff --git a/repo/README b/repo/README
new file mode 100644
index 0000000..c991ab0
--- /dev/null
+++ b/repo/README
@@ -0,0 +1,127 @@
+BlueZ - Bluetooth protocol stack for Linux
+******************************************
+
+Copyright (C) 2000-2001  Qualcomm Incorporated
+Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+
+
+Compilation and installation
+============================
+
+In order to compile Bluetooth utilities you need following software packages:
+	- GCC compiler
+	- GLib library
+	- D-Bus library
+	- udev library (optional)
+	- readline (command line clients)
+
+To configure run:
+	./configure --prefix=/usr --mandir=/usr/share/man \
+				--sysconfdir=/etc --localstatedir=/var
+
+Configure automatically searches for all required components and packages.
+
+To compile and install run:
+	make && make install
+
+
+Configuration and options
+=========================
+
+For a working system, certain configuration options need to be enabled:
+
+	--enable-library
+
+		Enable installation of Bluetooth library
+
+		By default the Bluetooth library is no longer installed.
+
+		The user interfaces or command line utilities do not
+		require an installed Bluetooth library anymore. This
+		option is provided for legacy third party applications
+		that still depend on the library.
+
+		When the library installation is enabled, it is a good
+		idea to use a separate bluez-library or libbluetooth
+		package for it.
+
+	--disable-tools
+
+		Disable support for Bluetooth utilities
+
+		By default the Bluetooth utilities are built and also
+		installed. For production systems the tools are not
+		needed and this option allows to disable them to save
+		build time and disk space.
+
+		When the tools are selected, it is a good idea to
+		use a separate bluez-tools package for them.
+
+	--disable-cups
+
+		Disable support for CUPS printer backend
+
+		By default the printer backend for CUPS is build and
+		also installed. For systems that do not require printing
+		over Bluetooth, this options allows to disable it.
+
+		When the CUPS backend is selected, it is a good idea to
+		use a separate bluez-cups package for it.
+
+	--disable-monitor
+
+		Disable support for the Bluetooth monitor utility
+
+		By default the monitor utility is enabled. It provides
+		support for HCI level tracing and debugging. For systems
+		that don't require any kind of tracing or debugging
+		capabilities, this options allows to disable it.
+
+		The monitor utility should be placed in the main package
+		along with the daemons. It is universally useful.
+
+	--disable-client
+
+		Disable support for the command line client
+
+		By default the command line client is enabled and uses the
+		readline library. For specific systems where BlueZ is
+		configured by other means, the command line client can be
+		disabled and the dependency on readline is removed.
+
+		The client should be placed in the main package along
+		with the daemons. It is universally useful.
+
+	--disable-systemd
+
+		Disable integration with systemd
+
+		By default the integration with systemd is enabled and
+		installed. This gives the best integration into all
+		distributions based on systemd.
+
+		This option is provided for distributions that do not
+		support systemd. In that case all integration with the
+		init system is up to the package.
+
+	--enable-experimental
+
+		Enable experimental plugins
+
+		By default all plugins that are still in development
+		are disabled. This option can be used to enable them.
+
+		It is not recommended to enable this option for production
+		systems. The APIs or behavior of the experimental plugins
+		is unstable and might still change.
+
+
+Information
+===========
+
+Mailing lists:
+	linux-bluetooth@vger.kernel.org
+
+For additional information about the project visit BlueZ web site:
+	http://www.bluez.org
diff --git a/repo/TODO b/repo/TODO
new file mode 100644
index 0000000..8c710ef
--- /dev/null
+++ b/repo/TODO
@@ -0,0 +1,288 @@
+Background
+==========
+
+- Priority scale: High, Medium and Low
+
+- Complexity scale: C1, C2, C4 and C8.  The complexity scale is exponential,
+  with complexity 1 being the lowest complexity.  Complexity is a function
+  of both task 'complexity' and task 'scope'.
+
+  The general rule of thumb is that a complexity 1 task should take 1-2 weeks
+  for a person very familiar with BlueZ codebase.  Higher complexity tasks
+  require more time and have higher uncertainty.
+
+  Higher complexity tasks should be refined into several lower complexity tasks
+  once the task is better understood.
+
+General
+=======
+
+- UUID handling: Use the new functions created for UUID handling in all parts
+  of BlueZ code.  Currently, the new bt_uuid_* functions are being used by
+  GATT-related code only.
+
+  Priority: high
+  Complexity: C4
+
+- Update PBAP client/server implementation to 1.2 and create necessary APIs for
+  new features it introduces.
+
+  Priority: Medium
+  Complexity: C4
+
+- Create GOEP unit tests based on its test specification:
+
+  https://www.bluetooth.org/docman/handlers/DownloadDoc.ashx?doc_id=230559
+
+  Priority: Medium
+  Complexity: C2
+
+- Function in src/adapter.c to convert old storage files to new ini-file format
+  should be removed 6-8 months after first BlueZ 5 release.
+
+  Priority: Low
+  Complexity: C1
+
+- Remove usage of symlinks for drivers, such as profiles/input/suspend.c and
+  profiles/sap/sap.c. Instead, select drivers at runtime by using config
+  options or probing for running D-Bus services (using e.g.
+  g_dbus_add_service_watch()). Idea first mentioned on
+  http://thread.gmane.org/gmane.linux.bluez.kernel/30175/focus=30190.
+
+- Reuse connection handling code of src/profile.c also for built-in profiles
+  so plugins would only need to register their btd_profile and the core takes
+  care of the rest including listen to the right channel and manages the sdp
+  record. Once btd_profile manages the connection it can also notify about
+  their state, this probably remove the need of having callbacks to
+  .connect/.disconnect since their state can be tracked, it also enables any
+  plugin to track any profile state change which can be useful for e.g.
+  a connection policy plugin in case one is needed.
+
+  Priority: Low
+  Complexity: C2
+
+- Add queueing support for src/agent.c, currently if there is any request
+  pending the code fail with error EBUSY which is very inconvenient.
+
+  Priority: Low
+  Complexity: C2
+
+Low Energy
+==========
+
+- Connection modes. Adapter interface needs to be changed to manage
+  connection modes and adapter type. See Volume 3, Part C, section 9.3.
+  1. Mode management: Peripheral / Central
+
+  Priority: Medium
+  Complexity: C2
+
+- Advertising data. The D-Bus interface needs to be updated to enable setting
+  scan response data, and to read the advertising and scan response data which
+  has been broadcast from other LE devices.
+
+  Priority: Medium
+  Complexity: C2
+
+- Static random address setup and storage. Once this address is written
+  in a given remote, the address can not be changed anymore.
+
+  Priority: Low
+  Complexity: C1
+
+- Device Name Characteristic is a GAP characteristic for Low Energy. This
+  characteristic shall be integrated/used in the discovery procedure. The
+  idea is to report the value of this characteristic using DeviceFound signals.
+  Discussion with the community is needed before to start this task. Other GAP
+  characteristics for LE needs to follow a similar approach. It is not clear
+  if all GAP characteristics can be exposed using properties instead of a primary
+  service characteristics.
+  See Volume 3, Part C, section 12.1 for more information.
+
+  Priority: Low
+  Complexity: C2
+
+ATT/GATT (new shared stack)
+===========================
+
+- Add complete GATT test coverage in unit/test-gatt following the GATT test
+  spec. This could use shared/gatt-client and shared/gatt-server at the same
+  time to test both against eachother. We should definitely have tests for
+  gatt-server and gatt-client simultaneously on one side of the connection.
+
+  Priority: High
+  Complexity: C4
+
+- Write an example using client D-Bus API using C.
+
+  Priority: High
+  Complexity: C2
+
+- Write an example using client D-Bus API using python.
+
+  Priority: High
+  Complexity: C2
+
+- Define packed structs for ATT protocol PDUs in shared/att-types to improve
+  readability. We should probably do this once there are extensive unit tests
+  for gatt-client/gatt-server so that we don't accidentally break working code.
+
+  Priority: Medium
+  Complexity: C2
+
+- Use struct iovec to pass around byte buffers that will be sent over the wire,
+  instead of passing uint8_t and size_t parameters everywhere.
+
+  Priority: Medium
+  Complexity: C1
+
+- Persist client attribute cache across reboots.
+
+  Priority: Medium
+  Complexity: C4
+
+- Move all daemon plugins and profiles that are GATT based to use
+  shared/gatt-client instead of attrib/*. This is a complicated task that
+  potentially needs a new plugin/profile probing interface and a lot of
+  rewriting that can cause regressions in existing functionality.
+
+  Priority: Medium
+  Complexity: C4
+
+- Introduce a way for shared/gatt-server to check security permissions on the
+  current connection through bt_att.
+
+  Priority: Medium
+  Complexity: C2
+
+- Implement other low-priority ATT protocol operations for shared/gatt-server:
+
+      Read Multiple Request
+
+  Priority: Low
+  Complexity: C1
+
+- Implement the server portion of doc/gatt-api.txt using shared/gatt-server once
+  it exists.
+
+  Priority: Medium
+  Complexity: C4
+
+- Send out indications from the "Service Changed" characteristic upon
+  reconnection if a bonded device is not connected when the local database is
+  modified.
+
+  Priority: High
+  Complexity: C2
+
+- Unify the GATT server and client D-Bus implementations into a single module.
+  While these don't share a lot of code, keeping them all in src/gatt-dbus seems
+  to make more sense from an organizational perspective.
+
+  Priority: Low
+  Complexity: C1
+
+- Isolate all GATT code inside the daemon into its own module and perform
+  interaction with other modules (e.g. src/device.c) via callbacks. This
+  includes client/server management, tracking incoming/outgoing connections for
+  ATT, and callbacks to perform profile probing.
+
+  Priority: Low
+  Complexity: C4
+
+- Support included services in the GATT D-Bus client API.
+
+  Priority: Medium
+  Complexity: C1
+
+- The recently added support for ATT signed writes requires the following kernel
+  modules to be enabled:
+
+     CONFIG_CRYPTO_USER_API
+     CONFIG_CRYPTO_USER_API_HASH
+     CONFIG_CRYPTO_USER_API_SKCIPHER
+
+  Currently, if these are not enabled, bt_att_new silently returns NULL. We
+  should handle this more gracefully by not supporting signed writes if we can't
+  initialize bt_crypto while succeeding bt_att initialization regardless.
+
+  This behavior should be documented in the README.
+
+  Priority: High
+  Complexity: C1
+
+
+ATT/GATT (old/outdated)
+=======================
+
+- At the moment authentication and authorization is not supported at the
+  same time, read/write requirements in the attribute server needs to
+  be extended. According to Bluetooth Specification a server shall check
+  authentication and authorization requirements before any other check is
+  performed.
+
+  Priority: Medium
+  Complexity: C1
+
+- Implement ATT PDU validation. Malformed PDUs can cause division by zero
+  when decoding PDUs. A proper error PDU should be returned for this case.
+  See decoding function in att.c file.
+
+  Priority: Medium
+  Complexity: C1
+
+- Refactor read_by_group() and read_by_type() in src/attrib-server.c
+  (they've grown simply too big). First step could be to move out the
+  long for-loops to new functions called e.g. get_groups() and get_types().
+
+  Priority: Low
+  Complexity: C1
+
+- Agent for characteristics: Agent interface should be extended to support
+  authorization per characteristic if the remote is not in the trusted list.
+
+  Priority: Low
+  Complexity: C1
+
+- gatttool should have the ability to wait for req responses before
+  quitting (some servers require a small sleep even with cmd's). Maybe a
+  --delay-exit or --timeout command line switch.
+
+  Priority: Low
+  Complexity: C1
+
+- Client needs to export a property in the Device Characteristic hierarchy
+  to manage characteristic value changes reports in the remote device.
+  Currently, Client Characteristic Configuration attribute is not exposed
+  as an object. The user needs to use gatttool to change the value of the
+  this attribute to receive notification/indications. Export this attribute
+  as a property is a proposal that needs further discussion.
+
+  Priority: Low
+  Complexity: C1
+
+- Attribute server should process queued GATT/ATT commands if the
+  client disconnects. The client can simply send a command and quit,
+  without wait for a response(ex: Write Command). For this scenario
+  that the client disconnects the link quickly the queued received
+  command is ignored.
+
+  Priority: Low
+  Complecity: C1
+
+- Implement Server characteristic Configuration support in the attribute
+  server to manage characteristic value broadcasting. There is a single
+  instance of the Server Characteristic Configuration for all clients.
+  See Volume 3, Part G, section 3.3.3.4 for more information.
+
+  Priority: Low
+  Complexity: C1
+
+- Long write is not implemented. Attribute server, client and command line
+  tool shall be changed to support this feature.
+
+  Priority: Low
+  Complexity: C2
+
+Management Interface
+====================
diff --git a/repo/acinclude.m4 b/repo/acinclude.m4
new file mode 100644
index 0000000..bc39c6d
--- /dev/null
+++ b/repo/acinclude.m4
@@ -0,0 +1,62 @@
+AC_DEFUN([AC_PROG_CC_PIE], [
+	AC_CACHE_CHECK([whether ${CC-cc} accepts -fPIE], ac_cv_prog_cc_pie, [
+		echo 'void f(){}' > conftest.c
+		if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then
+			ac_cv_prog_cc_pie=yes
+		else
+			ac_cv_prog_cc_pie=no
+		fi
+		rm -rf conftest*
+	])
+])
+
+AC_DEFUN([COMPILER_FLAGS], [
+	with_cflags=""
+	if (test "$USE_MAINTAINER_MODE" = "yes"); then
+		with_cflags="$with_cflags -Wall -Werror -Wextra"
+		with_cflags="$with_cflags -Wno-unused-parameter"
+		with_cflags="$with_cflags -Wno-missing-field-initializers"
+		with_cflags="$with_cflags -Wdeclaration-after-statement"
+		with_cflags="$with_cflags -Wmissing-declarations"
+		with_cflags="$with_cflags -Wredundant-decls"
+		with_cflags="$with_cflags -Wcast-align"
+		with_cflags="$with_cflags -Wswitch-enum"
+		with_cflags="$with_cflags -Wformat -Wformat-security"
+		with_cflags="$with_cflags -DG_DISABLE_DEPRECATED"
+		with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28"
+		with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_28"
+	fi
+	AC_SUBST([WARNING_CFLAGS], $with_cflags)
+])
+
+AC_DEFUN([MISC_FLAGS], [
+	misc_cflags=""
+	misc_ldflags=""
+	AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization],
+			[disable code optimization through compiler]), [
+		if (test "${enableval}" = "no"); then
+			misc_cflags="$misc_cflags -O0"
+		fi
+	])
+	AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],
+			[enable compiling with debugging information]), [
+		if (test "${enableval}" = "yes" &&
+				test "${ac_cv_prog_cc_g}" = "yes"); then
+			misc_cflags="$misc_cflags -g"
+		fi
+	])
+	AC_ARG_ENABLE(pie, AC_HELP_STRING([--enable-pie],
+			[enable position independent executables flag]), [
+		if (test "${enableval}" = "yes" &&
+				test "${ac_cv_prog_cc_pie}" = "yes"); then
+			misc_cflags="$misc_cflags -fPIC"
+			misc_ldflags="$misc_ldflags -pie"
+		fi
+	])
+	if (test "$enable_coverage" = "yes"); then
+		misc_cflags="$misc_cflags --coverage"
+		misc_ldflags="$misc_ldflags --coverage"
+	fi
+	AC_SUBST([MISC_CFLAGS], $misc_cflags)
+	AC_SUBST([MISC_LDFLAGS], $misc_ldflags)
+])
diff --git a/repo/android/Android.mk b/repo/android/Android.mk
new file mode 100644
index 0000000..38ef4aa
--- /dev/null
+++ b/repo/android/Android.mk
@@ -0,0 +1,855 @@
+LOCAL_PATH := external/bluetooth
+
+# Retrieve BlueZ version from configure.ac file
+BLUEZ_VERSION := `grep "^AC_INIT" $(LOCAL_PATH)/bluez/configure.ac | sed -e "s/.*,.\(.*\))/\1/"`
+
+ANDROID_VERSION := $(shell echo $(PLATFORM_VERSION) | awk -F. '{ printf "0x%02d%02d%02d",$$1,$$2,$$3 }')
+
+ANDROID_GE_5_0_0 := $(shell test `echo $$(($(ANDROID_VERSION)))` -lt `echo $$((0x050000))`; echo $$?)
+
+# Specify pathmap for glib and sbc
+pathmap_INCL += glib:external/bluetooth/glib \
+		sbc:external/bluetooth/sbc \
+
+# Specify common compiler flags
+BLUEZ_COMMON_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\" \
+			-DANDROID_VERSION=$(ANDROID_VERSION) \
+			-DANDROID_STORAGEDIR=\"/data/misc/bluetooth\" \
+			-DHAVE_LINUX_IF_ALG_H \
+			-DHAVE_LINUX_TYPES_H \
+
+# Enable warnings enabled in autotools build
+BLUEZ_COMMON_CFLAGS += -Wall -Wextra \
+			-Wdeclaration-after-statement \
+			-Wmissing-declarations \
+			-Wredundant-decls \
+			-Wcast-align \
+
+# Disable warnings enabled by Android but not enabled in autotools build
+BLUEZ_COMMON_CFLAGS += -Wno-pointer-arith \
+			-Wno-missing-field-initializers \
+			-Wno-unused-parameter \
+
+#
+# Android BlueZ daemon (bluetoothd)
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/android/main.c \
+	bluez/android/bluetooth.c \
+	bluez/profiles/scanparam/scpp.c \
+	bluez/profiles/deviceinfo/dis.c \
+	bluez/profiles/battery/bas.c \
+	bluez/profiles/input/hog-lib.c \
+	bluez/android/hidhost.c \
+	bluez/android/socket.c \
+	bluez/android/ipc.c \
+	bluez/android/avdtp.c \
+	bluez/android/a2dp.c \
+	bluez/android/a2dp-sink.c \
+	bluez/android/avctp.c \
+	bluez/android/avrcp.c \
+	bluez/android/avrcp-lib.c \
+	bluez/android/pan.c \
+	bluez/android/handsfree.c \
+	bluez/android/handsfree-client.c \
+	bluez/android/gatt.c \
+	bluez/android/health.c \
+	bluez/android/sco.c \
+	bluez/profiles/health/mcap.c \
+	bluez/android/map-client.c \
+	bluez/android/log.c \
+	bluez/src/shared/mgmt.c \
+	bluez/src/shared/util.c \
+	bluez/src/shared/queue.c \
+	bluez/src/shared/ringbuf.c \
+	bluez/src/shared/hfp.c \
+	bluez/src/shared/gatt-db.c \
+	bluez/src/shared/io-glib.c \
+	bluez/src/shared/timeout-glib.c \
+	bluez/src/shared/crypto.c \
+	bluez/src/shared/uhid.c \
+	bluez/src/shared/att.c \
+	bluez/src/sdpd-database.c \
+	bluez/src/sdpd-service.c \
+	bluez/src/sdpd-request.c \
+	bluez/src/sdpd-server.c \
+	bluez/src/uuid-helper.c \
+	bluez/src/eir.c \
+	bluez/lib/sdp.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+	bluez/lib/uuid.c \
+	bluez/btio/btio.c \
+	bluez/src/sdp-client.c \
+	bluez/profiles/network/bnep.c \
+	bluez/attrib/gattrib.c \
+	bluez/attrib/gatt.c \
+	bluez/attrib/att.c
+
+LOCAL_C_INCLUDES := \
+	$(call include-path-for, glib) \
+	$(call include-path-for, glib)/glib \
+
+LOCAL_C_INCLUDES += \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+	libglib \
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_TAGS := optional
+
+# for userdebug/eng this module is bluetoothd-main since bluetoothd is used as
+# wrapper to launch bluetooth with Valgrind
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+LOCAL_MODULE := bluetoothd-main
+LOCAL_STRIP_MODULE := false
+else
+LOCAL_MODULE := bluetoothd
+endif
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# bluetooth.default.so HAL
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/android/hal-ipc.c \
+	bluez/android/hal-bluetooth.c \
+	bluez/android/hal-socket.c \
+	bluez/android/hal-hidhost.c \
+	bluez/android/hal-pan.c \
+	bluez/android/hal-a2dp.c \
+	bluez/android/hal-avrcp.c \
+	bluez/android/hal-handsfree.c \
+	bluez/android/hal-gatt.c \
+	bluez/android/hal-utils.c \
+	bluez/android/hal-health.c \
+
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_SRC_FILES += \
+	bluez/android/hal-handsfree-client.c \
+	bluez/android/hal-map-client.c \
+	bluez/android/hal-a2dp-sink.c \
+	bluez/android/hal-avrcp-ctrl.c
+endif
+
+LOCAL_C_INCLUDES += \
+	$(call include-path-for, system-core) \
+	$(call include-path-for, libhardware) \
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE := bluetooth.default
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
+
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# haltest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/android/client/haltest.c \
+	bluez/android/client/pollhandler.c \
+	bluez/android/client/terminal.c \
+	bluez/android/client/history.c \
+	bluez/android/client/tabcompletion.c \
+	bluez/android/client/if-audio.c \
+	bluez/android/client/if-sco.c \
+	bluez/android/client/if-av.c \
+	bluez/android/client/if-rc.c \
+	bluez/android/client/if-bt.c \
+	bluez/android/client/if-hf.c \
+	bluez/android/client/if-hh.c \
+	bluez/android/client/if-pan.c \
+	bluez/android/client/if-hl.c \
+	bluez/android/client/if-sock.c \
+	bluez/android/client/if-gatt.c \
+	bluez/android/hal-utils.c \
+
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_SRC_FILES += \
+	bluez/android/client/if-hf-client.c \
+	bluez/android/client/if-mce.c \
+	bluez/android/client/if-av-sink.c \
+	bluez/android/client/if-rc-ctrl.c
+endif
+
+LOCAL_C_INCLUDES += \
+	$(call include-path-for, system-core) \
+	$(call include-path-for, libhardware) \
+
+LOCAL_C_INCLUDES += \
+	$(LOCAL_PATH)/bluez/android \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) -Wno-declaration-after-statement
+
+LOCAL_SHARED_LIBRARIES := \
+	libhardware \
+	libcutils \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := haltest
+
+include $(BUILD_EXECUTABLE)
+
+#
+# mcaptest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/src/log.c \
+	bluez/btio/btio.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+	bluez/profiles/health/mcap.c \
+	bluez/tools/mcaptest.c \
+
+LOCAL_C_INCLUDES := \
+	$(call include-path-for, glib) \
+	$(call include-path-for, glib)/glib \
+
+LOCAL_C_INCLUDES += \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+	libglib \
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := mcaptest
+
+include $(BUILD_EXECUTABLE)
+
+#
+# bneptest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/src/log.c \
+	bluez/btio/btio.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+	bluez/profiles/network/bnep.c \
+	bluez/tools/bneptest.c \
+
+LOCAL_C_INCLUDES := \
+	$(call include-path-for, glib) \
+	$(call include-path-for, glib)/glib \
+
+LOCAL_C_INCLUDES += \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+	libglib \
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := bneptest
+
+include $(BUILD_EXECUTABLE)
+
+#
+# avdtptest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/android/avdtptest.c \
+	bluez/android/avdtp.c \
+	bluez/src/log.c \
+	bluez/btio/btio.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+	bluez/src/shared/util.c \
+	bluez/src/shared/queue.c \
+
+LOCAL_C_INCLUDES += \
+	$(LOCAL_PATH)/bluez \
+	$(call include-path-for, glib) \
+	$(call include-path-for, glib)/glib \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+	libglib \
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := avdtptest
+
+include $(BUILD_EXECUTABLE)
+
+#
+# btmon
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/monitor/main.c \
+	bluez/monitor/display.c \
+	bluez/monitor/hcidump.c \
+	bluez/monitor/control.c \
+	bluez/monitor/packet.c \
+	bluez/monitor/l2cap.c \
+	bluez/monitor/avctp.c \
+	bluez/monitor/avdtp.c \
+	bluez/monitor/a2dp.c \
+	bluez/monitor/rfcomm.c \
+	bluez/monitor/bnep.c \
+	bluez/monitor/uuid.c \
+	bluez/monitor/sdp.c \
+	bluez/monitor/vendor.c \
+	bluez/monitor/lmp.c \
+	bluez/monitor/crc.c \
+	bluez/monitor/ll.c \
+	bluez/monitor/hwdb.c \
+	bluez/monitor/keys.c \
+	bluez/monitor/ellisys.c \
+	bluez/monitor/analyze.c \
+	bluez/monitor/intel.c \
+	bluez/monitor/broadcom.c \
+	bluez/src/shared/util.c \
+	bluez/src/shared/queue.c \
+	bluez/src/shared/crypto.c \
+	bluez/src/shared/btsnoop.c \
+	bluez/src/shared/mainloop.c \
+	bluez/lib/hci.c \
+	bluez/lib/bluetooth.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := btmon
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# btproxy
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/btproxy.c \
+	bluez/src/shared/mainloop.c \
+	bluez/src/shared/util.c \
+	bluez/src/shared/ecc.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := btproxy
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# A2DP audio
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/android/hal-audio.c \
+	bluez/android/hal-audio-sbc.c \
+	bluez/android/hal-audio-aptx.c \
+
+LOCAL_C_INCLUDES = \
+	$(LOCAL_PATH)/bluez \
+	$(call include-path-for, system-core) \
+	$(call include-path-for, libhardware) \
+	$(call include-path-for, sbc) \
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libsbc \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) -Wno-declaration-after-statement
+LOCAL_LDFLAGS := -ldl
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := audio.a2dp.default
+
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# SCO audio
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := bluez/android/hal-sco.c \
+	bluez/android/hal-utils.c
+
+LOCAL_C_INCLUDES = \
+	$(call include-path-for, system-core) \
+	$(call include-path-for, libhardware) \
+	$(call include-path-for, audio-utils) \
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libaudioutils \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) -Wno-declaration-after-statement
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := audio.sco.default
+
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# l2cap-test
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/l2test.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := l2test
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# bluetoothd-snoop
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/android/bluetoothd-snoop.c \
+	bluez/src/shared/mainloop.c \
+	bluez/src/shared/btsnoop.c \
+	bluez/android/log.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+	$(LOCAL_PATH)/bluez/lib \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := bluetoothd-snoop
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# init.bluetooth.rc
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := init.bluetooth.rc
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := bluez/android/$(LOCAL_MODULE)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+
+include $(BUILD_PREBUILT)
+
+#
+# btmgmt
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/btmgmt.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+	bluez/lib/sdp.c \
+	bluez/src/shared/mainloop.c \
+	bluez/src/shared/io-mainloop.c \
+	bluez/src/shared/mgmt.c \
+	bluez/src/shared/queue.c \
+	bluez/src/shared/util.c \
+	bluez/src/shared/gap.c \
+	bluez/src/uuid-helper.c \
+	bluez/client/display.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+	$(LOCAL_PATH)/bluez/android/compat \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := btmgmt
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# hcitool
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/hcitool.c \
+	bluez/src/oui.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := hcitool
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# hciconfig
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	bluez/tools/hciconfig.c \
+	bluez/tools/csr.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := hciconfig
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# l2ping
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/l2ping.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := l2ping
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# avtest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/avtest.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := avtest
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# hciattach
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/hciattach.c \
+	bluez/tools/hciattach_st.c \
+	bluez/tools/hciattach_ti.c \
+	bluez/tools/hciattach_tialt.c \
+	bluez/tools/hciattach_ath3k.c \
+	bluez/tools/hciattach_qualcomm.c \
+	bluez/tools/hciattach_intel.c \
+	bluez/tools/hciattach_bcm43xx.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := hciattach
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# libsbc
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	sbc/sbc/sbc.c \
+	sbc/sbc/sbc_primitives.c \
+	sbc/sbc/sbc_primitives_mmx.c \
+	sbc/sbc/sbc_primitives_neon.c \
+	sbc/sbc/sbc_primitives_armv6.c \
+	sbc/sbc/sbc_primitives_iwmmxt.c \
+
+LOCAL_C_INCLUDES:= \
+	$(LOCAL_PATH)/sbc \
+
+LOCAL_CFLAGS:= \
+	-Os \
+	-Wno-sign-compare \
+	-Wno-missing-field-initializers \
+	-Wno-unused-parameter \
+	-Wno-type-limits \
+	-Wno-empty-body \
+
+LOCAL_MODULE := libsbc
+
+include $(BUILD_SHARED_LIBRARY)
+
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+
+#
+# bluetoothd (debug)
+# this is just a wrapper used in userdebug/eng to launch bluetoothd-main
+# with/without Valgrind
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/android/bluetoothd-wrapper.c \
+	bluez/android/hal-utils.c
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := bluetoothd
+
+LOCAL_REQUIRED_MODULES := \
+	bluetoothd-main \
+	valgrind \
+	memcheck-$(TARGET_ARCH)-linux \
+	vgpreload_core-$(TARGET_ARCH)-linux \
+	vgpreload_memcheck-$(TARGET_ARCH)-linux \
+	default.supp
+
+include $(BUILD_EXECUTABLE)
+
+endif
+
+#
+# bluetooth-headers
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bluetooth-headers
+LOCAL_NODULE_TAGS := optional
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+
+include_path := $(local-intermediates-dir)/include
+include_files := $(wildcard $(LOCAL_PATH)/bluez/lib/*.h)
+$(shell mkdir -p $(include_path)/bluetooth)
+$(foreach file,$(include_files),$(shell cp -u $(file) $(include_path)/bluetooth))
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(include_path)
+
+include $(BUILD_STATIC_LIBRARY)
+
+#
+# avtest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/avinfo.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := avinfo
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# rctest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/rctest.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+	bluez/lib/sdp.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := rctest
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
diff --git a/repo/android/Makefile.am b/repo/android/Makefile.am
new file mode 100644
index 0000000..154f8db
--- /dev/null
+++ b/repo/android/Makefile.am
@@ -0,0 +1,320 @@
+if ANDROID
+
+AM_CFLAGS += -DANDROID_VERSION=0x050100
+
+android_plugindir = $(abs_top_srcdir)/android/.libs
+
+noinst_PROGRAMS += android/system-emulator
+
+android_system_emulator_SOURCES = android/system-emulator.c
+android_system_emulator_LDADD = src/libshared-mainloop.la
+
+noinst_PROGRAMS += android/bluetoothd-snoop
+
+android_bluetoothd_snoop_SOURCES = android/bluetoothd-snoop.c src/log.c
+android_bluetoothd_snoop_LDADD = src/libshared-mainloop.la @GLIB_LIBS@
+
+noinst_PROGRAMS += android/bluetoothd
+
+android_bluetoothd_SOURCES = android/main.c \
+				src/log.c \
+				android/hal-msg.h \
+				android/audio-msg.h \
+				android/sco-msg.h \
+				android/utils.h \
+				src/sdpd-database.c src/sdpd-server.c \
+				src/sdpd-service.c src/sdpd-request.c \
+				src/uuid-helper.h src/uuid-helper.c \
+				src/eir.h src/eir.c \
+				android/bluetooth.h android/bluetooth.c \
+				android/hidhost.h android/hidhost.c \
+				profiles/scanparam/scpp.h \
+				profiles/scanparam/scpp.c \
+				profiles/deviceinfo/dis.h \
+				profiles/deviceinfo/dis.c \
+				profiles/battery/bas.h profiles/battery/bas.c \
+				profiles/input/hog-lib.h \
+				profiles/input/hog-lib.c \
+				android/ipc-common.h \
+				android/ipc.h android/ipc.c \
+				android/avdtp.h android/avdtp.c \
+				android/a2dp.h android/a2dp.c \
+				android/a2dp-sink.h android/a2dp-sink.c \
+				android/avctp.h android/avctp.c \
+				android/avrcp.h android/avrcp.c \
+				android/avrcp-lib.h android/avrcp-lib.c \
+				android/socket.h android/socket.c \
+				android/sco.h android/sco.c \
+				android/pan.h android/pan.c \
+				android/handsfree.h android/handsfree.c \
+				android/handsfree-client.c android/handsfree-client.h \
+				android/gatt.h android/gatt.c \
+				android/health.h android/health.c \
+				profiles/health/mcap.h profiles/health/mcap.c \
+				android/map-client.h android/map-client.c \
+				attrib/att.c attrib/att.h \
+				attrib/gatt.c attrib/gatt.h \
+				attrib/gattrib.c attrib/gattrib.h \
+				btio/btio.h btio/btio.c \
+				src/sdp-client.h src/sdp-client.c \
+				profiles/network/bnep.h profiles/network/bnep.c
+android_bluetoothd_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+plugin_LTLIBRARIES += android/bluetooth.default.la
+
+android_bluetooth_default_la_SOURCES = android/hal.h android/hal-bluetooth.c \
+					android/hal-socket.c \
+					android/hal-hidhost.c \
+					android/hal-health.c \
+					android/hal-pan.c \
+					android/hal-a2dp.c \
+					android/hal-a2dp-sink.c \
+					android/hal-avrcp.c \
+					android/hal-avrcp-ctrl.c \
+					android/hal-handsfree.c \
+					android/hal-handsfree-client.c \
+					android/hal-gatt.c \
+					android/hal-map-client.c \
+					android/hardware/bluetooth.h \
+					android/hardware/bt_av.h \
+					android/hardware/bt_gatt.h \
+					android/hardware/bt_gatt_client.h \
+					android/hardware/bt_gatt_server.h \
+					android/hardware/bt_gatt_types.h \
+					android/hardware/bt_hf.h \
+					android/hardware/bt_hh.h \
+					android/hardware/bt_hl.h \
+					android/hardware/bt_pan.h \
+					android/hardware/bt_rc.h \
+					android/hardware/bt_sock.h \
+					android/hardware/bt_hf_client.h \
+					android/hardware/bt_mce.h \
+					android/hardware/hardware.h \
+					android/cutils/properties.h \
+					android/ipc-common.h \
+					android/hal-log.h \
+					android/hal-ipc.h android/hal-ipc.c \
+					android/hal-utils.h android/hal-utils.c
+android_bluetooth_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+android_bluetooth_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+					-no-undefined
+
+noinst_PROGRAMS += android/avdtptest
+
+android_avdtptest_SOURCES = android/avdtptest.c \
+				src/log.h src/log.c \
+				btio/btio.h btio/btio.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/queue.h src/shared/queue.c \
+				android/avdtp.h android/avdtp.c
+android_avdtptest_CFLAGS = $(AM_CFLAGS)
+android_avdtptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+noinst_PROGRAMS += android/haltest
+
+android_haltest_SOURCES = android/client/haltest.c \
+				android/client/pollhandler.h \
+				android/client/pollhandler.c \
+				android/client/terminal.h \
+				android/client/terminal.c \
+				android/client/history.h \
+				android/client/history.c \
+				android/client/tabcompletion.c \
+				android/client/if-main.h \
+				android/client/if-av.c \
+				android/client/if-av-sink.c \
+				android/client/if-rc.c \
+				android/client/if-rc-ctrl.c \
+				android/client/if-bt.c \
+				android/client/if-gatt.c \
+				android/client/if-hf.c \
+				android/client/if-hf-client.c \
+				android/client/if-hh.c \
+				android/client/if-pan.c \
+				android/client/if-hl.c \
+				android/client/if-sock.c \
+				android/client/if-audio.c \
+				android/client/if-sco.c \
+				android/client/if-mce.c \
+				android/hardware/hardware.c \
+				android/hal-utils.h android/hal-utils.c
+android_haltest_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+				-DPLUGINDIR=\""$(android_plugindir)"\"
+android_haltest_LDFLAGS = -pthread -ldl -lm
+
+noinst_PROGRAMS += android/android-tester
+
+android_android_tester_SOURCES = emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c \
+				monitor/rfcomm.h \
+				android/hardware/hardware.c \
+				android/tester-bluetooth.c \
+				android/tester-socket.c \
+				android/tester-hidhost.c \
+				android/tester-pan.c \
+				android/tester-hdp.c \
+				android/tester-a2dp.c \
+				android/tester-avrcp.c \
+				android/tester-gatt.c \
+				android/tester-map-client.c \
+				android/tester-main.h android/tester-main.c
+android_android_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+				-DPLUGINDIR=\""$(android_plugindir)"\"
+android_android_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+android_android_tester_LDFLAGS = -pthread -ldl
+
+noinst_PROGRAMS += android/ipc-tester
+
+android_ipc_tester_SOURCES = emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c \
+				android/hal-utils.h android/hal-utils.c \
+				android/ipc-common.h android/ipc-tester.c
+android_ipc_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+android_ipc_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+plugin_LTLIBRARIES += android/audio.a2dp.default.la
+
+android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \
+					android/hal-msg.h \
+					android/hal-audio.h \
+					android/hal-audio.c \
+					android/hal-audio-sbc.c \
+					android/hal-audio-aptx.c \
+					android/hardware/audio.h \
+					android/hardware/audio_effect.h \
+					android/hardware/hardware.h \
+					android/system/audio.h
+android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+					@SBC_CFLAGS@
+android_audio_a2dp_default_la_LIBADD = @SBC_LIBS@
+android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+					-no-undefined -pthread -lrt
+
+plugin_LTLIBRARIES += android/audio.sco.default.la
+
+android_audio_sco_default_la_SOURCES = android/hal-log.h \
+					android/sco-msg.h \
+					android/hal-sco.c \
+					android/hardware/audio.h \
+					android/hardware/audio_effect.h \
+					android/hardware/hardware.h \
+					android/audio_utils/resampler.c \
+					android/audio_utils/resampler.h \
+					android/system/audio.h
+android_audio_sco_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+android_audio_sco_default_la_LIBADD = @SPEEXDSP_LIBS@
+android_audio_sco_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+					-no-undefined -lrt
+unit_tests += android/test-ipc
+
+android_test_ipc_SOURCES = android/test-ipc.c \
+				src/log.h src/log.c \
+				android/ipc-common.h \
+				android/ipc.c android/ipc.h
+android_test_ipc_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+endif
+
+EXTRA_DIST += android/Android.mk android/README \
+				android/compat/readline/history.h \
+				android/compat/readline/readline.h \
+				android/compat/wordexp.h \
+				android/bluetoothd-wrapper.c \
+				android/log.c \
+				android/bluetoothd.te \
+				android/bluetoothd_snoop.te \
+				android/init.bluetooth.rc \
+				android/hal-ipc-api.txt \
+				android/audio-ipc-api.txt \
+				android/cts.txt \
+				android/pics-rfcomm.txt \
+				android/pics-spp.txt \
+				android/pics-sdp.txt \
+				android/pics-l2cap.txt \
+				android/pics-gap.txt \
+				android/pics-did.txt \
+				android/pics-hid.txt \
+				android/pics-pan.txt \
+				android/pics-opp.txt \
+				android/pics-map.txt \
+				android/pics-pbap.txt \
+				android/pics-a2dp.txt \
+				android/pics-avctp.txt \
+				android/pics-avrcp.txt \
+				android/pics-hsp.txt \
+				android/pics-hfp.txt \
+				android/pics-gatt.txt \
+				android/pics-mcap.txt \
+				android/pics-hdp.txt \
+				android/pics-iopt.txt \
+				android/pics-sm.txt \
+				android/pics-mps.txt \
+				android/pics-hogp.txt \
+				android/pics-scpp.txt \
+				android/pics-dis.txt \
+				android/pics-avdtp.txt \
+				android/pics-gavdp.txt \
+				android/pics-bnep.txt \
+				android/pixit-l2cap.txt \
+				android/pixit-gap.txt \
+				android/pixit-did.txt \
+				android/pixit-hid.txt \
+				android/pixit-pan.txt \
+				android/pixit-opp.txt \
+				android/pixit-map.txt \
+				android/pixit-pbap.txt \
+				android/pixit-a2dp.txt \
+				android/pixit-avctp.txt \
+				android/pixit-avrcp.txt \
+				android/pixit-hsp.txt \
+				android/pixit-hfp.txt \
+				android/pixit-gatt.txt \
+				android/pixit-mcap.txt \
+				android/pixit-hdp.txt \
+				android/pixit-iopt.txt \
+				android/pixit-sm.txt \
+				android/pixit-mps.txt \
+				android/pixit-hogp.txt \
+				android/pixit-scpp.txt \
+				android/pixit-dis.txt \
+				android/pixit-rfcomm.txt \
+				android/pixit-spp.txt \
+				android/pixit-avdtp.txt \
+				android/pixit-gavdp.txt \
+				android/pixit-sdp.txt \
+				android/pixit-bnep.txt \
+				android/pts-rfcomm.txt \
+				android/pts-spp.txt \
+				android/pts-l2cap.txt \
+				android/pts-gap.txt \
+				android/pts-did.txt \
+				android/pts-hid.txt \
+				android/pts-pan.txt \
+				android/pts-opp.txt \
+				android/pts-map.txt \
+				android/pts-a2dp.txt \
+				android/pts-avrcp.txt \
+				android/pts-avctp.txt \
+				android/pts-pbap.txt \
+				android/pts-hfp.txt \
+				android/pts-gatt.txt \
+				android/pts-hsp.txt \
+				android/pts-iopt.txt \
+				android/pts-hdp.txt \
+				android/pts-mcap.txt \
+				android/pts-mps.txt \
+				android/pts-sm.txt \
+				android/pts-hogp.txt \
+				android/pts-scpp.txt \
+				android/pts-dis.txt \
+				android/pts-avdtp.txt \
+				android/pts-gavdp.txt \
+				android/pts-sdp.txt \
+				android/pts-bnep.txt
diff --git a/repo/android/README b/repo/android/README
new file mode 100644
index 0000000..fa4c42a
--- /dev/null
+++ b/repo/android/README
@@ -0,0 +1,454 @@
+BlueZ for Android
+*****************
+
+Since Android 4.2 there exists a well standardized HAL interface that the
+Bluetooth stack is expected to provide and which enables the easy replacement
+of the stack of choice on Android. Android BlueZ is intended as a drop-in
+replacement to Android provided Bluetooth stack.
+
+More details about BlueZ for Android architecture and components can be found
+in android/hal-ipc-api.txt file.
+
+Supported Android version: 4.4 KitKat and 5.0, 5.1 Lollipop
+
+
+Building and running on Android
+===============================
+
+Steps needed to build and run Android Open Source Project with integrated BlueZ.
+
+
+Build requirements
+------------------
+
+- GLib - Android 4.2 or later don't provide GLib and one must provide it in
+'external/bluetooth/glib' folder of Android tree. Sample Android GLib port
+is available at https://github.com/bluez-android/glib
+
+- SBC - A2DP code requires SBC library (version 1.2 or higher) present in
+'external/bluetooth/sbc' directory. Library is build from Android.mk provided
+by BlueZ. SBC code is available at git://git.kernel.org/pub/scm/bluetooth/sbc
+
+- Bionic support - Android 5.0 provides all required functionality. Running
+BlueZ on Android 4.4 requires backporting missing features (epoll_create1 and
+ppoll calls). Sample Bionic for Android 4.4 with all required features
+backported is available at
+https://github.com/bluez-android/aosp_platform_bionic
+
+Runtime requirements
+--------------------
+
+BlueZ HAL library requires 'bluetoothd' and 'bluetoothd-snoop' services to be
+available on Android system. Some permissions settings are also required.
+
+This can be done by importing init.bluetooth.rc file in init.rc file of targeted
+board:
+import init.bluetooth.rc
+
+For convenience examples are provided at:
+https://github.com/bluez-android/aosp_device_lge_mako           (Nexus 4)
+https://github.com/bluez-android/aosp_device_lge_hammerhead     (Nexus 5)
+https://github.com/bluez-android/aosp_device_asus_flo           (Nexus 7 2013)
+
+Security-Enhanced Linux in Android
+----------------------------------
+
+Since 5.0 release Android moved to full enforcement of SELinux. This requires
+proper policy to be provided for all BlueZ for Android services (and services
+interacting with BlueZ). Policies should be placed in external/selinux/ path.
+
+Required policy files are provided at:
+bluetoothd.te
+bluetoothd_snoop.te
+
+For convenience sepolicy.git with all required policies is available at:
+https://github.com/bluez-android/aosp_platform_external_sepolicy
+
+Downloading and building
+------------------------
+
+Building for Android requires full Android AOSP source tree. Sample Android tree
+with all required components present is available at
+https://github.com/bluez-android
+
+This tree provides support for Nexus4 (mako), Nexus 5 (hammerhead) and
+Nexus 7 2013 (flo, deb). Tree does not provide binary blobs needed to run
+Android on supported devices. Those can be obtained from
+https://developers.google.com/android/nexus/drivers. Binary blobs needs to be
+unpacked (EULA acceptance required) into 'vendor' directory of Android tree.
+
+Downloading:
+Android 5.0 - 'lollipop' branch
+Android 4.4 - 'kitkat' branch
+
+repo init -u https://github.com/bluez-android/aosp_platform_manifest \
+	-b lollipop
+repo sync
+
+Building:
+source build/envsetup.sh
+lunch aosp_<target>-userdebug
+make -j8
+
+Flashing:
+adb reboot bootloader
+fastboot flashall -w
+
+After full build is done it is possible to rebuild only BlueZ:
+'cd external/bluetooth/bluez/android/'
+'mm' (or 'mm -B' to force rebuilding of all files)
+'adb sync' to update target device.
+
+Downloading and building for Intel devices
+------------------------------------------
+
+Sample Android tree with all required components for Intel devices based on
+Intel reference image (https://01.org/android-ia) can be reconstructed following
+instructions below.
+
+This tree provides support for Dell XPS12, Minnowboard MAX, Intel NUC,
+Acer Iconia W700 and other devices mentioned in:
+https://01.org/android-ia/guides/devices
+
+Downloading:
+repo init -u https://github.com/01org/android-bluez-manifest.git -b android-ia \
+	-m topic/bluez
+repo sync
+
+Building:
+source build/envsetup.sh
+lunch haswell_generic-eng
+make -j8
+
+Installing:
+Live and Install image is $OUT/live.img
+Flash live.img to USB flash and boot from it. More instructions here:
+https://01.org/android-ia/guides/developers/build-and-install
+
+Linux Kernel requirements
+-------------------------
+
+BlueZ for Android uses Linux Bluetooth subsystem and it must be enabled in
+kernel. Minimal required version of management interface is 1.3. This
+corresponds to Linux 3.9 but latest available version is recommended. Other
+requirements include UHID and network bridge support.
+
+Following kernel options should be enabled:
+CONFIG_BT
+CONFIG_BT_RFCOMM
+CONFIG_BT_RFCOMM_TTY
+CONFIG_BT_BNEP
+CONFIG_BT_BNEP_MC_FILTER
+CONFIG_BT_BNEP_PROTO_FILTER
+CONFIG_BRIDGE
+CONFIG_UHID
+CONFIG_CRYPTO_CMAC
+CONFIG_CRYPTO_USER_API
+CONFIG_CRYPTO_USER_API_HASH
+CONFIG_CRYPTO_USER_API_SKCIPHER
+
+Also BT chip driver needs to be enabled e.g:
+CONFIG_BT_HCIBTUSB
+
+If it is not possible to use new enough Linux kernel one can use updated
+bluetooth subsystem from Backports project. More information about Backports can
+be found at https://backports.wiki.kernel.org. Sample kernels using backports
+for running BlueZ on Android are available at https://github.com/bluez-android.
+
+
+Running with Valgrind
+---------------------
+
+BlueZ for Android is preconfigured to be easily run under Valgrind memcheck.
+Appropriate configuration and required modules are automatically included when
+building either userdebug or eng variant of Android platform.
+
+Valgrind can be enabled in runtime by setting "persist.sys.bluetooth.valgrind"
+property to either literal "true" or any numeric value >0. For example:
+adb root
+adb shell setprop persist.sys.bluetooth.valgrind true
+
+After changing property value Bluetooth need to be restarted to apply changes
+(this can be done using UI, just disable and enable it again). Property is
+persistent, i.e. there's no need to enable Valgrind again after reboot.
+
+It's recommended to have unstripped libglib.so installed which will enable
+complete backtraces in Valgrind output. Otherwise, in many cases backtrace
+will break at e.g. g_free() function without prior callers. It's possible to
+have proper library installed automatically by appropriate entry in Android.mk,
+see https://github.com/bluez-android/glib for an example.
+
+When running with valgrind SElinux needs to be set into permissive mode. This
+can be done by executing 'setenforce 0' from root shell.
+
+
+Enabling BlueZ debugs
+---------------------
+
+BlueZ debug logs can be enabled in runtime by setting
+"persist.sys.bluetooth.debug" property to either literal "true" or any
+numeric value >0. For example:
+adb root
+adb shell setprop persist.sys.bluetooth.debug 1
+
+After changing property value Bluetooth needs to be restarted to apply changes.
+
+There is also a possibility to enable mgmt debug logs which also enables debugs
+as above. To enable it proceed in the same way as described above but use
+system properties called: persist.sys.bluetooth.mgmtdbg
+
+Note: Debugs are only available on NON USER build variants
+
+
+Customization
+-------------
+
+It is possible to customize BlueZ for Android through Android system properties.
+This may include enabling extra profiles or features inside HALs implementation
+These properties are read on Bluetooth stack startup only and require stack
+restart if changed. All customization properties names start with
+"persist.sys.bluetooth." or "ro.bluetooth." followed by specific HAL name e.g.
+"persist.sys.bluetooth.handsfree". If both are present "persist.sys.bluetooth."
+takes precedence. This allows for read only properties to be set during build
+leaving enough flexibility for developing or debugging purposes.
+This section list available customization options.
+
+Property	Value		Description
+-------------------------------------------
+mode		bredr		Enable BlueZ in BR/EDR mode
+		le		Enable BlueZ in LE mode
+		<none>		Enable BlueZ in default mode - enable BR/EDR/LE
+				if available.
+handsfree	hfp		Enable Handsfree Profile (HFP) with narrowband
+				speech only
+		hfp_wbs		Enable Handsfree Profile (HFP) with narrowband
+				and wideband speech support
+		<none>		Don't enable Handsfree Profile (HFP)
+vendor		<any>		Set vendor name in DIS. If not set fallback to
+				"ro.product.manufacturer".
+model		<any>		Set model name used as default adapter name.
+				If not set fallback to "ro.product.model".
+name		<any>		Set model number in DIS. If not set fallback to
+				"ro.product.name".
+serialno	<any>		Set serial number in DIS. If not set fallback to
+				"ro.serialno".
+systemid	<uint64>	Set system ID in DIS. Hex string encoded uint64.
+pnpid		<any>		PnP information used in DIS and DID profiles.
+				Required format: "Source:VID:PID:Version".
+				Source must be either "bluetooth" or "usb".
+				VID, PID and Version are uint16. Version is
+				optional.
+fwrev		<any>		Firmware revision in DIS. If not set fallback to
+				"ro.build.version.release".
+hwrew		<any>		Hardware revision in DIS. If not set fallback to
+				"ro.board.platform".
+
+
+Building and running on Linux
+-----------------------------
+
+It is possible to build and test BlueZ for Android daemon on Linux (eg. PC).
+Simply follow instructions available at README file in BlueZ top directory.
+Android daemon binary is located at android/bluetoothd. See next section on
+how to test Android daemon on Linux.
+
+
+Testing tool
+------------
+
+BT HAL test tools located in android/haltest is provided for HAL level testing
+of both Android daemon and HAL library. Start it with '-n' parameter and type
+'bluetooth init' in prompt to initialize HAL library. Running without parameter
+will make haltest try to initialize all services after start. On Android
+required bluetoothd service will be started automatically. On Linux it is
+required to start android/bluetoothd manually before init command timeout or
+use provided android/system-emulator, which takes care of launching daemon
+automatically on HAL library initialization. To deinitialize HAL library and
+stop daemon type 'bluetooth cleanup'. Type 'help' for more information. Tab
+completion is also supported.
+
+
+Implementation status
+=====================
+
+Summary of HALs implementation status.
+
+complete    - implementation is feature complete and Android Framework is able
+              to use it normally
+partial     - implementation is in progress and not all required features are
+              present, Android Framework is able to use some of features
+initial     - only initial implementations is present, Android Framework is
+              able to initialize but most likely not able to use it
+not started - no implementation, Android Framework is not able to initialize it
+
+Profile ID        HAL header         4.4 Status    5.0 status
+-------------------------------------------------------------
+core              bluetooth.h        complete      partial
+a2dp              bt_av.h            complete      complete
+gatt              bt_gatt.h          complete      partial
+                  bt_gatt_client.h   complete      partial
+                  bt_gatt_server.h   complete      partial
+handsfree         bt_hf.h            complete      complete
+hidhost           bt_hh.h            complete      complete
+health            bt_hl.h            complete      complete
+pan               bt_pan.h           complete      complete
+avrcp             bt_rc.h            complete      complete
+socket            bt_sock.h          complete      partial
+handsfree_client  bt_hf_client.h     N/A           complete
+map_client        bt_mce.h           N/A           complete
+a2dp_sink         bt_av.h            N/A           partial
+avrcp_ctrl        bt_rc.h            N/A           partial
+
+
+Implementation shortcomings
+===========================
+
+It is possible that some of HAL functionality (although being marked as
+complete) is missing implementation due to reasons like feature feasibility or
+necessity for latest Android Framework. This sections provides list of such
+deficiencies. Note that HAL library is always expected to fully implement HAL
+API so missing implementation might happen only in daemon.
+
+
+HAL Bluetooth
+-------------
+
+methods:
+dut_mode_send                      never called from Android Framework
+le_test_mode                       never called from Android Framework
+
+callbacks:
+dut_mode_recv_cb                   empty JNI implementation
+le_test_mode_cb                    empty JNI implementation
+
+properties:
+BT_PROPERTY_SERVICE_RECORD         not supported for adapter, for device this
+                                   property is returned as a response to
+                                   get_remote_service_record call
+
+BT_PROPERTY_REMOTE_VERSION_INFO    information required by this property (LMP
+                                   information) are not accessible from mgmt
+                                   interface, also marking this property as
+                                   settable is probably a typo in HAL header
+
+HAL Socket
+----------
+
+Support only for BTSOCK_RFCOMM socket type.
+
+
+HAL AVRCP
+---------
+
+methods:
+list_player_app_attr_rsp           never called from Android Framework
+list_player_app_value_rsp          never called from Android Framework
+get_player_app_value_rsp           never called from Android Framework
+get_player_app_attr_text_rsp       never called from Android Framework
+get_player_app_value_text_rsp      never called from Android Framework
+set_player_app_value_rsp           never called from Android Framework
+
+callbacks:
+list_player_app_attr_cb            NULL JNI implementation
+list_player_app_values_cb          NULL JNI implementation
+get_player_app_value_cb            NULL JNI implementation
+get_player_app_attrs_text_cb       NULL JNI implementation
+get_player_app_values_text_cb      NULL JNI implementation
+set_player_app_value_cb            NULL JNI implementation
+
+
+HAL GATT
+--------
+
+methods:
+client->set_adv_data               missing kernel support for vendor data
+client->connect                    is_direct parameter is ignored
+
+
+Audio SCO HAL
+=============
+
+When Bluetooth chip's audio is not wired directly to device audio, Audio SCO
+HAL is used to enable SCO support. It needs to be loaded by AudioFlinger
+following audio_policy.conf configuration. Example of configuration is shown
+below:
+
+...
+  sco {
+    outputs {
+      sco {
+        sampling_rates 8000|44100
+        channel_masks AUDIO_CHANNEL_OUT_STEREO
+        formats AUDIO_FORMAT_PCM_16_BIT
+        devices AUDIO_DEVICE_OUT_ALL_SCO
+      }
+    }
+    inputs {
+      sco {
+        sampling_rates 8000|44100
+        channel_masks AUDIO_CHANNEL_IN_MONO
+        formats AUDIO_FORMAT_PCM_16_BIT
+        devices AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET
+      }
+    }
+  }
+...
+
+Known Android issues
+====================
+
+It is possible that BlueZ is triggering bugs on Android Framework that could
+affect qualification or user experience. This section provides list of
+recommended Android fixes that are not part of latest AOSP release supported by
+BlueZ.
+
+For Android 5.1 Lollipop:
+https://android-review.googlesource.com/177314
+
+For Android 5.0 Lollipop:
+https://android-review.googlesource.com/99761
+https://android-review.googlesource.com/100297
+https://android-review.googlesource.com/102882
+https://android-review.googlesource.com/132733
+https://android-review.googlesource.com/132763
+https://android-review.googlesource.com/177314
+
+For Android 4.4 KitKat:
+https://android-review.googlesource.com/82757
+https://android-review.googlesource.com/87670
+https://android-review.googlesource.com/88384
+https://android-review.googlesource.com/99761
+https://android-review.googlesource.com/99850
+https://android-review.googlesource.com/100297
+https://android-review.googlesource.com/102882
+https://android-review.googlesource.com/177314
+
+Unimplemented Bluetooth features
+================================
+
+Some Bluetooth functionality require support from outside of BT stack
+eg. telephony stack. This sections describes profiles optional features not
+implemented due to lack of support in other Android subsystems or missing API
+in respective BT HALs.
+
+Profile		Feature				Comments
+--------------------------------------------------------
+HFP		Attach a phone number to	AT+BINP=1
+		a voice tag
+HFP		Enhanced Call Control		AT+CHLD={1x,2x}
+HFP		Explicit Call Transfer		AT+CHLD=4
+HFP		Response and Hold		AT+BTRH, +BTRH
+HFP		In-band Ring Tone		+BSIR
+AVRCP		Player Settings			HAL API present but not used
+AVRCP		Browsing			No HAL API
+GATT		Read multiple characteristics	No HAL API
+
+
+Reporting Bugs
+==============
+
+Bugs should be reported at https://01.org/jira/browse/BA. When reporting
+a bug please attach logs from logcat (logcat -v time) and HCI trace. Daemon
+debug logs should be enabled. When reporting daemon crash please run it under
+valgrind if possible. For details on how to enabled debug logs and valgrind see
+"Enabling BlueZ debugs" section.
diff --git a/repo/android/a2dp-sink.c b/repo/android/a2dp-sink.c
new file mode 100644
index 0000000..7c1e1a0
--- /dev/null
+++ b/repo/android/a2dp-sink.c
@@ -0,0 +1,84 @@
+/*
+ *
+ *  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "ipc.h"
+#include "a2dp-sink.h"
+
+static struct ipc *hal_ipc = NULL;
+
+static void bt_a2dp_sink_connect(const void *buf, uint16_t len)
+{
+	/* TODO */
+
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_CONNECT,
+							HAL_STATUS_UNSUPPORTED);
+}
+
+static void bt_a2dp_sink_disconnect(const void *buf, uint16_t len)
+{
+	/* TODO */
+
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_DISCONNECT,
+							HAL_STATUS_UNSUPPORTED);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_A2DP_CONNECT */
+	{ bt_a2dp_sink_connect, false, sizeof(struct hal_cmd_a2dp_connect) },
+	/* HAL_OP_A2DP_DISCONNECT */
+	{ bt_a2dp_sink_disconnect, false,
+				sizeof(struct hal_cmd_a2dp_disconnect) },
+};
+
+bool bt_a2dp_sink_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	DBG("");
+
+	hal_ipc = ipc;
+	ipc_register(hal_ipc, HAL_SERVICE_ID_A2DP_SINK, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+}
+
+void bt_a2dp_sink_unregister(void)
+{
+	DBG("");
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_A2DP_SINK);
+	hal_ipc = NULL;
+}
diff --git a/repo/android/a2dp-sink.h b/repo/android/a2dp-sink.h
new file mode 100644
index 0000000..d2c5ff4
--- /dev/null
+++ b/repo/android/a2dp-sink.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  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
+ *
+ */
+
+bool bt_a2dp_sink_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_a2dp_sink_unregister(void);
diff --git a/repo/android/a2dp.c b/repo/android/a2dp.c
new file mode 100644
index 0000000..f219042
--- /dev/null
+++ b/repo/android/a2dp.c
@@ -0,0 +1,1774 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "profiles/audio/a2dp-codecs.h"
+#include "src/shared/queue.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "a2dp.h"
+#include "utils.h"
+#include "bluetooth.h"
+#include "avdtp.h"
+#include "avrcp.h"
+#include "audio-msg.h"
+
+#define SVC_HINT_CAPTURING 0x08
+#define IDLE_TIMEOUT 1
+#define AUDIO_RETRY_TIMEOUT 2
+
+static GIOChannel *server = NULL;
+static GSList *devices = NULL;
+static GSList *endpoints = NULL;
+static GSList *setups = NULL;
+static bdaddr_t adapter_addr;
+static uint32_t record_id = 0;
+static guint audio_retry_id = 0;
+static bool audio_retrying = false;
+
+static struct ipc *hal_ipc = NULL;
+static struct ipc *audio_ipc = NULL;
+
+static struct queue *lseps = NULL;
+
+struct a2dp_preset {
+	void *data;
+	int8_t len;
+};
+
+struct a2dp_endpoint {
+	uint8_t id;
+	uint8_t codec;
+	struct avdtp_local_sep *sep;
+	struct a2dp_preset *caps;
+	GSList *presets;
+};
+
+struct a2dp_device {
+	bdaddr_t	dst;
+	uint8_t		state;
+	GIOChannel	*io;
+	struct avdtp	*session;
+	guint		idle_id;
+};
+
+struct a2dp_setup {
+	struct a2dp_device *dev;
+	struct a2dp_endpoint *endpoint;
+	struct a2dp_preset *preset;
+	struct avdtp_stream *stream;
+	uint8_t state;
+};
+
+static int device_cmp(gconstpointer s, gconstpointer user_data)
+{
+	const struct a2dp_device *dev = s;
+	const bdaddr_t *dst = user_data;
+
+	return bacmp(&dev->dst, dst);
+}
+
+static void preset_free(void *data)
+{
+	struct a2dp_preset *preset = data;
+
+	g_free(preset->data);
+	g_free(preset);
+}
+
+static void unregister_endpoint(void *data)
+{
+	struct a2dp_endpoint *endpoint = data;
+
+	if (endpoint->sep)
+		avdtp_unregister_sep(lseps, endpoint->sep);
+
+	if (endpoint->caps)
+		preset_free(endpoint->caps);
+
+	g_slist_free_full(endpoint->presets, preset_free);
+
+	g_free(endpoint);
+}
+
+static void setup_free(void *data)
+{
+	struct a2dp_setup *setup = data;
+
+	if (!g_slist_find(setup->endpoint->presets, setup->preset))
+		preset_free(setup->preset);
+
+	g_free(setup);
+}
+
+static void setup_remove(struct a2dp_setup *setup)
+{
+	setups = g_slist_remove(setups, setup);
+	setup_free(setup);
+}
+
+static void setup_remove_all_by_dev(struct a2dp_device *dev)
+{
+	GSList *l = setups;
+
+	while (l) {
+		struct a2dp_setup *setup = l->data;
+		GSList *next = g_slist_next(l);
+
+		if (setup->dev == dev)
+			setup_remove(setup);
+
+		l = next;
+	}
+}
+
+static void a2dp_device_free(void *data)
+{
+	struct a2dp_device *dev = data;
+
+	if (dev->idle_id > 0)
+		g_source_remove(dev->idle_id);
+
+	if (dev->session)
+		avdtp_unref(dev->session);
+
+	if (dev->io) {
+		g_io_channel_shutdown(dev->io, FALSE, NULL);
+		g_io_channel_unref(dev->io);
+	}
+
+	setup_remove_all_by_dev(dev);
+
+	g_free(dev);
+}
+
+static void a2dp_device_remove(struct a2dp_device *dev)
+{
+	devices = g_slist_remove(devices, dev);
+	a2dp_device_free(dev);
+}
+
+static struct a2dp_device *a2dp_device_new(const bdaddr_t *dst)
+{
+	struct a2dp_device *dev;
+
+	dev = g_new0(struct a2dp_device, 1);
+	bacpy(&dev->dst, dst);
+	devices = g_slist_prepend(devices, dev);
+
+	return dev;
+}
+
+static bool a2dp_device_connect(struct a2dp_device *dev, BtIOConnect cb)
+{
+	GError *err = NULL;
+
+	dev->io = bt_io_connect(cb, dev, NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_DEST_BDADDR, &dev->dst,
+					BT_IO_OPT_PSM, AVDTP_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	return true;
+}
+
+static void bt_a2dp_notify_state(struct a2dp_device *dev, uint8_t state)
+{
+	struct hal_ev_a2dp_conn_state ev;
+	char address[18];
+
+	if (dev->state == state)
+		return;
+
+	dev->state = state;
+
+	ba2str(&dev->dst, address);
+	DBG("device %s state %u", address, state);
+
+	bdaddr2android(&dev->dst, ev.bdaddr);
+	ev.state = state;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_CONN_STATE,
+							sizeof(ev), &ev);
+
+	if (state != HAL_A2DP_STATE_DISCONNECTED)
+		return;
+
+	bt_avrcp_disconnect(&dev->dst);
+
+	a2dp_device_remove(dev);
+}
+
+static void bt_audio_notify_state(struct a2dp_setup *setup, uint8_t state)
+{
+	struct hal_ev_a2dp_audio_state ev;
+	char address[18];
+
+	if (setup->state == state)
+		return;
+
+	setup->state = state;
+
+	ba2str(&setup->dev->dst, address);
+	DBG("device %s state %u", address, state);
+
+	bdaddr2android(&setup->dev->dst, ev.bdaddr);
+	ev.state = state;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_AUDIO_STATE,
+							sizeof(ev), &ev);
+}
+
+static void disconnect_cb(void *user_data)
+{
+	struct a2dp_device *dev = user_data;
+
+	bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+}
+
+static int sbc_check_config(void *caps, uint8_t caps_len, void *conf,
+							uint8_t conf_len)
+{
+	a2dp_sbc_t *cap, *config;
+
+	if (conf_len != caps_len || conf_len != sizeof(a2dp_sbc_t)) {
+		error("SBC: Invalid configuration size (%u)", conf_len);
+		return -EINVAL;
+	}
+
+	cap = caps;
+	config = conf;
+
+	if (!(cap->frequency & config->frequency)) {
+		error("SBC: Unsupported frequency (%u) by endpoint",
+							config->frequency);
+		return -EINVAL;
+	}
+
+	if (!(cap->channel_mode & config->channel_mode)) {
+		error("SBC: Unsupported channel mode (%u) by endpoint",
+							config->channel_mode);
+		return -EINVAL;
+	}
+
+	if (!(cap->block_length & config->block_length)) {
+		error("SBC: Unsupported block length (%u) by endpoint",
+							config->block_length);
+		return -EINVAL;
+	}
+
+	if (!(cap->allocation_method & config->allocation_method)) {
+		error("SBC: Unsupported allocation method (%u) by endpoint",
+							config->block_length);
+		return -EINVAL;
+	}
+
+	if (config->max_bitpool < cap->min_bitpool) {
+		error("SBC: Invalid maximun bitpool (%u < %u)",
+					config->max_bitpool, cap->min_bitpool);
+		return -EINVAL;
+	}
+
+	if (config->min_bitpool > cap->max_bitpool) {
+		error("SBC: Invalid minimun bitpool (%u > %u)",
+					config->min_bitpool, cap->min_bitpool);
+		return -EINVAL;
+	}
+
+	if (config->max_bitpool > cap->max_bitpool)
+		return -ERANGE;
+
+	if (config->min_bitpool < cap->min_bitpool)
+		return -ERANGE;
+
+	return 0;
+}
+
+static int aac_check_config(void *caps, uint8_t caps_len, void *conf,
+							uint8_t conf_len)
+{
+	a2dp_aac_t *cap, *config;
+
+	if (conf_len != caps_len || conf_len != sizeof(a2dp_aac_t)) {
+		error("AAC: Invalid configuration size (%u)", conf_len);
+		return -EINVAL;
+	}
+
+	cap = caps;
+	config = conf;
+
+	if (!(cap->object_type & config->object_type)) {
+		error("AAC: Unsupported object type (%u) by endpoint",
+							config->object_type);
+		return -EINVAL;
+	}
+
+	if (!(AAC_GET_FREQUENCY(*cap) & AAC_GET_FREQUENCY(*config))) {
+		error("AAC: Unsupported frequency (%u) by endpoint",
+						AAC_GET_FREQUENCY(*config));
+		return -EINVAL;
+	}
+
+	if (!(cap->channels & config->channels)) {
+		error("AAC: Unsupported channels (%u) by endpoint",
+							config->channels);
+		return -EINVAL;
+	}
+
+	/* VBR support in SNK is mandatory but let's make sure we don't try to
+	 * have VBR on remote which for some reason does not support it
+	 */
+	if (!cap->vbr && config->vbr) {
+		error("AAC: Unsupported VBR (%u) by endpoint",
+							config->vbr);
+		return -EINVAL;
+	}
+
+	if (AAC_GET_BITRATE(*cap) < AAC_GET_BITRATE(*config))
+		return -ERANGE;
+
+	return 0;
+}
+
+static int aptx_check_config(void *caps, uint8_t caps_len, void *conf,
+							uint8_t conf_len)
+{
+	a2dp_aptx_t *cap, *config;
+
+	if (conf_len != caps_len || conf_len != sizeof(a2dp_aptx_t)) {
+		error("APTX: Invalid configuration size (%u)", conf_len);
+		return -EINVAL;
+	}
+
+	cap = caps;
+	config = conf;
+
+	if (!(cap->frequency & config->frequency)) {
+		error("APTX: Unsupported frequenct (%u) by endpoint",
+							config->frequency);
+		return -EINVAL;
+	}
+
+	if (!(cap->channel_mode & config->channel_mode)) {
+		error("APTX: Unsupported channel mode (%u) by endpoint",
+							config->channel_mode);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int check_capabilities(struct a2dp_preset *preset,
+				struct avdtp_media_codec_capability *codec,
+				uint8_t codec_len)
+{
+	a2dp_vendor_codec_t *vndcodec;
+
+	/* Codec specific */
+	switch (codec->media_codec_type) {
+	case A2DP_CODEC_SBC:
+		return sbc_check_config(codec->data, codec_len, preset->data,
+								preset->len);
+	case A2DP_CODEC_MPEG24:
+		return aac_check_config(codec->data, codec_len, preset->data,
+								preset->len);
+	case A2DP_CODEC_VENDOR:
+		vndcodec = (void *) codec->data;
+		if (btohl(vndcodec->vendor_id) == APTX_VENDOR_ID &&
+				btohs(vndcodec->codec_id) == APTX_CODEC_ID)
+			return aptx_check_config(codec->data, codec_len,
+						preset->data, preset->len);
+		return -EINVAL;
+	default:
+		return -EINVAL;
+	}
+}
+
+static struct a2dp_preset *sbc_select_range(void *caps, uint8_t caps_len,
+						void *conf, uint8_t conf_len)
+{
+	struct a2dp_preset *p;
+	a2dp_sbc_t *cap, *config;
+
+	cap = caps;
+	config = conf;
+
+	config->min_bitpool = MAX(config->min_bitpool, cap->min_bitpool);
+	config->max_bitpool = MIN(config->max_bitpool, cap->max_bitpool);
+
+	p = g_new0(struct a2dp_preset, 1);
+	p->len = conf_len;
+	p->data = g_memdup(conf, p->len);
+
+	return p;
+}
+
+static struct a2dp_preset *aac_select_range(void *caps, uint8_t caps_len,
+						void *conf, uint8_t conf_len)
+{
+	struct a2dp_preset *p;
+	a2dp_aac_t *cap, *config;
+	uint32_t bitrate;
+
+	cap = caps;
+	config = conf;
+
+	bitrate = MIN(AAC_GET_BITRATE(*cap), AAC_GET_BITRATE(*config));
+	AAC_SET_BITRATE(*config, bitrate);
+
+	p = g_new0(struct a2dp_preset, 1);
+	p->len = conf_len;
+	p->data = g_memdup(conf, p->len);
+
+	return p;
+}
+
+static struct a2dp_preset *select_preset_range(struct a2dp_preset *preset,
+				struct avdtp_media_codec_capability *codec,
+				uint8_t codec_len)
+{
+	/* Codec specific */
+	switch (codec->media_codec_type) {
+	case A2DP_CODEC_SBC:
+		return sbc_select_range(codec->data, codec_len, preset->data,
+								preset->len);
+	case A2DP_CODEC_MPEG24:
+		return aac_select_range(codec->data, codec_len, preset->data,
+								preset->len);
+	default:
+		return NULL;
+	}
+}
+
+static struct a2dp_preset *select_preset(struct a2dp_endpoint *endpoint,
+						struct avdtp_remote_sep *rsep)
+{
+	struct avdtp_service_capability *service;
+	struct avdtp_media_codec_capability *codec;
+	GSList *l;
+	uint8_t codec_len;
+
+	service = avdtp_get_codec(rsep);
+	codec = (struct avdtp_media_codec_capability *) service->data;
+	codec_len = service->length - sizeof(*codec);
+
+	for (l = endpoint->presets; l; l = g_slist_next(l)) {
+		struct a2dp_preset *preset = l->data;
+		int err;
+
+		err = check_capabilities(preset, codec, codec_len);
+		if (err == 0)
+			return preset;
+
+		if (err == -ERANGE)
+			return select_preset_range(preset, codec, codec_len);
+	}
+
+	return NULL;
+}
+
+static void setup_add(struct a2dp_device *dev, struct a2dp_endpoint *endpoint,
+			struct a2dp_preset *preset, struct avdtp_stream *stream)
+{
+	struct a2dp_setup *setup;
+
+	setup = g_new0(struct a2dp_setup, 1);
+	setup->dev = dev;
+	setup->endpoint = endpoint;
+	setup->preset = preset;
+	setup->stream = stream;
+	setups = g_slist_append(setups, setup);
+
+	if (dev->idle_id > 0) {
+		g_source_remove(dev->idle_id);
+		dev->idle_id = 0;
+	}
+}
+
+static int select_configuration(struct a2dp_device *dev,
+				struct a2dp_endpoint *endpoint,
+				struct avdtp_remote_sep *rsep)
+{
+	struct a2dp_preset *preset;
+	struct avdtp_stream *stream;
+	struct avdtp_service_capability *service;
+	struct avdtp_media_codec_capability *codec;
+	GSList *caps;
+	int err;
+
+	preset = select_preset(endpoint, rsep);
+	if (!preset) {
+		error("Unable to select codec preset");
+		return -EINVAL;
+	}
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
+	caps = g_slist_append(NULL, service);
+
+	codec = g_malloc0(sizeof(*codec) + preset->len);
+	codec->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+	codec->media_codec_type = endpoint->codec;
+	memcpy(codec->data, preset->data, preset->len);
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec,
+						sizeof(*codec) + preset->len);
+	caps = g_slist_append(caps, service);
+
+	g_free(codec);
+
+	err = avdtp_set_configuration(dev->session, rsep, endpoint->sep, caps,
+								&stream);
+	g_slist_free_full(caps, g_free);
+	if (err < 0) {
+		error("avdtp_set_configuration: %s", strerror(-err));
+		return err;
+	}
+
+	setup_add(dev, endpoint, preset, stream);
+
+	return 0;
+}
+
+static void discover_cb(struct avdtp *session, GSList *seps,
+				struct avdtp_error *err, void *user_data)
+{
+	struct a2dp_device *dev = user_data;
+	struct a2dp_endpoint *endpoint = NULL;
+	struct avdtp_remote_sep *rsep = NULL;
+	GSList *l;
+
+	for (l = endpoints; l; l = g_slist_next(l)) {
+		endpoint = l->data;
+
+		rsep = avdtp_find_remote_sep(session, endpoint->sep);
+		if (rsep)
+			break;
+	}
+
+	if (!rsep) {
+		error("Unable to find matching endpoint");
+		goto failed;
+	}
+
+	if (select_configuration(dev, endpoint, rsep) < 0)
+		goto failed;
+
+	return;
+
+failed:
+	avdtp_shutdown(session);
+}
+
+static gboolean idle_timeout(gpointer user_data)
+{
+	struct a2dp_device *dev = user_data;
+	int err;
+
+	dev->idle_id = 0;
+
+	err = avdtp_discover(dev->session, discover_cb, dev);
+	if (err == 0)
+		return FALSE;
+
+	error("avdtp_discover: %s", strerror(-err));
+	bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+
+	return FALSE;
+}
+
+static void signaling_connect_cb(GIOChannel *chan, GError *err,
+							gpointer user_data)
+{
+	struct a2dp_device *dev = user_data;
+	struct avdtp *session;
+	uint16_t imtu, omtu;
+	GError *gerr = NULL;
+	int fd;
+
+	if (err) {
+		bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+		error("%s", err->message);
+		return;
+	}
+
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_IMTU, &imtu,
+			BT_IO_OPT_OMTU, &omtu,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		goto failed;
+	}
+
+	fd = g_io_channel_unix_get_fd(chan);
+
+	/* FIXME: Add proper version */
+	session = avdtp_new(fd, imtu, omtu, 0x0100, lseps);
+	if (!session)
+		goto failed;
+
+	dev->session = session;
+
+	avdtp_add_disconnect_cb(dev->session, disconnect_cb, dev);
+
+	/* Proceed to stream setup if initiator */
+	if (dev->io) {
+		int perr;
+
+		g_io_channel_unref(dev->io);
+		dev->io = NULL;
+
+		perr = avdtp_discover(dev->session, discover_cb, dev);
+		if (perr < 0) {
+			error("avdtp_discover: %s", strerror(-perr));
+			goto failed;
+		}
+		bt_avrcp_connect(&dev->dst);
+	} else /* Init idle timeout to discover */
+		dev->idle_id = g_timeout_add_seconds(IDLE_TIMEOUT, idle_timeout,
+									dev);
+
+	return;
+
+failed:
+	bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+}
+
+static void bt_a2dp_connect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_a2dp_connect *cmd = buf;
+	struct a2dp_device *dev;
+	uint8_t status;
+	char addr[18];
+	bdaddr_t dst;
+	GSList *l;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = a2dp_device_new(&dst);
+	if (!a2dp_device_connect(dev, signaling_connect_cb)) {
+		a2dp_device_remove(dev);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ba2str(&dev->dst, addr);
+	DBG("connecting to %s", addr);
+
+	bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, status);
+}
+
+static void bt_a2dp_disconnect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_a2dp_connect *cmd = buf;
+	uint8_t status;
+	struct a2dp_device *dev;
+	GSList *l;
+	bdaddr_t dst;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+	status = HAL_STATUS_SUCCESS;
+
+	if (dev->io) {
+		bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+		goto failed;
+	}
+
+	/* Wait AVDTP session to shutdown */
+	avdtp_shutdown(dev->session);
+	bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTING);
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT,
+									status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_A2DP_CONNECT */
+	{ bt_a2dp_connect, false, sizeof(struct hal_cmd_a2dp_connect) },
+	/* HAL_OP_A2DP_DISCONNECT */
+	{ bt_a2dp_disconnect, false, sizeof(struct hal_cmd_a2dp_disconnect) },
+};
+
+static struct a2dp_setup *find_setup_by_device(struct a2dp_device *dev)
+{
+	GSList *l;
+
+	for (l = setups; l; l = g_slist_next(l)) {
+		struct a2dp_setup *setup = l->data;
+
+		if (setup->dev == dev)
+			return setup;
+	}
+
+	return NULL;
+}
+
+static void transport_connect_cb(GIOChannel *chan, GError *err,
+							gpointer user_data)
+{
+	struct a2dp_device *dev = user_data;
+	struct a2dp_setup *setup;
+	uint16_t imtu, omtu;
+	GError *gerr = NULL;
+	int fd;
+
+	if (err) {
+		error("%s", err->message);
+		return;
+	}
+
+	setup = find_setup_by_device(dev);
+	if (!setup) {
+		error("Unable to find stream setup");
+		return;
+	}
+
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_IMTU, &imtu,
+			BT_IO_OPT_OMTU, &omtu,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		return;
+	}
+
+	fd = g_io_channel_unix_get_fd(chan);
+
+	if (!avdtp_stream_set_transport(setup->stream, fd, imtu, omtu)) {
+		error("avdtp_stream_set_transport: failed");
+		return;
+	}
+
+	g_io_channel_set_close_on_unref(chan, FALSE);
+
+	if (dev->io) {
+		g_io_channel_unref(dev->io);
+		dev->io = NULL;
+	}
+
+	bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTED);
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct a2dp_device *dev;
+	bdaddr_t dst;
+	char address[18];
+	GError *gerr = NULL;
+	GSList *l;
+
+	if (err) {
+		error("%s", err->message);
+		return;
+	}
+
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	ba2str(&dst, address);
+	DBG("Incoming connection from %s", address);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (l) {
+		transport_connect_cb(chan, err, l->data);
+		return;
+	}
+
+	dev = a2dp_device_new(&dst);
+	bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING);
+	signaling_connect_cb(chan, err, dev);
+}
+
+static sdp_record_t *a2dp_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t *record;
+	sdp_data_t *psm, *version, *features;
+	uint16_t lp = AVDTP_UUID;
+	uint16_t a2dp_ver = 0x0103, avdtp_ver = 0x0103, feat = 0x000f;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &a2dp_uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+	profile[0].version = a2dp_ver;
+	pfseq = sdp_list_append(NULL, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&avdtp_uuid, AVDTP_UUID);
+	proto[1] = sdp_list_append(NULL, &avdtp_uuid);
+	version = sdp_data_alloc(SDP_UINT16, &avdtp_ver);
+	proto[1] = sdp_list_append(proto[1], version);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	features = sdp_data_alloc(SDP_UINT16, &feat);
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	sdp_set_info_attr(record, "Audio Source", NULL, NULL);
+
+	sdp_data_free(psm);
+	sdp_data_free(version);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static gboolean sep_getcap_ind(struct avdtp *session,
+					struct avdtp_local_sep *sep,
+					GSList **caps, uint8_t *err,
+					void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_preset *cap = endpoint->caps;
+	struct avdtp_service_capability *service;
+	struct avdtp_media_codec_capability *codec;
+
+	*caps = NULL;
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
+	*caps = g_slist_append(*caps, service);
+
+	codec = g_malloc0(sizeof(*codec) + cap->len);
+	codec->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+	codec->media_codec_type = endpoint->codec;
+	memcpy(codec->data, cap->data, cap->len);
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec,
+						sizeof(*codec) + cap->len);
+	*caps = g_slist_append(*caps, service);
+	g_free(codec);
+
+	return TRUE;
+}
+
+static int check_config(struct a2dp_endpoint *endpoint,
+						struct a2dp_preset *config)
+{
+	GSList *l;
+	struct a2dp_preset *caps;
+
+	for (l = endpoint->presets; l; l = g_slist_next(l)) {
+		struct a2dp_preset *preset = l->data;
+
+		if (preset->len != config->len)
+			continue;
+
+		if (memcmp(preset->data, config->data, preset->len) == 0)
+			return 0;
+	}
+
+	caps = endpoint->caps;
+
+	/* Codec specific */
+	switch (endpoint->codec) {
+	case A2DP_CODEC_SBC:
+		return sbc_check_config(caps->data, caps->len, config->data,
+								config->len);
+	default:
+		return -EINVAL;
+	}
+}
+
+static struct a2dp_device *find_device_by_session(struct avdtp *session)
+{
+	GSList *l;
+
+	for (l = devices; l; l = g_slist_next(l)) {
+		struct a2dp_device *dev = l->data;
+
+		if (dev->session == session)
+			return dev;
+	}
+
+	return NULL;
+}
+
+static struct a2dp_setup *find_setup(uint8_t id)
+{
+	GSList *l;
+
+	for (l = setups; l; l = g_slist_next(l)) {
+		struct a2dp_setup *setup = l->data;
+
+		if (setup->endpoint->id == id)
+			return setup;
+	}
+
+	return NULL;
+}
+
+static void setup_remove_by_id(uint8_t id)
+{
+	struct a2dp_setup *setup;
+
+	setup = find_setup(id);
+	if (!setup) {
+		error("Unable to find stream setup for endpoint %u", id);
+		return;
+	}
+
+	setup_remove(setup);
+}
+
+static gboolean sep_setconf_ind(struct avdtp *session,
+						struct avdtp_local_sep *sep,
+						struct avdtp_stream *stream,
+						GSList *caps,
+						avdtp_set_configuration_cb cb,
+						void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_device *dev;
+	struct a2dp_preset *preset = NULL;
+
+	DBG("");
+
+	dev = find_device_by_session(session);
+	if (!dev) {
+		error("Unable to find device for session %p", session);
+		return FALSE;
+	}
+
+	for (; caps != NULL; caps = g_slist_next(caps)) {
+		struct avdtp_service_capability *cap = caps->data;
+		struct avdtp_media_codec_capability *codec;
+
+		if (cap->category == AVDTP_DELAY_REPORTING)
+			return FALSE;
+
+		if (cap->category != AVDTP_MEDIA_CODEC)
+			continue;
+
+		codec = (struct avdtp_media_codec_capability *) cap->data;
+
+		if (codec->media_codec_type != endpoint->codec)
+			return FALSE;
+
+		preset = g_new0(struct a2dp_preset, 1);
+		preset->len = cap->length - sizeof(*codec);
+		preset->data = g_memdup(codec->data, preset->len);
+
+		if (check_config(endpoint, preset) < 0) {
+			preset_free(preset);
+			return FALSE;
+		}
+	}
+
+	if (!preset)
+		return FALSE;
+
+	setup_add(dev, endpoint, preset, stream);
+
+	cb(session, stream, NULL);
+
+	return TRUE;
+}
+
+static gboolean sep_open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+
+	DBG("");
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for endpoint %u",
+								endpoint->id);
+		*err = AVDTP_SEP_NOT_IN_USE;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean sep_close_ind(struct avdtp *session,
+						struct avdtp_local_sep *sep,
+						struct avdtp_stream *stream,
+						uint8_t *err,
+						void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+
+	DBG("");
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for endpoint %u",
+								endpoint->id);
+		*err = AVDTP_SEP_NOT_IN_USE;
+		return FALSE;
+	}
+
+	bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
+
+	setup_remove(setup);
+
+	return TRUE;
+}
+
+static gboolean sep_start_ind(struct avdtp *session,
+						struct avdtp_local_sep *sep,
+						struct avdtp_stream *stream,
+						uint8_t *err,
+						void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+
+	DBG("");
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for endpoint %u",
+								endpoint->id);
+		*err = AVDTP_SEP_NOT_IN_USE;
+		return FALSE;
+	}
+
+	bt_audio_notify_state(setup, HAL_AUDIO_STARTED);
+
+	return TRUE;
+}
+
+static gboolean sep_suspend_ind(struct avdtp *session,
+						struct avdtp_local_sep *sep,
+						struct avdtp_stream *stream,
+						uint8_t *err,
+						void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+
+	DBG("");
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for endpoint %u",
+								endpoint->id);
+		*err = AVDTP_SEP_NOT_IN_USE;
+		return FALSE;
+	}
+
+	bt_audio_notify_state(setup, HAL_AUDIO_SUSPEND);
+
+	return TRUE;
+}
+
+static struct avdtp_sep_ind sep_ind = {
+	.get_capability		= sep_getcap_ind,
+	.set_configuration	= sep_setconf_ind,
+	.open			= sep_open_ind,
+	.close			= sep_close_ind,
+	.start			= sep_start_ind,
+	.suspend		= sep_suspend_ind,
+};
+
+static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+	int ret;
+
+	DBG("");
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for endpoint %u",
+								endpoint->id);
+		return;
+	}
+
+	if (err)
+		goto failed;
+
+	ret = avdtp_open(session, stream);
+	if (ret < 0) {
+		error("avdtp_open: %s", strerror(-ret));
+		goto failed;
+	}
+
+	return;
+
+failed:
+	setup_remove(setup);
+}
+
+static void sep_open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_device *dev;
+
+	DBG("");
+
+	if (err)
+		goto failed;
+
+	dev = find_device_by_session(session);
+	if (!dev) {
+		error("Unable to find device for session");
+		goto failed;
+	}
+
+	a2dp_device_connect(dev, transport_connect_cb);
+
+	return;
+
+failed:
+	setup_remove_by_id(endpoint->id);
+}
+
+static void sep_start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+
+	DBG("");
+
+	if (err) {
+		setup_remove_by_id(endpoint->id);
+		return;
+	}
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for %u endpoint",
+								endpoint->id);
+		return;
+	}
+
+	bt_audio_notify_state(setup, HAL_AUDIO_STARTED);
+}
+
+static void sep_suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+
+	DBG("");
+
+	if (err) {
+		setup_remove_by_id(endpoint->id);
+		return;
+	}
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for %u endpoint",
+								endpoint->id);
+		return;
+	}
+
+	bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
+}
+
+static void sep_close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+
+	DBG("");
+
+	if (err)
+		return;
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for %u endpoint",
+								endpoint->id);
+		return;
+	}
+
+	bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
+
+	setup_remove(setup);
+}
+
+static void sep_abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+
+	DBG("");
+
+	if (err)
+		return;
+
+	setup_remove_by_id(endpoint->id);
+}
+
+static struct avdtp_sep_cfm sep_cfm = {
+	.set_configuration	= sep_setconf_cfm,
+	.open			= sep_open_cfm,
+	.start			= sep_start_cfm,
+	.suspend		= sep_suspend_cfm,
+	.close			= sep_close_cfm,
+	.abort			= sep_abort_cfm,
+};
+
+static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec,
+							GSList *presets)
+{
+	struct a2dp_endpoint *endpoint;
+
+	/* FIXME: Add proper check for uuid */
+
+	endpoint = g_new0(struct a2dp_endpoint, 1);
+	endpoint->id = g_slist_length(endpoints) + 1;
+	endpoint->codec = codec;
+	endpoint->sep = avdtp_register_sep(lseps, AVDTP_SEP_TYPE_SOURCE,
+						AVDTP_MEDIA_TYPE_AUDIO,
+						codec, FALSE, &sep_ind,
+						&sep_cfm, endpoint);
+	endpoint->caps = presets->data;
+	endpoint->presets = g_slist_copy(g_slist_nth(presets, 1));
+
+	if (endpoint->codec == A2DP_CODEC_VENDOR) {
+		a2dp_vendor_codec_t *vndcodec = (void *) endpoint->caps->data;
+
+		avdtp_sep_set_vendor_codec(endpoint->sep,
+						btohl(vndcodec->vendor_id),
+						btohs(vndcodec->codec_id));
+	}
+
+	endpoints = g_slist_append(endpoints, endpoint);
+
+	return endpoint->id;
+}
+
+static GSList *parse_presets(const struct audio_preset *p, uint8_t count,
+								uint16_t len)
+{
+	GSList *l = NULL;
+	uint8_t i;
+
+	for (i = 0; count > i; i++) {
+		const uint8_t *ptr = (const uint8_t *) p;
+		struct a2dp_preset *preset;
+
+		if (len < sizeof(struct audio_preset)) {
+			DBG("Invalid preset index %u", i);
+			g_slist_free_full(l, preset_free);
+			return NULL;
+		}
+
+		len -= sizeof(struct audio_preset);
+		if (len == 0 || len < p->len) {
+			DBG("Invalid preset size of %u for index %u", len, i);
+			g_slist_free_full(l, preset_free);
+			return NULL;
+		}
+
+		preset = g_new0(struct a2dp_preset, 1);
+		preset->len = p->len;
+		preset->data = g_memdup(p->data, preset->len);
+		l = g_slist_append(l, preset);
+
+		len -= preset->len;
+		ptr += sizeof(*p) + preset->len;
+		p = (const struct audio_preset *) ptr;
+	}
+
+	return l;
+}
+
+static void bt_audio_open(const void *buf, uint16_t len)
+{
+	const struct audio_cmd_open *cmd = buf;
+	struct audio_rsp_open rsp;
+	GSList *presets;
+
+	DBG("");
+
+	audio_retrying = false;
+
+	if (cmd->presets == 0) {
+		error("No audio presets found");
+		goto failed;
+	}
+
+	presets = parse_presets(cmd->preset, cmd->presets, len - sizeof(*cmd));
+	if (!presets) {
+		error("No audio presets found");
+		goto failed;
+	}
+
+	rsp.id = register_endpoint(cmd->uuid, cmd->codec, presets);
+	if (rsp.id == 0) {
+		g_slist_free_full(presets, preset_free);
+		error("Unable to register endpoint");
+		goto failed;
+	}
+
+	g_slist_free(presets);
+
+	ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN,
+							sizeof(rsp), &rsp, -1);
+
+	return;
+
+failed:
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN,
+							AUDIO_STATUS_FAILED);
+}
+
+static struct a2dp_endpoint *find_endpoint(uint8_t id)
+{
+	GSList *l;
+
+	for (l = endpoints; l; l = g_slist_next(l)) {
+		struct a2dp_endpoint *endpoint = l->data;
+
+		if (endpoint->id == id)
+			return endpoint;
+	}
+
+	return NULL;
+}
+
+static void bt_audio_close(const void *buf, uint16_t len)
+{
+	const struct audio_cmd_close *cmd = buf;
+	struct a2dp_endpoint *endpoint;
+
+	DBG("");
+
+	endpoint = find_endpoint(cmd->id);
+	if (!endpoint) {
+		error("Unable to find endpoint %u", cmd->id);
+		ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE,
+							AUDIO_STATUS_FAILED);
+		return;
+	}
+
+	endpoints = g_slist_remove(endpoints, endpoint);
+	unregister_endpoint(endpoint);
+
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE,
+							AUDIO_STATUS_SUCCESS);
+}
+
+static void bt_stream_open(const void *buf, uint16_t len)
+{
+	const struct audio_cmd_open_stream *cmd = buf;
+	struct audio_rsp_open_stream *rsp;
+	struct a2dp_setup *setup;
+	int fd;
+	uint16_t omtu;
+
+	DBG("");
+
+	if (cmd->id)
+		setup = find_setup(cmd->id);
+	else
+		setup = setups ? setups->data : NULL;
+	if (!setup) {
+		error("Unable to find stream for endpoint %u", cmd->id);
+		ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+							AUDIO_STATUS_FAILED);
+		return;
+	}
+
+	if (!avdtp_stream_get_transport(setup->stream, &fd, NULL, &omtu,
+								NULL)) {
+		error("avdtp_stream_get_transport: failed");
+		ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+							AUDIO_STATUS_FAILED);
+		return;
+	}
+
+	len = sizeof(struct audio_rsp_open_stream) +
+			sizeof(struct audio_preset) + setup->preset->len;
+	rsp = g_malloc0(len);
+	rsp->id = setup->endpoint->id;
+	rsp->mtu = omtu;
+	rsp->preset->len = setup->preset->len;
+	memcpy(rsp->preset->data, setup->preset->data, setup->preset->len);
+
+	ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+								len, rsp, fd);
+
+	g_free(rsp);
+}
+
+static void bt_stream_close(const void *buf, uint16_t len)
+{
+	const struct audio_cmd_close_stream *cmd = buf;
+	struct a2dp_setup *setup;
+	int err;
+
+	DBG("");
+
+	setup = find_setup(cmd->id);
+	if (!setup) {
+		error("Unable to find stream for endpoint %u", cmd->id);
+		goto failed;
+	}
+
+	err = avdtp_close(setup->dev->session, setup->stream, FALSE);
+	if (err < 0) {
+		error("avdtp_close: %s", strerror(-err));
+		goto failed;
+	}
+
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM,
+							AUDIO_STATUS_SUCCESS);
+
+	return;
+
+failed:
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM,
+							AUDIO_STATUS_FAILED);
+}
+
+static void bt_stream_resume(const void *buf, uint16_t len)
+{
+	const struct audio_cmd_resume_stream *cmd = buf;
+	struct a2dp_setup *setup;
+	int err;
+
+	DBG("");
+
+	setup = find_setup(cmd->id);
+	if (!setup) {
+		error("Unable to find stream for endpoint %u", cmd->id);
+		goto failed;
+	}
+
+	if (setup->state != HAL_AUDIO_STARTED) {
+		err = avdtp_start(setup->dev->session, setup->stream);
+		if (err < 0) {
+			error("avdtp_start: %s", strerror(-err));
+			goto failed;
+		}
+	}
+
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM,
+							AUDIO_STATUS_SUCCESS);
+
+	return;
+
+failed:
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM,
+							AUDIO_STATUS_FAILED);
+}
+
+static void bt_stream_suspend(const void *buf, uint16_t len)
+{
+	const struct audio_cmd_suspend_stream *cmd = buf;
+	struct a2dp_setup *setup;
+	int err;
+
+	DBG("");
+
+	setup = find_setup(cmd->id);
+	if (!setup) {
+		error("Unable to find stream for endpoint %u", cmd->id);
+		goto failed;
+	}
+
+	err = avdtp_suspend(setup->dev->session, setup->stream);
+	if (err < 0) {
+		error("avdtp_suspend: %s", strerror(-err));
+		goto failed;
+	}
+
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM,
+							AUDIO_STATUS_SUCCESS);
+
+	return;
+
+failed:
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM,
+							AUDIO_STATUS_FAILED);
+}
+
+static const struct ipc_handler audio_handlers[] = {
+	/* AUDIO_OP_OPEN */
+	{ bt_audio_open, true, sizeof(struct audio_cmd_open) },
+	/* AUDIO_OP_CLOSE */
+	{ bt_audio_close, false, sizeof(struct audio_cmd_close) },
+	/* AUDIO_OP_OPEN_STREAM */
+	{ bt_stream_open, false, sizeof(struct audio_cmd_open_stream) },
+	/* AUDIO_OP_CLOSE_STREAM */
+	{ bt_stream_close, false, sizeof(struct audio_cmd_close_stream) },
+	/* AUDIO_OP_RESUME_STREAM */
+	{ bt_stream_resume, false, sizeof(struct audio_cmd_resume_stream) },
+	/* AUDIO_OP_SUSPEND_STREAM */
+	{ bt_stream_suspend, false, sizeof(struct audio_cmd_suspend_stream) },
+};
+
+static void bt_audio_unregister(void)
+{
+	DBG("");
+
+	if (audio_retry_id > 0)
+		g_source_remove(audio_retry_id);
+
+	g_slist_free_full(endpoints, unregister_endpoint);
+	endpoints = NULL;
+
+	g_slist_free_full(setups, setup_free);
+	setups = NULL;
+
+	ipc_cleanup(audio_ipc);
+	audio_ipc = NULL;
+
+	queue_destroy(lseps, NULL);
+}
+
+static bool bt_audio_register(ipc_disconnect_cb disconnect)
+{
+	DBG("");
+
+	audio_ipc = ipc_init(BLUEZ_AUDIO_SK_PATH, sizeof(BLUEZ_AUDIO_SK_PATH),
+				AUDIO_SERVICE_ID_MAX, false, disconnect, NULL);
+	if (!audio_ipc)
+		return false;
+
+	ipc_register(audio_ipc, AUDIO_SERVICE_ID, audio_handlers,
+						G_N_ELEMENTS(audio_handlers));
+
+	return true;
+}
+
+static gboolean audio_retry_register(void *data)
+{
+	ipc_disconnect_cb cb = data;
+
+	audio_retry_id = 0;
+	audio_retrying = true;
+
+	bt_audio_register(cb);
+
+	return FALSE;
+}
+
+static void audio_disconnected(void *data)
+{
+	GSList *l;
+	bool restart;
+
+	DBG("");
+
+	if (audio_retrying)
+		goto retry;
+
+	restart = endpoints != NULL ? true : false;
+
+	bt_audio_unregister();
+
+	for (l = devices; l; l = g_slist_next(l)) {
+		struct a2dp_device *dev = l->data;
+
+		avdtp_shutdown(dev->session);
+	}
+
+	if (!restart)
+		return;
+
+retry:
+	audio_retry_id = g_timeout_add_seconds(AUDIO_RETRY_TIMEOUT,
+						audio_retry_register,
+						audio_disconnected);
+}
+
+bool bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	GError *err = NULL;
+	sdp_record_t *rec;
+
+	DBG("");
+
+	bacpy(&adapter_addr, addr);
+
+	lseps = queue_new();
+
+	server = bt_io_listen(connect_cb, NULL, NULL, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_PSM, AVDTP_PSM,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_MASTER, true,
+				BT_IO_OPT_INVALID);
+	if (!server) {
+		error("Failed to listen on AVDTP channel: %s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	rec = a2dp_record();
+	if (!rec) {
+		error("Failed to allocate A2DP record");
+		goto fail;
+	}
+
+	if (bt_adapter_add_record(rec, SVC_HINT_CAPTURING) < 0) {
+		error("Failed to register A2DP record");
+		sdp_record_free(rec);
+		goto fail;
+	}
+	record_id = rec->handle;
+
+	hal_ipc = ipc;
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_A2DP, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	if (bt_audio_register(audio_disconnected))
+		return true;
+
+fail:
+	g_io_channel_shutdown(server, TRUE, NULL);
+	g_io_channel_unref(server);
+	server = NULL;
+	return false;
+}
+
+void bt_a2dp_unregister(void)
+{
+	DBG("");
+
+	g_slist_free_full(setups, setup_free);
+	setups = NULL;
+
+	g_slist_free_full(endpoints, unregister_endpoint);
+	endpoints = NULL;
+
+	g_slist_free_full(devices, a2dp_device_free);
+	devices = NULL;
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_A2DP);
+	hal_ipc = NULL;
+
+	bt_adapter_remove_record(record_id);
+	record_id = 0;
+
+	if (server) {
+		g_io_channel_shutdown(server, TRUE, NULL);
+		g_io_channel_unref(server);
+		server = NULL;
+	}
+
+	if (audio_ipc) {
+		ipc_unregister(audio_ipc, AUDIO_SERVICE_ID);
+		ipc_cleanup(audio_ipc);
+		audio_ipc = NULL;
+	}
+}
diff --git a/repo/android/a2dp.h b/repo/android/a2dp.h
new file mode 100644
index 0000000..8a70407
--- /dev/null
+++ b/repo/android/a2dp.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+bool bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_a2dp_unregister(void);
diff --git a/repo/android/audio-ipc-api.txt b/repo/android/audio-ipc-api.txt
new file mode 100644
index 0000000..f4a497d
--- /dev/null
+++ b/repo/android/audio-ipc-api.txt
@@ -0,0 +1,87 @@
+Bluetooth Audio Plugin
+======================
+
+The audio plugin happen to be in a different socket but all the rules for
+HAL socket apply here as well, the abstract socket name is
+"\0bluez_audio_socket" (tentative):
+
+	.---Audio---.                             .--Android--.
+	|  Plugin   |                             |   Daemon  |
+	|           |          Command            |           |
+	|           | --------------------------> |           |
+	|           |                             |           |
+	|           | <-------------------------- |           |
+	|           |          Response           |           |
+	|           |                             |           |
+	|           |                             |           |
+	|           |                             |           |
+	'-----------'                             '-----------'
+
+
+	Audio HAL                               Daemon
+	----------------------------------------------------
+
+	call dev->open()                    --> command 0x01
+	return dev->open()                  <-- response 0x01
+
+	call dev->open_output_stream()      --> command 0x03
+	return dev->open_output_stream()    <-- response 0x03
+
+	call stream->write()                --> command 0x05
+	return stream->write()              <-- response 0x05
+
+	call stream->common.standby()       --> command 0x06
+	return stream->common.standby()     <-- response 0x06
+
+	call dev->close_output_stream()     --> command 0x04
+	return dev->close_output_stream()   <-- response 0x04
+
+	call dev->close()                   --> command 0x02
+	return dev->close()                 <-- response 0x02
+
+Audio Service (ID 0)
+====================
+
+	Opcode 0x00 - Error response
+
+		Response parameters: Status (1 octet)
+
+	Opcode 0x01 - Open Audio Endpoint commmand
+
+		Command parameters: Service UUID (16 octets)
+				    Codec ID (1 octet)
+				    Number of codec presets (1 octet)
+				    Codec capabilities length (1 octet)
+				    Codec capabilities (variable)
+				    Codec preset # length (1 octet)
+				    Codec preset # configuration (variable)
+				    ...
+		Response parameters: Endpoint ID (1 octet)
+
+	Opcode 0x02 - Close Audio Endpoint command
+
+		Command parameters: Endpoint ID (1 octet)
+		Response parameters: <none>
+
+	Opcode 0x03 - Open Stream command
+
+		Command parameters: Endpoint ID (1 octet)
+		Response parameters: Outgoing MTU (2 octets)
+				     Codec configuration length (1 octet)
+				     Codec configuration (1 octet)
+				     File descriptor (inline)
+
+	Opcode 0x04 - Close Stream command
+
+		Command parameters: Endpoint ID (1 octet)
+		Response parameters: <none>
+
+	Opcode 0x05 - Resume Stream command
+
+		Command parameters: Endpoint ID (1 octet)
+		Response parameters: <none>
+
+	Opcode 0x06 - Suspend Stream command
+
+		Command parameters: Endpoint ID (1 octet)
+		Response parameters: <none>
diff --git a/repo/android/audio-msg.h b/repo/android/audio-msg.h
new file mode 100644
index 0000000..7b9553b
--- /dev/null
+++ b/repo/android/audio-msg.h
@@ -0,0 +1,82 @@
+/*
+ *
+ *  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
+ *
+ */
+
+#define BLUEZ_AUDIO_MTU 1024
+
+static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket";
+
+#define AUDIO_SERVICE_ID		0
+#define AUDIO_SERVICE_ID_MAX		AUDIO_SERVICE_ID
+
+#define AUDIO_STATUS_SUCCESS		IPC_STATUS_SUCCESS
+#define AUDIO_STATUS_FAILED		0x01
+
+#define AUDIO_OP_STATUS			IPC_OP_STATUS
+
+#define AUDIO_OP_OPEN			0x01
+struct audio_preset {
+	uint8_t len;
+	uint8_t data[0];
+} __attribute__((packed));
+
+struct audio_cmd_open {
+	uint8_t uuid[16];
+	uint8_t codec;
+	uint8_t presets;
+	struct audio_preset preset[0];
+} __attribute__((packed));
+
+struct audio_rsp_open {
+	uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_CLOSE			0x02
+struct audio_cmd_close {
+	uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_OPEN_STREAM		0x03
+struct audio_cmd_open_stream {
+	uint8_t id;
+} __attribute__((packed));
+
+struct audio_rsp_open_stream {
+	uint16_t id;
+	uint16_t mtu;
+	struct audio_preset preset[0];
+} __attribute__((packed));
+
+#define AUDIO_OP_CLOSE_STREAM		0x04
+struct audio_cmd_close_stream {
+	uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_RESUME_STREAM		0x05
+struct audio_cmd_resume_stream {
+	uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_SUSPEND_STREAM		0x06
+struct audio_cmd_suspend_stream {
+	uint8_t id;
+} __attribute__((packed));
diff --git a/repo/android/audio_utils/resampler.c b/repo/android/audio_utils/resampler.c
new file mode 100644
index 0000000..ce30375
--- /dev/null
+++ b/repo/android/audio_utils/resampler.c
@@ -0,0 +1,270 @@
+/*
+** Copyright 2011, The Android Open-Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <system/audio.h>
+#include <audio_utils/resampler.h>
+#include <speex/speex_resampler.h>
+
+#include "hal-log.h"
+
+struct resampler {
+    struct resampler_itfe itfe;
+    SpeexResamplerState *speex_resampler;       // handle on speex resampler
+    struct resampler_buffer_provider *provider; // buffer provider installed by client
+    uint32_t in_sample_rate;                    // input sampling rate in Hz
+    uint32_t out_sample_rate;                   // output sampling rate in Hz
+    uint32_t channel_count;                     // number of channels (interleaved)
+    int16_t *in_buf;                            // input buffer
+    size_t in_buf_size;                         // input buffer size
+    size_t frames_in;                           // number of frames in input buffer
+    size_t frames_rq;                           // cached number of output frames
+    size_t frames_needed;                       // minimum number of input frames to produce
+                                                // frames_rq output frames
+    int32_t speex_delay_ns;                     // delay introduced by speex resampler in ns
+};
+
+
+//------------------------------------------------------------------------------
+// speex based resampler
+//------------------------------------------------------------------------------
+
+static void resampler_reset(struct resampler_itfe *resampler)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    rsmp->frames_in = 0;
+    rsmp->frames_rq = 0;
+
+    if (rsmp != NULL && rsmp->speex_resampler != NULL) {
+        speex_resampler_reset_mem(rsmp->speex_resampler);
+    }
+}
+
+static int32_t resampler_delay_ns(struct resampler_itfe *resampler)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    int32_t delay = (int32_t)((1000000000 * (int64_t)rsmp->frames_in) / rsmp->in_sample_rate);
+    delay += rsmp->speex_delay_ns;
+
+    return delay;
+}
+
+// outputs a number of frames less or equal to *outFrameCount and updates *outFrameCount
+// with the actual number of frames produced.
+static int resampler_resample_from_provider(struct resampler_itfe *resampler,
+                       int16_t *out,
+                       size_t *outFrameCount)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+    size_t framesRq;
+    size_t framesWr;
+    size_t inFrames;
+
+    if (rsmp == NULL || out == NULL || outFrameCount == NULL) {
+        return -EINVAL;
+    }
+    if (rsmp->provider == NULL) {
+        *outFrameCount = 0;
+        return -ENOSYS;
+    }
+
+    framesRq = *outFrameCount;
+    // update and cache the number of frames needed at the input sampling rate to produce
+    // the number of frames requested at the output sampling rate
+    if (framesRq != rsmp->frames_rq) {
+        rsmp->frames_needed = (framesRq * rsmp->in_sample_rate) / rsmp->out_sample_rate + 1;
+        rsmp->frames_rq = framesRq;
+    }
+
+    framesWr = 0;
+    inFrames = 0;
+    while (framesWr < framesRq) {
+        size_t outFrames;
+        if (rsmp->frames_in < rsmp->frames_needed) {
+            struct resampler_buffer buf;
+            // make sure that the number of frames present in rsmp->in_buf (rsmp->frames_in) is at
+            // least the number of frames needed to produce the number of frames requested at
+            // the output sampling rate
+            if (rsmp->in_buf_size < rsmp->frames_needed) {
+                rsmp->in_buf_size = rsmp->frames_needed;
+                rsmp->in_buf = (int16_t *)realloc(rsmp->in_buf,
+                                        rsmp->in_buf_size * rsmp->channel_count * sizeof(int16_t));
+            }
+            buf.frame_count = rsmp->frames_needed - rsmp->frames_in;
+            rsmp->provider->get_next_buffer(rsmp->provider, &buf);
+            if (buf.raw == NULL) {
+                break;
+            }
+            memcpy(rsmp->in_buf + rsmp->frames_in * rsmp->channel_count,
+                    buf.raw,
+                    buf.frame_count * rsmp->channel_count * sizeof(int16_t));
+            rsmp->frames_in += buf.frame_count;
+            rsmp->provider->release_buffer(rsmp->provider, &buf);
+        }
+
+        outFrames = framesRq - framesWr;
+        inFrames = rsmp->frames_in;
+        if (rsmp->channel_count == 1) {
+            speex_resampler_process_int(rsmp->speex_resampler,
+                                        0,
+                                        rsmp->in_buf,
+                                        (void *) &inFrames,
+                                        out + framesWr,
+                                        (void *) &outFrames);
+        } else {
+            speex_resampler_process_interleaved_int(rsmp->speex_resampler,
+                                        rsmp->in_buf,
+                                        (void *) &inFrames,
+                                        out + framesWr * rsmp->channel_count,
+                                        (void *) &outFrames);
+        }
+        framesWr += outFrames;
+        rsmp->frames_in -= inFrames;
+
+        if ((framesWr != framesRq) && (rsmp->frames_in != 0))
+            warn("ReSampler::resample() remaining %zd frames in and %zd out",
+                rsmp->frames_in, (framesRq - framesWr));
+    }
+    if (rsmp->frames_in) {
+        memmove(rsmp->in_buf,
+                rsmp->in_buf + inFrames * rsmp->channel_count,
+                rsmp->frames_in * rsmp->channel_count * sizeof(int16_t));
+    }
+    *outFrameCount = framesWr;
+
+    return 0;
+}
+
+static int resampler_resample_from_input(struct resampler_itfe *resampler,
+                                  int16_t *in,
+                                  size_t *inFrameCount,
+                                  int16_t *out,
+                                  size_t *outFrameCount)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    if (rsmp == NULL || in == NULL || inFrameCount == NULL ||
+            out == NULL || outFrameCount == NULL) {
+        return -EINVAL;
+    }
+    if (rsmp->provider != NULL) {
+        *outFrameCount = 0;
+        return -ENOSYS;
+    }
+
+    if (rsmp->channel_count == 1) {
+        speex_resampler_process_int(rsmp->speex_resampler,
+                                    0,
+                                    in,
+                                    (void *) inFrameCount,
+                                    out,
+                                    (void *) outFrameCount);
+    } else {
+        speex_resampler_process_interleaved_int(rsmp->speex_resampler,
+                                                in,
+                                                (void *) inFrameCount,
+                                                out,
+                                                (void *) outFrameCount);
+    }
+
+    DBG("resampler_resample_from_input() DONE in %zd out %zd", *inFrameCount, *outFrameCount);
+
+    return 0;
+}
+
+int create_resampler(uint32_t inSampleRate,
+                    uint32_t outSampleRate,
+                    uint32_t channelCount,
+                    uint32_t quality,
+                    struct resampler_buffer_provider* provider,
+                    struct resampler_itfe **resampler)
+{
+    int error;
+    struct resampler *rsmp;
+    int frames;
+
+    DBG("create_resampler() In SR %d Out SR %d channels %d",
+         inSampleRate, outSampleRate, channelCount);
+
+    if (resampler == NULL) {
+        return -EINVAL;
+    }
+
+    *resampler = NULL;
+
+    if (quality <= RESAMPLER_QUALITY_MIN || quality >= RESAMPLER_QUALITY_MAX) {
+        return -EINVAL;
+    }
+
+    rsmp = (struct resampler *)calloc(1, sizeof(struct resampler));
+
+    rsmp->speex_resampler = speex_resampler_init(channelCount,
+                                      inSampleRate,
+                                      outSampleRate,
+                                      quality,
+                                      &error);
+    if (rsmp->speex_resampler == NULL) {
+        error("ReSampler: Cannot create speex resampler: %s", speex_resampler_strerror(error));
+        free(rsmp);
+        return -ENODEV;
+    }
+
+    rsmp->itfe.reset = resampler_reset;
+    rsmp->itfe.resample_from_provider = resampler_resample_from_provider;
+    rsmp->itfe.resample_from_input = resampler_resample_from_input;
+    rsmp->itfe.delay_ns = resampler_delay_ns;
+
+    rsmp->provider = provider;
+    rsmp->in_sample_rate = inSampleRate;
+    rsmp->out_sample_rate = outSampleRate;
+    rsmp->channel_count = channelCount;
+    rsmp->in_buf = NULL;
+    rsmp->in_buf_size = 0;
+
+    resampler_reset(&rsmp->itfe);
+
+    frames = speex_resampler_get_input_latency(rsmp->speex_resampler);
+    rsmp->speex_delay_ns = (int32_t)((1000000000 * (int64_t)frames) / rsmp->in_sample_rate);
+    frames = speex_resampler_get_output_latency(rsmp->speex_resampler);
+    rsmp->speex_delay_ns += (int32_t)((1000000000 * (int64_t)frames) / rsmp->out_sample_rate);
+
+    *resampler = &rsmp->itfe;
+    DBG("create_resampler() DONE rsmp %p &rsmp->itfe %p speex %p",
+         rsmp, &rsmp->itfe, rsmp->speex_resampler);
+    return 0;
+}
+
+void release_resampler(struct resampler_itfe *resampler)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    if (rsmp == NULL) {
+        return;
+    }
+
+    free(rsmp->in_buf);
+
+    if (rsmp->speex_resampler != NULL) {
+        speex_resampler_destroy(rsmp->speex_resampler);
+    }
+    free(rsmp);
+}
diff --git a/repo/android/audio_utils/resampler.h b/repo/android/audio_utils/resampler.h
new file mode 100644
index 0000000..0c7046f
--- /dev/null
+++ b/repo/android/audio_utils/resampler.h
@@ -0,0 +1,109 @@
+/*
+** Copyright 2008, The Android Open-Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_RESAMPLER_H
+#define ANDROID_RESAMPLER_H
+
+#include <stdint.h>
+#include <sys/time.h>
+
+__BEGIN_DECLS
+
+
+#define RESAMPLER_QUALITY_MAX 10
+#define RESAMPLER_QUALITY_MIN 0
+#define RESAMPLER_QUALITY_DEFAULT 4
+#define RESAMPLER_QUALITY_VOIP 3
+#define RESAMPLER_QUALITY_DESKTOP 5
+
+struct resampler_buffer {
+    union {
+        void*       raw;
+        short*      i16;
+        int8_t*     i8;
+    };
+    size_t frame_count;
+};
+
+/* call back interface used by the resampler to get new data */
+struct resampler_buffer_provider
+{
+    /**
+     *  get a new buffer of data:
+     *   as input: buffer->frame_count is the number of frames requested
+     *   as output: buffer->frame_count is the number of frames returned
+     *              buffer->raw points to data returned
+     */
+    int (*get_next_buffer)(struct resampler_buffer_provider *provider,
+            struct resampler_buffer *buffer);
+    /**
+     *  release a consumed buffer of data:
+     *   as input: buffer->frame_count is the number of frames released
+     *             buffer->raw points to data released
+     */
+    void (*release_buffer)(struct resampler_buffer_provider *provider,
+            struct resampler_buffer *buffer);
+};
+
+/* resampler interface */
+struct resampler_itfe {
+    /**
+     * reset resampler state
+     */
+    void (*reset)(struct resampler_itfe *resampler);
+    /**
+     * resample input from buffer provider and output at most *outFrameCount to out buffer.
+     * *outFrameCount is updated with the actual number of frames produced.
+     */
+    int (*resample_from_provider)(struct resampler_itfe *resampler,
+                    int16_t *out,
+                    size_t *outFrameCount);
+    /**
+     * resample at most *inFrameCount frames from in buffer and output at most
+     * *outFrameCount to out buffer. *inFrameCount and *outFrameCount are updated respectively
+     * with the number of frames remaining in input and written to output.
+     */
+    int (*resample_from_input)(struct resampler_itfe *resampler,
+                    int16_t *in,
+                    size_t *inFrameCount,
+                    int16_t *out,
+                    size_t *outFrameCount);
+    /**
+     * return the latency introduced by the resampler in ns.
+     */
+    int32_t (*delay_ns)(struct resampler_itfe *resampler);
+};
+
+/**
+ * create a resampler according to input parameters passed.
+ * If resampler_buffer_provider is not NULL only resample_from_provider() can be called.
+ * If resampler_buffer_provider is NULL only resample_from_input() can be called.
+ */
+int create_resampler(uint32_t inSampleRate,
+          uint32_t outSampleRate,
+          uint32_t channelCount,
+          uint32_t quality,
+          struct resampler_buffer_provider *provider,
+          struct resampler_itfe **);
+
+/**
+ * release resampler resources.
+ */
+void release_resampler(struct resampler_itfe *);
+
+__END_DECLS
+
+#endif // ANDROID_RESAMPLER_H
diff --git a/repo/android/avctp.c b/repo/android/avctp.c
new file mode 100644
index 0000000..6aa64cf
--- /dev/null
+++ b/repo/android/avctp.c
@@ -0,0 +1,1653 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  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 <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+
+#include "lib/sdp.h"
+
+#include "src/log.h"
+#include "src/uinput.h"
+
+#include "avctp.h"
+
+/*
+ * AV/C Panel 1.23, page 76:
+ * command with the pressed value is valid for two seconds
+ */
+#define AVC_PRESS_TIMEOUT	2
+
+#define QUIRK_NO_RELEASE 1 << 0
+
+/* Message types */
+#define AVCTP_COMMAND		0
+#define AVCTP_RESPONSE		1
+
+/* Packet types */
+#define AVCTP_PACKET_SINGLE	0
+#define AVCTP_PACKET_START	1
+#define AVCTP_PACKET_CONTINUE	2
+#define AVCTP_PACKET_END	3
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avctp_header {
+	uint8_t ipid:1;
+	uint8_t cr:1;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint16_t pid;
+} __attribute__ ((packed));
+
+struct avc_header {
+	uint8_t code:4;
+	uint8_t _hdr0:4;
+	uint8_t subunit_id:3;
+	uint8_t subunit_type:5;
+	uint8_t opcode;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avctp_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t cr:1;
+	uint8_t ipid:1;
+	uint16_t pid;
+} __attribute__ ((packed));
+
+struct avc_header {
+	uint8_t _hdr0:4;
+	uint8_t code:4;
+	uint8_t subunit_type:5;
+	uint8_t subunit_id:3;
+	uint8_t opcode;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct avctp_control_req {
+	struct avctp_pending_req *p;
+	uint8_t code;
+	uint8_t subunit;
+	uint8_t op;
+	struct iovec *iov;
+	int iov_cnt;
+	avctp_rsp_cb func;
+	void *user_data;
+};
+
+struct avctp_browsing_req {
+	struct avctp_pending_req *p;
+	struct iovec *iov;
+	int iov_cnt;
+	avctp_browsing_rsp_cb func;
+	void *user_data;
+};
+
+typedef int (*avctp_process_cb) (void *data);
+
+struct avctp_pending_req {
+	struct avctp_channel *chan;
+	uint8_t transaction;
+	guint timeout;
+	int err;
+	avctp_process_cb process;
+	void *data;
+	avctp_destroy_cb_t destroy;
+};
+
+struct avctp_channel {
+	struct avctp *session;
+	GIOChannel *io;
+	uint8_t transaction;
+	guint watch;
+	uint16_t imtu;
+	uint16_t omtu;
+	uint8_t *buffer;
+	GSList *handlers;
+	struct avctp_pending_req *p;
+	GQueue *queue;
+	GSList *processed;
+	guint process_id;
+	avctp_destroy_cb_t destroy;
+};
+
+struct key_pressed {
+	uint8_t op;
+	uint8_t *params;
+	size_t params_len;
+	guint timer;
+};
+
+struct avctp {
+	unsigned int ref;
+	int uinput;
+
+	unsigned int passthrough_id;
+	unsigned int unit_id;
+	unsigned int subunit_id;
+
+	struct avctp_channel *control;
+	struct avctp_channel *browsing;
+
+	struct avctp_passthrough_handler *handler;
+
+	uint8_t key_quirks[256];
+	struct key_pressed key;
+	uint16_t version;
+
+	avctp_destroy_cb_t destroy;
+	void *data;
+};
+
+struct avctp_passthrough_handler {
+	avctp_passthrough_cb cb;
+	void *user_data;
+	unsigned int id;
+};
+
+struct avctp_pdu_handler {
+	uint8_t opcode;
+	avctp_control_pdu_cb cb;
+	void *user_data;
+	unsigned int id;
+};
+
+struct avctp_browsing_pdu_handler {
+	avctp_browsing_pdu_cb cb;
+	void *user_data;
+	unsigned int id;
+	avctp_destroy_cb_t destroy;
+};
+
+static struct {
+	const char *name;
+	uint8_t avc;
+	uint16_t uinput;
+} key_map[] = {
+	{ "SELECT",		AVC_SELECT,		KEY_SELECT },
+	{ "UP",			AVC_UP,			KEY_UP },
+	{ "DOWN",		AVC_DOWN,		KEY_DOWN },
+	{ "LEFT",		AVC_LEFT,		KEY_LEFT },
+	{ "RIGHT",		AVC_RIGHT,		KEY_RIGHT },
+	{ "ROOT MENU",		AVC_ROOT_MENU,		KEY_MENU },
+	{ "CONTENTS MENU",	AVC_CONTENTS_MENU,	KEY_PROGRAM },
+	{ "FAVORITE MENU",	AVC_FAVORITE_MENU,	KEY_FAVORITES },
+	{ "EXIT",		AVC_EXIT,		KEY_EXIT },
+	{ "ON DEMAND MENU",	AVC_ON_DEMAND_MENU,	KEY_MENU },
+	{ "APPS MENU",		AVC_APPS_MENU,		KEY_MENU },
+	{ "0",			AVC_0,			KEY_0 },
+	{ "1",			AVC_1,			KEY_1 },
+	{ "2",			AVC_2,			KEY_2 },
+	{ "3",			AVC_3,			KEY_3 },
+	{ "4",			AVC_4,			KEY_4 },
+	{ "5",			AVC_5,			KEY_5 },
+	{ "6",			AVC_6,			KEY_6 },
+	{ "7",			AVC_7,			KEY_7 },
+	{ "8",			AVC_8,			KEY_8 },
+	{ "9",			AVC_9,			KEY_9 },
+	{ "DOT",		AVC_DOT,		KEY_DOT },
+	{ "ENTER",		AVC_ENTER,		KEY_ENTER },
+	{ "CHANNEL UP",		AVC_CHANNEL_UP,		KEY_CHANNELUP },
+	{ "CHANNEL DOWN",	AVC_CHANNEL_DOWN,	KEY_CHANNELDOWN },
+	{ "CHANNEL PREVIOUS",	AVC_CHANNEL_PREVIOUS,	KEY_LAST },
+	{ "INPUT SELECT",	AVC_INPUT_SELECT,	KEY_CONFIG },
+	{ "INFO",		AVC_INFO,		KEY_INFO },
+	{ "HELP",		AVC_HELP,		KEY_HELP },
+	{ "POWER",		AVC_POWER,		KEY_POWER2 },
+	{ "VOLUME UP",		AVC_VOLUME_UP,		KEY_VOLUMEUP },
+	{ "VOLUME DOWN",	AVC_VOLUME_DOWN,	KEY_VOLUMEDOWN },
+	{ "MUTE",		AVC_MUTE,		KEY_MUTE },
+	{ "PLAY",		AVC_PLAY,		KEY_PLAYCD },
+	{ "STOP",		AVC_STOP,		KEY_STOPCD },
+	{ "PAUSE",		AVC_PAUSE,		KEY_PAUSECD },
+	{ "FORWARD",		AVC_FORWARD,		KEY_NEXTSONG },
+	{ "BACKWARD",		AVC_BACKWARD,		KEY_PREVIOUSSONG },
+	{ "RECORD",		AVC_RECORD,		KEY_RECORD },
+	{ "REWIND",		AVC_REWIND,		KEY_REWIND },
+	{ "FAST FORWARD",	AVC_FAST_FORWARD,	KEY_FASTFORWARD },
+	{ "LIST",		AVC_LIST,		KEY_LIST },
+	{ "F1",			AVC_F1,			KEY_F1 },
+	{ "F2",			AVC_F2,			KEY_F2 },
+	{ "F3",			AVC_F3,			KEY_F3 },
+	{ "F4",			AVC_F4,			KEY_F4 },
+	{ "F5",			AVC_F5,			KEY_F5 },
+	{ "F6",			AVC_F6,			KEY_F6 },
+	{ "F7",			AVC_F7,			KEY_F7 },
+	{ "F8",			AVC_F8,			KEY_F8 },
+	{ "F9",			AVC_F9,			KEY_F9 },
+	{ "RED",		AVC_RED,		KEY_RED },
+	{ "GREEN",		AVC_GREEN,		KEY_GREEN },
+	{ "BLUE",		AVC_BLUE,		KEY_BLUE },
+	{ "YELLOW",		AVC_YELLOW,		KEY_YELLOW },
+	{ NULL }
+};
+
+static gboolean process_queue(gpointer user_data);
+static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
+					uint8_t subunit, uint8_t *operands,
+					size_t operand_count, void *user_data);
+
+static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
+{
+	struct uinput_event event;
+	int err;
+
+	memset(&event, 0, sizeof(event));
+	event.type	= type;
+	event.code	= code;
+	event.value	= value;
+
+	do {
+		err = write(fd, &event, sizeof(event));
+	} while (err < 0 && errno == EINTR);
+
+	if (err < 0) {
+		err = -errno;
+		error("send_event: %s (%d)", strerror(-err), -err);
+	}
+
+	return err;
+}
+
+static void send_key(int fd, uint16_t key, int pressed)
+{
+	send_event(fd, EV_KEY, key, pressed);
+	send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static gboolean auto_release(gpointer user_data)
+{
+	struct avctp *session = user_data;
+
+	session->key.timer = 0;
+
+	DBG("AV/C: key press timeout");
+
+	send_key(session->uinput, session->key.op, 0);
+
+	return FALSE;
+}
+
+static ssize_t handle_panel_passthrough(struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avctp_passthrough_handler *handler = session->handler;
+	const char *status;
+	int pressed, i;
+
+	if (*code != AVC_CTYPE_CONTROL || *subunit != AVC_SUBUNIT_PANEL) {
+		*code = AVC_CTYPE_REJECTED;
+		return operand_count;
+	}
+
+	if (operand_count == 0)
+		goto done;
+
+	if (operands[0] & 0x80) {
+		status = "released";
+		pressed = 0;
+	} else {
+		status = "pressed";
+		pressed = 1;
+	}
+
+	if (session->key.timer == 0 && handler != NULL) {
+		if (handler->cb(session, operands[0] & 0x7F,
+						pressed, handler->user_data))
+			goto done;
+	}
+
+	if (session->uinput < 0) {
+		DBG("AV/C: uinput not initialized");
+		*code = AVC_CTYPE_NOT_IMPLEMENTED;
+		return 0;
+	}
+
+	for (i = 0; key_map[i].name != NULL; i++) {
+		uint8_t key_quirks;
+
+		if ((operands[0] & 0x7F) != key_map[i].avc)
+			continue;
+
+		DBG("AV/C: %s %s", key_map[i].name, status);
+
+		key_quirks = session->key_quirks[key_map[i].avc];
+
+		if (key_quirks & QUIRK_NO_RELEASE) {
+			if (!pressed) {
+				DBG("AV/C: Ignoring release");
+				break;
+			}
+
+			DBG("AV/C: treating key press as press + release");
+			send_key(session->uinput, key_map[i].uinput, 1);
+			send_key(session->uinput, key_map[i].uinput, 0);
+			break;
+		}
+
+		if (pressed) {
+			if (session->key.timer > 0) {
+				g_source_remove(session->key.timer);
+				send_key(session->uinput, session->key.op, 0);
+			}
+
+			session->key.op = key_map[i].uinput;
+			session->key.timer = g_timeout_add_seconds(
+							AVC_PRESS_TIMEOUT,
+							auto_release,
+							session);
+		} else if (session->key.timer > 0) {
+			g_source_remove(session->key.timer);
+			session->key.timer = 0;
+		}
+
+		send_key(session->uinput, key_map[i].uinput, pressed);
+		break;
+	}
+
+	if (key_map[i].name == NULL) {
+		DBG("AV/C: unknown button 0x%02X %s",
+						operands[0] & 0x7F, status);
+		*code = AVC_CTYPE_NOT_IMPLEMENTED;
+		return operand_count;
+	}
+
+done:
+	*code = AVC_CTYPE_ACCEPTED;
+	return operand_count;
+}
+
+static ssize_t handle_unit_info(struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	if (*code != AVC_CTYPE_STATUS) {
+		*code = AVC_CTYPE_REJECTED;
+		return 0;
+	}
+
+	*code = AVC_CTYPE_STABLE;
+
+	/*
+	 * The first operand should be 0x07 for the UNITINFO response.
+	 * Neither AVRCP (section 22.1, page 117) nor AVC Digital
+	 * Interface Command Set (section 9.2.1, page 45) specs
+	 * explain this value but both use it
+	 */
+	if (operand_count >= 1)
+		operands[0] = 0x07;
+	if (operand_count >= 2)
+		operands[1] = AVC_SUBUNIT_PANEL << 3;
+
+	DBG("reply to AVC_OP_UNITINFO");
+
+	return operand_count;
+}
+
+static ssize_t handle_subunit_info(struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	if (*code != AVC_CTYPE_STATUS) {
+		*code = AVC_CTYPE_REJECTED;
+		return 0;
+	}
+
+	*code = AVC_CTYPE_STABLE;
+
+	/*
+	 * The first operand should be 0x07 for the UNITINFO response.
+	 * Neither AVRCP (section 22.1, page 117) nor AVC Digital
+	 * Interface Command Set (section 9.2.1, page 45) specs
+	 * explain this value but both use it
+	 */
+	if (operand_count >= 2)
+		operands[1] = AVC_SUBUNIT_PANEL << 3;
+
+	DBG("reply to AVC_OP_SUBUNITINFO");
+
+	return operand_count;
+}
+
+static struct avctp_pdu_handler *find_handler(GSList *list, uint8_t opcode)
+{
+	for (; list; list = list->next) {
+		struct avctp_pdu_handler *handler = list->data;
+
+		if (handler->opcode == opcode)
+			return handler;
+	}
+
+	return NULL;
+}
+
+static void pending_destroy(gpointer data, gpointer user_data)
+{
+	struct avctp_pending_req *req = data;
+
+	if (req->destroy)
+		req->destroy(req->data);
+
+	if (req->timeout > 0)
+		g_source_remove(req->timeout);
+
+	g_free(req);
+}
+
+static void avctp_channel_destroy(struct avctp_channel *chan)
+{
+	g_io_channel_shutdown(chan->io, TRUE, NULL);
+	g_io_channel_unref(chan->io);
+
+	if (chan->watch)
+		g_source_remove(chan->watch);
+
+	if (chan->p)
+		pending_destroy(chan->p, NULL);
+
+	if (chan->process_id > 0)
+		g_source_remove(chan->process_id);
+
+	if (chan->destroy)
+		chan->destroy(chan);
+
+	g_free(chan->buffer);
+	g_queue_foreach(chan->queue, pending_destroy, NULL);
+	g_queue_free(chan->queue);
+	g_slist_foreach(chan->processed, pending_destroy, NULL);
+	g_slist_free(chan->processed);
+	g_slist_free_full(chan->handlers, g_free);
+	g_free(chan);
+}
+
+static int avctp_send(struct avctp_channel *control, uint8_t transaction,
+				uint8_t cr, uint8_t code,
+				uint8_t subunit, uint8_t opcode,
+				const struct iovec *iov, int iov_cnt)
+{
+	struct avctp_header avctp;
+	struct avc_header avc;
+	struct msghdr msg;
+	int sk, err = 0;
+	struct iovec pdu[iov_cnt + 2];
+	int i;
+	size_t len = sizeof(avctp) + sizeof(avc);
+
+	DBG("");
+
+	pdu[0].iov_base = &avctp;
+	pdu[0].iov_len  = sizeof(avctp);
+	pdu[1].iov_base = &avc;
+	pdu[1].iov_len  = sizeof(avc);
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i + 2].iov_base = iov[i].iov_base;
+		pdu[i + 2].iov_len  = iov[i].iov_len;
+		len += iov[i].iov_len;
+	}
+
+	if (control->omtu < len)
+		return -EOVERFLOW;
+
+	sk = g_io_channel_unix_get_fd(control->io);
+
+	memset(&avctp, 0, sizeof(avctp));
+
+	avctp.transaction = transaction;
+	avctp.packet_type = AVCTP_PACKET_SINGLE;
+	avctp.cr = cr;
+	avctp.pid = htons(AV_REMOTE_SVCLASS_ID);
+
+	memset(&avc, 0, sizeof(avc));
+
+	avc.code = code;
+	avc.subunit_type = subunit;
+	avc.opcode = opcode;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = pdu;
+	msg.msg_iovlen = iov_cnt + 2;
+
+	if (sendmsg(sk, &msg, 0) < 0)
+		err = -errno;
+
+	return err;
+}
+
+static int avctp_browsing_send(struct avctp_channel *browsing,
+				uint8_t transaction, uint8_t cr,
+				const struct iovec *iov, int iov_cnt)
+{
+	struct avctp_header avctp;
+	struct msghdr msg;
+	struct iovec pdu[iov_cnt + 1];
+	int sk, err = 0;
+	int i;
+	size_t len = sizeof(avctp);
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i + 1].iov_base = iov[i].iov_base;
+		pdu[i + 1].iov_len  = iov[i].iov_len;
+		len += iov[i].iov_len;
+	}
+
+	pdu[0].iov_base = &avctp;
+	pdu[0].iov_len  = sizeof(avctp);
+
+	if (browsing->omtu < len)
+		return -EOVERFLOW;
+
+	sk = g_io_channel_unix_get_fd(browsing->io);
+
+	memset(&avctp, 0, sizeof(avctp));
+
+	avctp.transaction = transaction;
+	avctp.packet_type = AVCTP_PACKET_SINGLE;
+	avctp.cr = cr;
+	avctp.pid = htons(AV_REMOTE_SVCLASS_ID);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = pdu;
+	msg.msg_iovlen = iov_cnt + 1;
+
+	if (sendmsg(sk, &msg, 0) < 0)
+		err = -errno;
+
+	return err;
+}
+
+static void control_req_destroy(void *data)
+{
+	struct avctp_control_req *req = data;
+	struct avctp_pending_req *p = req->p;
+	struct avctp *session = p->chan->session;
+	int i;
+
+	if (p->err == 0 || req->func == NULL)
+		goto done;
+
+	req->func(session, AVC_CTYPE_REJECTED, req->subunit, NULL, 0,
+							req->user_data);
+
+done:
+	for (i = 0; i < req->iov_cnt; i++)
+		g_free(req->iov[i].iov_base);
+
+	g_free(req->iov);
+	g_free(req);
+}
+
+static void browsing_req_destroy(void *data)
+{
+	struct avctp_browsing_req *req = data;
+	struct avctp_pending_req *p = req->p;
+	struct avctp *session = p->chan->session;
+	int i;
+
+	if (p->err == 0 || req->func == NULL)
+		goto done;
+
+	req->func(session, NULL, 0, req->user_data);
+
+done:
+	for (i = 0; i < req->iov_cnt; i++)
+		g_free(req->iov[i].iov_base);
+
+	g_free(req->iov);
+	g_free(req);
+}
+
+static gboolean req_timeout(gpointer user_data)
+{
+	struct avctp_channel *chan = user_data;
+	struct avctp_pending_req *p = chan->p;
+
+	DBG("transaction %u", p->transaction);
+
+	p->timeout = 0;
+	p->err = -ETIMEDOUT;
+
+	pending_destroy(p, NULL);
+	chan->p = NULL;
+
+	if (chan->process_id == 0)
+		chan->process_id = g_idle_add(process_queue, chan);
+
+	return FALSE;
+}
+
+static int process_control(void *data)
+{
+	struct avctp_control_req *req = data;
+	struct avctp_pending_req *p = req->p;
+
+	return avctp_send(p->chan, p->transaction, AVCTP_COMMAND, req->code,
+				req->subunit, req->op, req->iov, req->iov_cnt);
+}
+
+static int process_browsing(void *data)
+{
+	struct avctp_browsing_req *req = data;
+	struct avctp_pending_req *p = req->p;
+
+	return avctp_browsing_send(p->chan, p->transaction, AVCTP_COMMAND,
+						req->iov, req->iov_cnt);
+}
+
+static gboolean process_queue(void *user_data)
+{
+	struct avctp_channel *chan = user_data;
+	struct avctp_pending_req *p = chan->p;
+
+	chan->process_id = 0;
+
+	if (p != NULL)
+		return FALSE;
+
+	while ((p = g_queue_pop_head(chan->queue))) {
+
+		if (p->process(p->data) == 0)
+			break;
+
+		pending_destroy(p, NULL);
+	}
+
+	if (p == NULL)
+		return FALSE;
+
+	chan->p = p;
+	p->timeout = g_timeout_add_seconds(2, req_timeout, chan);
+
+	return FALSE;
+
+}
+
+static struct avctp *avctp_ref(struct avctp *session)
+{
+	__sync_fetch_and_add(&session->ref, 1);
+
+	DBG("%p: ref=%d", session, session->ref);
+
+	return session;
+}
+
+static void avctp_unref(struct avctp *session)
+{
+	DBG("%p: ref=%d", session, session->ref);
+
+	if (__sync_sub_and_fetch(&session->ref, 1))
+		return;
+
+	if (session->browsing)
+		avctp_channel_destroy(session->browsing);
+
+	if (session->control)
+		avctp_channel_destroy(session->control);
+
+	if (session->destroy)
+		session->destroy(session->data);
+
+	g_free(session->handler);
+
+	if (session->key.timer > 0)
+		g_source_remove(session->key.timer);
+
+	if (session->uinput >= 0) {
+		DBG("AVCTP: closing uinput");
+
+		ioctl(session->uinput, UI_DEV_DESTROY);
+		close(session->uinput);
+		session->uinput = -1;
+	}
+
+	g_free(session);
+}
+
+static void control_response(struct avctp_channel *control,
+					struct avctp_header *avctp,
+					struct avc_header *avc,
+					uint8_t *operands,
+					size_t operand_count)
+{
+	struct avctp_pending_req *p = control->p;
+	struct avctp_control_req *req;
+	GSList *l;
+
+	if (p && p->transaction == avctp->transaction) {
+		control->processed = g_slist_prepend(control->processed, p);
+
+		if (p->timeout > 0) {
+			g_source_remove(p->timeout);
+			p->timeout = 0;
+		}
+
+		control->p = NULL;
+
+		if (control->process_id == 0)
+			control->process_id = g_idle_add(process_queue,
+								control);
+	}
+
+	avctp_ref(control->session);
+
+	for (l = control->processed; l; l = l->next) {
+		p = l->data;
+		req = p->data;
+
+		if (p->transaction != avctp->transaction)
+			continue;
+
+		if (req->func && req->func(control->session, avc->code,
+						avc->subunit_type,
+						operands, operand_count,
+						req->user_data))
+			break;
+
+		control->processed = g_slist_remove(control->processed, p);
+		pending_destroy(p, NULL);
+
+		break;
+	}
+
+	avctp_unref(control->session);
+}
+
+static void browsing_response(struct avctp_channel *browsing,
+					struct avctp_header *avctp,
+					uint8_t *operands,
+					size_t operand_count)
+{
+	struct avctp_pending_req *p = browsing->p;
+	struct avctp_browsing_req *req;
+	GSList *l;
+
+	if (p && p->transaction == avctp->transaction) {
+		browsing->processed = g_slist_prepend(browsing->processed, p);
+
+		if (p->timeout > 0) {
+			g_source_remove(p->timeout);
+			p->timeout = 0;
+		}
+
+		browsing->p = NULL;
+
+		if (browsing->process_id == 0)
+			browsing->process_id = g_idle_add(process_queue,
+								browsing);
+	}
+
+	avctp_ref(browsing->session);
+
+	for (l = browsing->processed; l; l = l->next) {
+		p = l->data;
+		req = p->data;
+
+		if (p->transaction != avctp->transaction)
+			continue;
+
+		if (req->func && req->func(browsing->session, operands,
+						operand_count, req->user_data))
+			break;
+
+		browsing->processed = g_slist_remove(browsing->processed, p);
+		pending_destroy(p, NULL);
+
+		break;
+	}
+
+	avctp_unref(browsing->session);
+}
+
+static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
+				gpointer data)
+{
+	struct avctp *session = data;
+	struct avctp_channel *browsing = session->browsing;
+	uint8_t *buf = browsing->buffer;
+	uint8_t *operands;
+	struct avctp_header *avctp;
+	int sock, ret, packet_size, operand_count;
+	struct avctp_browsing_pdu_handler *handler;
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+		goto failed;
+
+	sock = g_io_channel_unix_get_fd(chan);
+
+	ret = read(sock, buf, browsing->imtu);
+	if (ret <= 0)
+		goto failed;
+
+	if (ret < AVCTP_HEADER_LENGTH) {
+		error("Too small AVCTP packet");
+		goto failed;
+	}
+
+	avctp = (struct avctp_header *) buf;
+
+	if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
+		error("Invalid packet type");
+		goto failed;
+	}
+
+	operands = buf + AVCTP_HEADER_LENGTH;
+	ret -= AVCTP_HEADER_LENGTH;
+	operand_count = ret;
+
+	if (avctp->cr == AVCTP_RESPONSE) {
+		browsing_response(browsing, avctp, operands, operand_count);
+		return TRUE;
+	}
+
+	packet_size = AVCTP_HEADER_LENGTH;
+	avctp->cr = AVCTP_RESPONSE;
+
+	handler = g_slist_nth_data(browsing->handlers, 0);
+	if (handler == NULL) {
+		DBG("handler not found");
+		/* FIXME: Add general reject */
+		/* packet_size += avrcp_browsing_general_reject(operands); */
+		goto send;
+	}
+
+	ret = handler->cb(session, avctp->transaction, operands, operand_count,
+							handler->user_data);
+	if (ret < 0) {
+		if (ret == -EAGAIN)
+			return TRUE;
+		goto failed;
+	}
+
+	packet_size += ret;
+
+send:
+	if (packet_size != 0) {
+		ret = write(sock, buf, packet_size);
+		if (ret != packet_size)
+			goto failed;
+	}
+
+	return TRUE;
+
+failed:
+	DBG("AVCTP Browsing: disconnected");
+
+	if (session->browsing) {
+		avctp_channel_destroy(session->browsing);
+		session->browsing = NULL;
+	}
+
+	return FALSE;
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+	struct avctp *session = data;
+	struct avctp_channel *control = session->control;
+	uint8_t *buf = control->buffer;
+	uint8_t *operands, code, subunit;
+	struct avctp_header *avctp;
+	struct avc_header *avc;
+	int packet_size, operand_count, sock;
+	struct avctp_pdu_handler *handler;
+	ssize_t ret;
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+		goto failed;
+
+	sock = g_io_channel_unix_get_fd(chan);
+
+	ret = read(sock, buf, control->imtu);
+	if (ret <= 0)
+		goto failed;
+
+	if (ret < AVCTP_HEADER_LENGTH) {
+		error("Too small AVCTP packet");
+		goto failed;
+	}
+
+	avctp = (struct avctp_header *) buf;
+
+	ret -= AVCTP_HEADER_LENGTH;
+	if (ret < AVC_HEADER_LENGTH) {
+		error("Too small AVC packet");
+		goto failed;
+	}
+
+	avc = (struct avc_header *) (buf + AVCTP_HEADER_LENGTH);
+
+	ret -= AVC_HEADER_LENGTH;
+
+	operands = (uint8_t *) avc + AVC_HEADER_LENGTH;
+	operand_count = ret;
+
+	if (avctp->cr == AVCTP_RESPONSE) {
+		control_response(control, avctp, avc, operands, operand_count);
+		return TRUE;
+	}
+
+	packet_size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH;
+	avctp->cr = AVCTP_RESPONSE;
+
+	if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
+		avc->code = AVC_CTYPE_NOT_IMPLEMENTED;
+		goto done;
+	}
+
+	if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) {
+		avctp->ipid = 1;
+		packet_size = AVCTP_HEADER_LENGTH;
+		goto done;
+	}
+
+	handler = find_handler(control->handlers, avc->opcode);
+	if (!handler) {
+		DBG("handler not found for 0x%02x", avc->opcode);
+		avc->code = AVC_CTYPE_REJECTED;
+		goto done;
+	}
+
+	code = avc->code;
+	subunit = avc->subunit_type;
+
+	ret = handler->cb(session, avctp->transaction, &code,
+					&subunit, operands, operand_count,
+					handler->user_data);
+	if (ret < 0) {
+		if (ret == -EAGAIN)
+			return TRUE;
+		goto failed;
+	}
+
+	packet_size += ret;
+	avc->code = code;
+	avc->subunit_type = subunit;
+
+done:
+	ret = write(sock, buf, packet_size);
+	if (ret != packet_size)
+		goto failed;
+
+	return TRUE;
+
+failed:
+	DBG("AVCTP session %p got disconnected", session);
+	avctp_shutdown(session);
+	return FALSE;
+}
+
+static int uinput_create(const char *name)
+{
+	struct uinput_dev dev;
+	int fd, err, i;
+
+	fd = open("/dev/uinput", O_RDWR);
+	if (fd < 0) {
+		fd = open("/dev/input/uinput", O_RDWR);
+		if (fd < 0) {
+			fd = open("/dev/misc/uinput", O_RDWR);
+			if (fd < 0) {
+				err = -errno;
+				error("Can't open input device: %s (%d)",
+							strerror(-err), -err);
+				return err;
+			}
+		}
+	}
+
+	memset(&dev, 0, sizeof(dev));
+	if (name)
+		strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1);
+
+	dev.id.bustype = BUS_BLUETOOTH;
+	dev.id.vendor  = 0x0000;
+	dev.id.product = 0x0000;
+	dev.id.version = 0x0000;
+
+	if (write(fd, &dev, sizeof(dev)) < 0) {
+		err = -errno;
+		error("Can't write device information: %s (%d)",
+						strerror(-err), -err);
+		close(fd);
+		return err;
+	}
+
+	ioctl(fd, UI_SET_EVBIT, EV_KEY);
+	ioctl(fd, UI_SET_EVBIT, EV_REL);
+	ioctl(fd, UI_SET_EVBIT, EV_REP);
+	ioctl(fd, UI_SET_EVBIT, EV_SYN);
+
+	for (i = 0; key_map[i].name != NULL; i++)
+		ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput);
+
+	if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
+		err = -errno;
+		error("Can't create uinput device: %s (%d)",
+						strerror(-err), -err);
+		close(fd);
+		return err;
+	}
+
+	return fd;
+}
+
+int avctp_init_uinput(struct avctp *session, const char *name,
+							const char *address)
+{
+	if (g_str_equal(name, "Nokia CK-20W")) {
+		session->key_quirks[AVC_FORWARD] |= QUIRK_NO_RELEASE;
+		session->key_quirks[AVC_BACKWARD] |= QUIRK_NO_RELEASE;
+		session->key_quirks[AVC_PLAY] |= QUIRK_NO_RELEASE;
+		session->key_quirks[AVC_PAUSE] |= QUIRK_NO_RELEASE;
+	}
+
+	session->uinput = uinput_create(address);
+	if (session->uinput < 0) {
+		error("AVCTP: failed to init uinput for %s", address);
+		return session->uinput;
+	}
+
+	return 0;
+}
+
+static struct avctp_channel *avctp_channel_create(struct avctp *session, int fd,
+						size_t imtu, size_t omtu,
+						avctp_destroy_cb_t destroy)
+{
+	struct avctp_channel *chan;
+
+	chan = g_new0(struct avctp_channel, 1);
+	chan->session = session;
+	chan->io = g_io_channel_unix_new(fd);
+	chan->queue = g_queue_new();
+	chan->imtu = imtu;
+	chan->omtu = omtu;
+	chan->buffer = g_malloc0(MAX(imtu, omtu));
+	chan->destroy = destroy;
+
+	return chan;
+}
+
+static void handler_free(void *data)
+{
+	struct avctp_browsing_pdu_handler *handler = data;
+
+	if (handler->destroy)
+		handler->destroy(handler->user_data);
+
+	g_free(data);
+}
+
+static void avctp_destroy_browsing(void *data)
+{
+	struct avctp_channel *chan = data;
+
+	g_slist_free_full(chan->handlers, handler_free);
+
+	chan->handlers = NULL;
+}
+
+static struct avctp_pending_req *pending_create(struct avctp_channel *chan,
+						avctp_process_cb process,
+						void *data,
+						avctp_destroy_cb_t destroy)
+{
+	struct avctp_pending_req *p;
+	GSList *l, *tmp;
+
+	if (!chan->processed)
+		goto done;
+
+	tmp = g_slist_copy(chan->processed);
+
+	/* Find first unused transaction id */
+	for (l = tmp; l; l = g_slist_next(l)) {
+		struct avctp_pending_req *req = l->data;
+
+		if (req->transaction == chan->transaction) {
+			chan->transaction++;
+			chan->transaction %= 16;
+			tmp = g_slist_delete_link(tmp, l);
+			l = tmp;
+		}
+	}
+
+	g_slist_free(tmp);
+
+done:
+	p = g_new0(struct avctp_pending_req, 1);
+	p->chan = chan;
+	p->transaction = chan->transaction;
+	p->process = process;
+	p->data = data;
+	p->destroy = destroy;
+
+	chan->transaction++;
+	chan->transaction %= 16;
+
+	return p;
+}
+
+static int avctp_send_req(struct avctp *session, uint8_t code, uint8_t subunit,
+			uint8_t opcode, const struct iovec *iov, int iov_cnt,
+			avctp_rsp_cb func, void *user_data)
+{
+	struct avctp_channel *control = session->control;
+	struct avctp_pending_req *p;
+	struct avctp_control_req *req;
+	struct iovec *pdu;
+	int i;
+
+	if (control == NULL)
+		return -ENOTCONN;
+
+	pdu = g_new0(struct iovec, iov_cnt);
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i].iov_len = iov[i].iov_len;
+		pdu[i].iov_base = g_memdup(iov[i].iov_base, iov[i].iov_len);
+	}
+
+	req = g_new0(struct avctp_control_req, 1);
+	req->code = code;
+	req->subunit = subunit;
+	req->op = opcode;
+	req->func = func;
+	req->iov = pdu;
+	req->iov_cnt = iov_cnt;
+	req->user_data = user_data;
+
+	p = pending_create(control, process_control, req, control_req_destroy);
+
+	req->p = p;
+
+	g_queue_push_tail(control->queue, p);
+
+	if (control->process_id == 0)
+		control->process_id = g_idle_add(process_queue, control);
+
+	return 0;
+}
+
+int avctp_send_browsing_req(struct avctp *session,
+				const struct iovec *iov, int iov_cnt,
+				avctp_browsing_rsp_cb func, void *user_data)
+{
+	struct avctp_channel *browsing = session->browsing;
+	struct avctp_pending_req *p;
+	struct avctp_browsing_req *req;
+	struct iovec *pdu;
+	int i;
+
+	if (browsing == NULL)
+		return -ENOTCONN;
+
+	pdu = g_new0(struct iovec, iov_cnt);
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i].iov_len = iov[i].iov_len;
+		pdu[i].iov_base = g_memdup(iov[i].iov_base, iov[i].iov_len);
+	}
+
+	req = g_new0(struct avctp_browsing_req, 1);
+	req->func = func;
+	req->iov = pdu;
+	req->iov_cnt = iov_cnt;
+	req->user_data = user_data;
+
+	p = pending_create(browsing, process_browsing, req,
+			browsing_req_destroy);
+
+	req->p = p;
+
+	g_queue_push_tail(browsing->queue, p);
+
+	/* Connection did not complete, delay process of the request */
+	if (browsing->watch == 0)
+		return 0;
+
+	if (browsing->process_id == 0)
+		browsing->process_id = g_idle_add(process_queue, browsing);
+
+	return 0;
+}
+
+int avctp_send_browsing(struct avctp *session, uint8_t transaction,
+					const struct iovec *iov, int iov_cnt)
+{
+	struct avctp_channel *browsing = session->browsing;
+
+	if (browsing == NULL)
+		return -ENOTCONN;
+
+	return avctp_browsing_send(browsing, transaction, AVCTP_RESPONSE,
+								iov, iov_cnt);
+}
+
+static const char *op2str(uint8_t op)
+{
+	int i;
+
+	for (i = 0; key_map[i].name != NULL; i++) {
+		if ((op & 0x7F) == key_map[i].avc)
+			return key_map[i].name;
+	}
+
+	return "UNKNOWN";
+}
+
+static int avctp_passthrough_press(struct avctp *session, uint8_t op,
+					uint8_t *params, size_t params_len)
+{
+	struct iovec iov[2];
+	int iov_cnt;
+	uint8_t operands[2];
+
+	DBG("%s", op2str(op));
+
+	iov[0].iov_base = operands;
+	iov[0].iov_len = sizeof(operands);
+
+	/* Button pressed */
+	operands[0] = op & 0x7f;
+
+	if (params_len > 0) {
+		iov[1].iov_base = params;
+		iov[1].iov_len = params_len;
+		iov_cnt = 2;
+		operands[1] = params_len;
+	} else {
+		iov_cnt = 1;
+		operands[1] = 0;
+	}
+
+	return avctp_send_req(session, AVC_CTYPE_CONTROL,
+				AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+				iov, iov_cnt, avctp_passthrough_rsp, NULL);
+}
+
+static int avctp_passthrough_release(struct avctp *session, uint8_t op,
+					uint8_t *params, size_t params_len)
+{
+	struct iovec iov[2];
+	int iov_cnt;
+	uint8_t operands[2];
+
+	DBG("%s", op2str(op));
+
+	iov[0].iov_base = operands;
+	iov[0].iov_len = sizeof(operands);
+
+	/* Button released */
+	operands[0] = op | 0x80;
+
+	if (params_len > 0) {
+		iov[1].iov_base = params;
+		iov[1].iov_len = params_len;
+		iov_cnt = 2;
+		operands[1] = params_len;
+	} else {
+		iov_cnt = 1;
+		operands[1] = 0;
+	}
+
+	return avctp_send_req(session, AVC_CTYPE_CONTROL,
+				AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+				iov, iov_cnt, NULL, NULL);
+}
+
+static gboolean repeat_timeout(gpointer user_data)
+{
+	struct avctp *session = user_data;
+
+	avctp_passthrough_release(session, session->key.op, session->key.params,
+						session->key.params_len);
+	avctp_passthrough_press(session, session->key.op, session->key.params,
+						session->key.params_len);
+
+	return TRUE;
+}
+
+static void release_pressed(struct avctp *session)
+{
+	avctp_passthrough_release(session, session->key.op, session->key.params,
+						session->key.params_len);
+
+	if (session->key.timer > 0)
+		g_source_remove(session->key.timer);
+
+	session->key.timer = 0;
+}
+
+static bool set_pressed(struct avctp *session, uint8_t op, uint8_t *params,
+							size_t params_len)
+{
+	if (session->key.timer > 0) {
+		if (session->key.op == op)
+			return TRUE;
+		release_pressed(session);
+	}
+
+	if (op != AVC_FAST_FORWARD && op != AVC_REWIND)
+		return FALSE;
+
+	session->key.op = op;
+	session->key.params = params;
+	session->key.params_len = params_len;
+	session->key.timer = g_timeout_add_seconds(AVC_PRESS_TIMEOUT,
+							repeat_timeout,
+							session);
+
+	return TRUE;
+}
+
+static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
+					uint8_t subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	uint8_t *params;
+	size_t params_len;
+
+	DBG("code 0x%02x operand_count %zd", code, operand_count);
+
+	if (code != AVC_CTYPE_ACCEPTED)
+		return FALSE;
+
+	if (operands[0] == AVC_VENDOR_UNIQUE) {
+		params = &operands[2];
+		params_len = operand_count - 2;
+	} else {
+		params = NULL;
+		params_len = 0;
+	}
+
+	if (set_pressed(session, operands[0], params, params_len))
+		return FALSE;
+
+	avctp_passthrough_release(session, operands[0], params, params_len);
+
+	return FALSE;
+}
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op, uint8_t *params,
+							size_t params_len)
+{
+	/* Auto release if key pressed */
+	if (session->key.timer > 0)
+		release_pressed(session);
+
+	return avctp_passthrough_press(session, op, params, params_len);
+}
+
+int avctp_send_vendor(struct avctp *session, uint8_t transaction,
+				uint8_t code, uint8_t subunit,
+				const struct iovec *iov, int iov_cnt)
+{
+	struct avctp_channel *control = session->control;
+
+	if (control == NULL)
+		return -ENOTCONN;
+
+	return avctp_send(control, transaction, AVCTP_RESPONSE, code, subunit,
+						AVC_OP_VENDORDEP, iov, iov_cnt);
+}
+
+int avctp_send_vendor_req(struct avctp *session, uint8_t code, uint8_t subunit,
+					const struct iovec *iov, int iov_cnt,
+					avctp_rsp_cb func, void *user_data)
+{
+	return avctp_send_req(session, code, subunit, AVC_OP_VENDORDEP, iov,
+						iov_cnt, func, user_data);
+}
+
+unsigned int avctp_register_passthrough_handler(struct avctp *session,
+						avctp_passthrough_cb cb,
+						void *user_data)
+{
+	struct avctp_channel *control = session->control;
+	struct avctp_passthrough_handler *handler;
+	static unsigned int id = 0;
+
+	if (control == NULL || session->handler != NULL)
+		return 0;
+
+	handler = g_new(struct avctp_passthrough_handler, 1);
+	handler->cb = cb;
+	handler->user_data = user_data;
+	handler->id = ++id;
+
+	session->handler = handler;
+
+	return handler->id;
+}
+
+bool avctp_unregister_passthrough_handler(struct avctp *session,
+							unsigned int id)
+{
+	if (session->handler == NULL)
+		return false;
+
+	if (session->handler->id != id)
+		return false;
+
+	g_free(session->handler);
+	session->handler = NULL;
+	return true;
+}
+
+unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode,
+						avctp_control_pdu_cb cb,
+						void *user_data)
+{
+	struct avctp_channel *control = session->control;
+	struct avctp_pdu_handler *handler;
+	static unsigned int id = 0;
+
+	if (control == NULL)
+		return 0;
+
+	handler = find_handler(control->handlers, opcode);
+	if (handler)
+		return 0;
+
+	handler = g_new(struct avctp_pdu_handler, 1);
+	handler->opcode = opcode;
+	handler->cb = cb;
+	handler->user_data = user_data;
+	handler->id = ++id;
+
+	control->handlers = g_slist_append(control->handlers, handler);
+
+	return handler->id;
+}
+
+unsigned int avctp_register_browsing_pdu_handler(struct avctp *session,
+						avctp_browsing_pdu_cb cb,
+						void *user_data,
+						avctp_destroy_cb_t destroy)
+{
+	struct avctp_channel *browsing = session->browsing;
+	struct avctp_browsing_pdu_handler *handler;
+	static unsigned int id = 0;
+
+	if (browsing == NULL)
+		return 0;
+
+	if (browsing->handlers != NULL)
+		return 0;
+
+	handler = g_new(struct avctp_browsing_pdu_handler, 1);
+	handler->cb = cb;
+	handler->user_data = user_data;
+	handler->id = ++id;
+	handler->destroy = destroy;
+
+	browsing->handlers = g_slist_append(browsing->handlers, handler);
+
+	return handler->id;
+}
+
+bool avctp_unregister_pdu_handler(struct avctp *session, unsigned int id)
+{
+	struct avctp_channel *control = session->control;
+	GSList *l;
+
+	if (!control)
+		return false;
+
+	for (l = control->handlers; l; l = g_slist_next(l)) {
+		struct avctp_pdu_handler *handler = l->data;
+
+		if (handler->id != id)
+			continue;
+
+		control->handlers = g_slist_remove(control->handlers, handler);
+		g_free(handler);
+		return true;
+	}
+
+	return false;
+}
+
+bool avctp_unregister_browsing_pdu_handler(struct avctp *session,
+							unsigned int id)
+{
+	struct avctp_channel *browsing = session->browsing;
+	GSList *l;
+
+	if (browsing == NULL)
+		return false;
+
+	for (l = browsing->handlers; l; l = g_slist_next(l)) {
+		struct avctp_browsing_pdu_handler *handler = l->data;
+
+		if (handler->id != id)
+			continue;
+
+		browsing->handlers = g_slist_remove(browsing->handlers,
+								handler);
+		g_free(handler);
+		return true;
+	}
+
+	return false;
+}
+
+struct avctp *avctp_new(int fd, size_t imtu, size_t omtu, uint16_t version)
+{
+	struct avctp *session;
+	struct avctp_channel *control;
+	GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+	session = g_new0(struct avctp, 1);
+	session->version = version;
+
+	control = avctp_channel_create(session, fd, imtu, omtu, NULL);
+	if (!control) {
+		g_free(session);
+		return NULL;
+	}
+
+	session->uinput = -1;
+	session->control = control;
+	session->passthrough_id = avctp_register_pdu_handler(session,
+						AVC_OP_PASSTHROUGH,
+						handle_panel_passthrough,
+						NULL);
+	session->unit_id = avctp_register_pdu_handler(session,
+						AVC_OP_UNITINFO,
+						handle_unit_info,
+						NULL);
+	session->subunit_id = avctp_register_pdu_handler(session,
+						AVC_OP_SUBUNITINFO,
+						handle_subunit_info,
+						NULL);
+
+	control->watch = g_io_add_watch(session->control->io, cond,
+						(GIOFunc) session_cb, session);
+
+	return avctp_ref(session);
+}
+
+int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu,
+								size_t omtu)
+{
+	struct avctp_channel *browsing;
+	GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+	if (session->browsing)
+		return -EISCONN;
+
+	browsing = avctp_channel_create(session, fd, imtu, omtu,
+						avctp_destroy_browsing);
+	if (!browsing)
+		return -EINVAL;
+
+	session->browsing = browsing;
+	browsing->watch = g_io_add_watch(session->browsing->io, cond,
+					(GIOFunc) session_browsing_cb, session);
+
+	return 0;
+}
+
+void avctp_set_destroy_cb(struct avctp *session, avctp_destroy_cb_t cb,
+							void *user_data)
+{
+	session->destroy = cb;
+	session->data = user_data;
+}
+
+void avctp_shutdown(struct avctp *session)
+{
+	if (!session)
+		return;
+
+	avctp_unref(session);
+}
diff --git a/repo/android/avctp.h b/repo/android/avctp.h
new file mode 100644
index 0000000..f0da2b3
--- /dev/null
+++ b/repo/android/avctp.h
@@ -0,0 +1,183 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-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
+ *
+ */
+
+#define AVCTP_CONTROL_PSM		23
+#define AVCTP_BROWSING_PSM		27
+
+#define AVCTP_HEADER_LENGTH		3
+#define AVC_HEADER_LENGTH		3
+
+#define AVC_DATA_OFFSET			AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH
+#define AVC_DATA_MTU			512
+
+/* ctype entries */
+#define AVC_CTYPE_CONTROL		0x0
+#define AVC_CTYPE_STATUS		0x1
+#define AVC_CTYPE_NOTIFY		0x3
+#define AVC_CTYPE_NOT_IMPLEMENTED	0x8
+#define AVC_CTYPE_ACCEPTED		0x9
+#define AVC_CTYPE_REJECTED		0xA
+#define AVC_CTYPE_STABLE		0xC
+#define AVC_CTYPE_CHANGED		0xD
+#define AVC_CTYPE_INTERIM		0xF
+
+/* opcodes */
+#define AVC_OP_VENDORDEP		0x00
+#define AVC_OP_UNITINFO			0x30
+#define AVC_OP_SUBUNITINFO		0x31
+#define AVC_OP_PASSTHROUGH		0x7c
+
+/* subunits of interest */
+#define AVC_SUBUNIT_PANEL		0x09
+
+/* operands in passthrough commands */
+#define AVC_SELECT			0x00
+#define AVC_UP				0x01
+#define AVC_DOWN			0x02
+#define AVC_LEFT			0x03
+#define AVC_RIGHT			0x04
+#define AVC_ROOT_MENU			0x09
+#define AVC_CONTENTS_MENU		0x0b
+#define AVC_FAVORITE_MENU		0x0c
+#define AVC_EXIT			0x0d
+#define AVC_ON_DEMAND_MENU		0x0e
+#define AVC_APPS_MENU			0x0f
+#define AVC_0				0x20
+#define AVC_1				0x21
+#define AVC_2				0x22
+#define AVC_3				0x23
+#define AVC_4				0x24
+#define AVC_5				0x25
+#define AVC_6				0x26
+#define AVC_7				0x27
+#define AVC_8				0x28
+#define AVC_9				0x29
+#define AVC_DOT				0x2a
+#define AVC_ENTER			0x2b
+#define AVC_CHANNEL_UP			0x30
+#define AVC_CHANNEL_DOWN		0x31
+#define AVC_CHANNEL_PREVIOUS		0x32
+#define AVC_INPUT_SELECT		0x34
+#define AVC_INFO			0x35
+#define AVC_HELP			0x36
+#define AVC_PAGE_UP			0x37
+#define AVC_PAGE_DOWN			0x38
+#define AVC_LOCK			0x3a
+#define AVC_POWER			0x40
+#define AVC_VOLUME_UP			0x41
+#define AVC_VOLUME_DOWN			0x42
+#define AVC_MUTE			0x43
+#define AVC_PLAY			0x44
+#define AVC_STOP			0x45
+#define AVC_PAUSE			0x46
+#define AVC_RECORD			0x47
+#define AVC_REWIND			0x48
+#define AVC_FAST_FORWARD		0x49
+#define AVC_EJECT			0x4a
+#define AVC_FORWARD			0x4b
+#define AVC_BACKWARD			0x4c
+#define AVC_LIST			0x4d
+#define AVC_F1				0x71
+#define AVC_F2				0x72
+#define AVC_F3				0x73
+#define AVC_F4				0x74
+#define AVC_F5				0x75
+#define AVC_F6				0x76
+#define AVC_F7				0x77
+#define AVC_F8				0x78
+#define AVC_F9				0x79
+#define AVC_RED				0x7a
+#define AVC_GREEN			0x7b
+#define AVC_BLUE			0x7c
+#define AVC_YELLOW			0x7c
+
+#define AVC_VENDOR_UNIQUE		0x7e
+
+#define AVC_VENDOR_NEXT_GROUP		0x00
+#define AVC_VENDOR_PREV_GROUP		0x01
+
+struct avctp;
+
+typedef bool (*avctp_passthrough_cb) (struct avctp *session,
+					uint8_t op, bool pressed,
+					void *user_data);
+typedef ssize_t (*avctp_control_pdu_cb) (struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data);
+typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
+					uint8_t subunit, uint8_t *operands,
+					size_t operand_count, void *user_data);
+typedef gboolean (*avctp_browsing_rsp_cb) (struct avctp *session,
+					uint8_t *operands, size_t operand_count,
+					void *user_data);
+typedef ssize_t (*avctp_browsing_pdu_cb) (struct avctp *session,
+					uint8_t transaction,
+					uint8_t *operands, size_t operand_count,
+					void *user_data);
+
+typedef void (*avctp_destroy_cb_t) (void *user_data);
+
+struct avctp *avctp_new(int fd, size_t imtu, size_t omtu, uint16_t version);
+void avctp_set_destroy_cb(struct avctp *session, avctp_destroy_cb_t cb,
+							void *user_data);
+
+int avctp_init_uinput(struct avctp *session, const char *name,
+							const char *address);
+int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu,
+								size_t omtu);
+
+void avctp_shutdown(struct avctp *session);
+
+unsigned int avctp_register_passthrough_handler(struct avctp *session,
+						avctp_passthrough_cb cb,
+						void *user_data);
+bool avctp_unregister_passthrough_handler(struct avctp *session,
+							unsigned int id);
+
+unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode,
+						avctp_control_pdu_cb cb,
+						void *user_data);
+bool avctp_unregister_pdu_handler(struct avctp *session, unsigned int id);
+
+unsigned int avctp_register_browsing_pdu_handler(struct avctp *session,
+						avctp_browsing_pdu_cb cb,
+						void *user_data,
+						avctp_destroy_cb_t destroy);
+bool avctp_unregister_browsing_pdu_handler(struct avctp *session,
+							unsigned int id);
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op, uint8_t *params,
+							size_t params_len);
+int avctp_send_vendor(struct avctp *session, uint8_t transaction,
+				uint8_t code, uint8_t subunit,
+				const struct iovec *iov, int iov_cnt);
+int avctp_send_vendor_req(struct avctp *session, uint8_t code, uint8_t subunit,
+					const struct iovec *iov, int iov_cnt,
+					avctp_rsp_cb func, void *user_data);
+int avctp_send_browsing(struct avctp *session, uint8_t transaction,
+					const struct iovec *iov, int iov_cnt);
+int avctp_send_browsing_req(struct avctp *session,
+				const struct iovec *iov, int iov_cnt,
+				avctp_browsing_rsp_cb func, void *user_data);
diff --git a/repo/android/avdtp.c b/repo/android/avdtp.c
new file mode 100644
index 0000000..bab305b
--- /dev/null
+++ b/repo/android/avdtp.c
@@ -0,0 +1,3484 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-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 <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "src/log.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "avdtp.h"
+#include "../profiles/audio/a2dp-codecs.h"
+
+#define MAX_SEID 0x3E
+static unsigned int seids;
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#define AVDTP_DISCOVER				0x01
+#define AVDTP_GET_CAPABILITIES			0x02
+#define AVDTP_SET_CONFIGURATION			0x03
+#define AVDTP_GET_CONFIGURATION			0x04
+#define AVDTP_RECONFIGURE			0x05
+#define AVDTP_OPEN				0x06
+#define AVDTP_START				0x07
+#define AVDTP_CLOSE				0x08
+#define AVDTP_SUSPEND				0x09
+#define AVDTP_ABORT				0x0A
+#define AVDTP_SECURITY_CONTROL			0x0B
+#define AVDTP_GET_ALL_CAPABILITIES		0x0C
+#define AVDTP_DELAY_REPORT			0x0D
+
+#define AVDTP_PKT_TYPE_SINGLE			0x00
+#define AVDTP_PKT_TYPE_START			0x01
+#define AVDTP_PKT_TYPE_CONTINUE			0x02
+#define AVDTP_PKT_TYPE_END			0x03
+
+#define AVDTP_MSG_TYPE_COMMAND			0x00
+#define AVDTP_MSG_TYPE_GEN_REJECT		0x01
+#define AVDTP_MSG_TYPE_ACCEPT			0x02
+#define AVDTP_MSG_TYPE_REJECT			0x03
+
+#define REQ_TIMEOUT 6
+#define ABORT_TIMEOUT 2
+#define DISCONNECT_TIMEOUT 1
+#define START_TIMEOUT 1
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_common_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint8_t signal_id:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint8_t no_of_packets;
+	uint8_t signal_id:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct seid_info {
+	uint8_t rfa0:1;
+	uint8_t inuse:1;
+	uint8_t seid:6;
+	uint8_t rfa2:3;
+	uint8_t type:1;
+	uint8_t media_type:4;
+} __attribute__ ((packed));
+
+struct seid {
+	uint8_t rfa0:2;
+	uint8_t seid:6;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_common_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+	uint8_t rfa0:2;
+	uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+	uint8_t no_of_packets;
+	uint8_t rfa0:2;
+	uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct seid_info {
+	uint8_t seid:6;
+	uint8_t inuse:1;
+	uint8_t rfa0:1;
+	uint8_t media_type:4;
+	uint8_t type:1;
+	uint8_t rfa2:3;
+} __attribute__ ((packed));
+
+struct seid {
+	uint8_t seid:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+/* packets */
+
+struct discover_resp {
+	struct seid_info seps[0];
+} __attribute__ ((packed));
+
+struct getcap_resp {
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct start_req {
+	struct seid first_seid;
+	struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct suspend_req {
+	struct seid first_seid;
+	struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct seid_rej {
+	uint8_t error;
+} __attribute__ ((packed));
+
+struct conf_rej {
+	uint8_t category;
+	uint8_t error;
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct seid_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+} __attribute__ ((packed));
+
+struct setconf_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+	uint8_t rfa1:2;
+	uint8_t int_seid:6;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+	uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+
+	uint8_t serv_cap;
+	uint8_t serv_cap_len;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct delay_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+	uint16_t delay;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct seid_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct setconf_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+	uint8_t int_seid:6;
+	uint8_t rfa1:2;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+	uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+
+	uint8_t serv_cap;
+	uint8_t serv_cap_len;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct delay_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+	uint16_t delay;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct in_buf {
+	gboolean active;
+	int no_of_packets;
+	uint8_t transaction;
+	uint8_t message_type;
+	uint8_t signal_id;
+	uint8_t buf[1024];
+	uint8_t data_size;
+};
+
+struct pending_req {
+	uint8_t transaction;
+	uint8_t signal_id;
+	void *data;
+	size_t data_size;
+	struct avdtp_stream *stream; /* Set if the request targeted a stream */
+	guint timeout;
+	gboolean collided;
+};
+
+struct avdtp_remote_sep {
+	uint8_t seid;
+	uint8_t type;
+	uint8_t media_type;
+	struct avdtp_service_capability *codec;
+	gboolean delay_reporting;
+	GSList *caps; /* of type struct avdtp_service_capability */
+	struct avdtp_stream *stream;
+};
+
+struct avdtp_local_sep {
+	avdtp_state_t state;
+	struct avdtp_stream *stream;
+	struct seid_info info;
+	uint8_t codec;
+	uint32_t vndcodec_vendor;
+	uint16_t vndcodec_codec;
+	gboolean delay_reporting;
+	GSList *caps;
+	struct avdtp_sep_ind *ind;
+	struct avdtp_sep_cfm *cfm;
+	void *user_data;
+};
+
+struct stream_callback {
+	avdtp_stream_state_cb cb;
+	void *user_data;
+	unsigned int id;
+};
+
+struct discover_callback {
+	unsigned int id;
+	avdtp_discover_cb_t cb;
+	void *user_data;
+};
+
+struct disconnect_callback {
+	unsigned int id;
+	avdtp_disconnect_cb_t cb;
+	void *user_data;
+};
+
+struct avdtp_stream {
+	GIOChannel *io;
+	uint16_t imtu;
+	uint16_t omtu;
+	struct avdtp *session;
+	struct avdtp_local_sep *lsep;
+	uint8_t rseid;
+	GSList *caps;
+	GSList *callbacks;
+	struct avdtp_service_capability *codec;
+	guint io_id;		/* Transport GSource ID */
+	guint timer;		/* Waiting for other side to close or open
+				 * the transport channel */
+	gboolean open_acp;	/* If we are in ACT role for Open */
+	gboolean close_int;	/* If we are in INT role for Close */
+	gboolean abort_int;	/* If we are in INT role for Abort */
+	guint start_timer;	/* Wait START command timer */
+	gboolean delay_reporting;
+	uint16_t delay;		/* AVDTP 1.3 Delay Reporting feature */
+	gboolean starting;	/* only valid while sep state == OPEN */
+};
+
+/* Structure describing an AVDTP connection between two devices */
+
+struct avdtp {
+	unsigned int ref;
+
+	uint16_t version;
+
+	guint auth_id;
+
+	GIOChannel *io;
+	guint io_id;
+
+	GSList *seps; /* Elements of type struct avdtp_remote_sep * */
+	struct queue *lseps; /* Elements of type struct avdtp_local_sep * */
+
+	GSList *streams; /* Elements of type struct avdtp_stream * */
+
+	GSList *req_queue; /* Elements of type struct pending_req * */
+	GSList *prio_queue; /* Same as req_queue but is processed before it */
+
+	struct avdtp_stream *pending_open;
+
+	uint16_t imtu;
+	uint16_t omtu;
+
+	struct in_buf in;
+
+	char *buf;
+
+	struct discover_callback *discover;
+	struct pending_req *req;
+
+	GSList *disconnect;
+
+	bool shutdown;
+};
+
+static int send_request(struct avdtp *session, gboolean priority,
+			struct avdtp_stream *stream, uint8_t signal_id,
+			void *buffer, size_t size);
+static gboolean avdtp_parse_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size);
+static gboolean avdtp_parse_rej(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size);
+static int process_queue(struct avdtp *session);
+static void avdtp_sep_set_state(struct avdtp *session,
+				struct avdtp_local_sep *sep,
+				avdtp_state_t state);
+
+static const char *avdtp_statestr(avdtp_state_t state)
+{
+	switch (state) {
+	case AVDTP_STATE_IDLE:
+		return "IDLE";
+	case AVDTP_STATE_CONFIGURED:
+		return "CONFIGURED";
+	case AVDTP_STATE_OPEN:
+		return "OPEN";
+	case AVDTP_STATE_STREAMING:
+		return "STREAMING";
+	case AVDTP_STATE_CLOSING:
+		return "CLOSING";
+	case AVDTP_STATE_ABORTING:
+		return "ABORTING";
+	default:
+		return "<unknown state>";
+	}
+}
+
+static gboolean try_send(int sk, void *data, size_t len)
+{
+	int err;
+
+	do {
+		err = send(sk, data, len, 0);
+	} while (err < 0 && errno == EINTR);
+
+	if (err < 0) {
+		error("send: %s (%d)", strerror(errno), errno);
+		return FALSE;
+	} else if ((size_t) err != len) {
+		error("try_send: complete buffer not sent (%d/%zu bytes)",
+								err, len);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean avdtp_send(struct avdtp *session, uint8_t transaction,
+				uint8_t message_type, uint8_t signal_id,
+				void *data, size_t len)
+{
+	unsigned int cont_fragments, sent;
+	struct avdtp_start_header start;
+	struct avdtp_continue_header cont;
+	int sock;
+
+	if (session->io == NULL) {
+		error("avdtp_send: session is closed");
+		return FALSE;
+	}
+
+	sock = g_io_channel_unix_get_fd(session->io);
+
+	/* Single packet - no fragmentation */
+	if (sizeof(struct avdtp_single_header) + len <= session->omtu) {
+		struct avdtp_single_header single;
+
+		memset(&single, 0, sizeof(single));
+
+		single.transaction = transaction;
+		single.packet_type = AVDTP_PKT_TYPE_SINGLE;
+		single.message_type = message_type;
+		single.signal_id = signal_id;
+
+		memcpy(session->buf, &single, sizeof(single));
+		memcpy(session->buf + sizeof(single), data, len);
+
+		return try_send(sock, session->buf, sizeof(single) + len);
+	}
+
+	/* Check if there is enough space to start packet */
+	if (session->omtu < sizeof(start)) {
+		error("No enough space to fragment packet");
+		return FALSE;
+	}
+
+	/* Count the number of needed fragments */
+	cont_fragments = (len - (session->omtu - sizeof(start))) /
+					(session->omtu - sizeof(cont)) + 1;
+
+	DBG("%zu bytes split into %d fragments", len, cont_fragments + 1);
+
+	/* Send the start packet */
+	memset(&start, 0, sizeof(start));
+	start.transaction = transaction;
+	start.packet_type = AVDTP_PKT_TYPE_START;
+	start.message_type = message_type;
+	start.no_of_packets = cont_fragments + 1;
+	start.signal_id = signal_id;
+
+	memcpy(session->buf, &start, sizeof(start));
+	memcpy(session->buf + sizeof(start), data,
+					session->omtu - sizeof(start));
+
+	if (!try_send(sock, session->buf, session->omtu))
+		return FALSE;
+
+	DBG("first packet with %zu bytes sent", session->omtu - sizeof(start));
+
+	sent = session->omtu - sizeof(start);
+
+	/* Send the continue fragments and the end packet */
+	while (sent < len) {
+		int left, to_copy;
+
+		left = len - sent;
+		if (left + sizeof(cont) > session->omtu) {
+			cont.packet_type = AVDTP_PKT_TYPE_CONTINUE;
+			to_copy = session->omtu - sizeof(cont);
+			DBG("sending continue with %d bytes", to_copy);
+		} else {
+			cont.packet_type = AVDTP_PKT_TYPE_END;
+			to_copy = left;
+			DBG("sending end with %d bytes", to_copy);
+		}
+
+		cont.transaction = transaction;
+		cont.message_type = message_type;
+
+		memcpy(session->buf, &cont, sizeof(cont));
+		memcpy(session->buf + sizeof(cont), data + sent, to_copy);
+
+		if (!try_send(sock, session->buf, to_copy + sizeof(cont)))
+			return FALSE;
+
+		sent += to_copy;
+	}
+
+	return TRUE;
+}
+
+static void pending_req_free(void *data)
+{
+	struct pending_req *req = data;
+
+	if (req->timeout)
+		g_source_remove(req->timeout);
+	g_free(req->data);
+	g_free(req);
+}
+
+static void close_stream(struct avdtp_stream *stream)
+{
+	int sock;
+
+	if (stream->io == NULL)
+		return;
+
+	sock = g_io_channel_unix_get_fd(stream->io);
+
+	shutdown(sock, SHUT_RDWR);
+
+	g_io_channel_shutdown(stream->io, FALSE, NULL);
+
+	g_io_channel_unref(stream->io);
+	stream->io = NULL;
+}
+
+static gboolean stream_close_timeout(gpointer user_data)
+{
+	struct avdtp_stream *stream = user_data;
+
+	DBG("Timed out waiting for peer to close the transport channel");
+
+	stream->timer = 0;
+
+	close_stream(stream);
+
+	return FALSE;
+}
+
+static gboolean stream_open_timeout(gpointer user_data)
+{
+	struct avdtp_stream *stream = user_data;
+
+	DBG("Timed out waiting for peer to open the transport channel");
+
+	stream->timer = 0;
+
+	stream->session->pending_open = NULL;
+
+	avdtp_abort(stream->session, stream);
+
+	return FALSE;
+}
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t category, int id)
+{
+	err->category = category;
+
+	if (category == AVDTP_ERRNO)
+		err->err.posix_errno = id;
+	else
+		err->err.error_code = id;
+}
+
+uint8_t avdtp_error_category(struct avdtp_error *err)
+{
+	return err->category;
+}
+
+int avdtp_error_error_code(struct avdtp_error *err)
+{
+	assert(err->category != AVDTP_ERRNO);
+	return err->err.error_code;
+}
+
+int avdtp_error_posix_errno(struct avdtp_error *err)
+{
+	assert(err->category == AVDTP_ERRNO);
+	return err->err.posix_errno;
+}
+
+static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session,
+							uint8_t rseid)
+{
+	GSList *l;
+
+	for (l = session->streams; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_stream *stream = l->data;
+
+		if (stream->rseid == rseid)
+			return stream;
+	}
+
+	return NULL;
+}
+
+static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid)
+{
+	GSList *l;
+
+	for (l = seps; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_remote_sep *sep = l->data;
+
+		if (sep->seid == seid)
+			return sep;
+	}
+
+	return NULL;
+}
+
+static void stream_free(void *data)
+{
+	struct avdtp_stream *stream = data;
+	struct avdtp_remote_sep *rsep;
+
+	stream->lsep->info.inuse = 0;
+	stream->lsep->stream = NULL;
+
+	rsep = find_remote_sep(stream->session->seps, stream->rseid);
+	if (rsep)
+		rsep->stream = NULL;
+
+	if (stream->timer)
+		g_source_remove(stream->timer);
+
+	if (stream->start_timer > 0)
+		g_source_remove(stream->start_timer);
+
+	if (stream->io)
+		close_stream(stream);
+
+	if (stream->io_id)
+		g_source_remove(stream->io_id);
+
+	g_slist_free_full(stream->callbacks, g_free);
+	g_slist_free_full(stream->caps, g_free);
+
+	g_free(stream);
+}
+
+static gboolean transport_cb(GIOChannel *chan, GIOCondition cond,
+				gpointer data)
+{
+	struct avdtp_stream *stream = data;
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	if (stream->close_int && sep->cfm && sep->cfm->close)
+		sep->cfm->close(stream->session, sep, stream, NULL,
+				sep->user_data);
+
+	if (!(cond & G_IO_NVAL))
+		close_stream(stream);
+
+	stream->io_id = 0;
+
+	if (!stream->abort_int)
+		avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE);
+
+	return FALSE;
+}
+
+static void handle_transport_connect(struct avdtp *session, GIOChannel *io,
+					uint16_t imtu, uint16_t omtu)
+{
+	struct avdtp_stream *stream = session->pending_open;
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	session->pending_open = NULL;
+
+	if (stream->timer) {
+		g_source_remove(stream->timer);
+		stream->timer = 0;
+	}
+
+	if (io == NULL)
+		return;
+
+	if (stream->io == NULL)
+		stream->io = g_io_channel_ref(io);
+
+	stream->omtu = omtu;
+	stream->imtu = imtu;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+	stream->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+					(GIOFunc) transport_cb, stream);
+}
+
+static int pending_req_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct pending_req *req = a;
+	const struct avdtp_stream *stream = b;
+
+	if (req->stream == stream)
+		return 0;
+
+	return -1;
+}
+
+static void cleanup_queue(struct avdtp *session, struct avdtp_stream *stream)
+{
+	GSList *l;
+	struct pending_req *req;
+
+	while ((l = g_slist_find_custom(session->prio_queue, stream,
+							pending_req_cmp))) {
+		req = l->data;
+		pending_req_free(req);
+		session->prio_queue = g_slist_remove(session->prio_queue, req);
+	}
+
+	while ((l = g_slist_find_custom(session->req_queue, stream,
+							pending_req_cmp))) {
+		req = l->data;
+		pending_req_free(req);
+		session->req_queue = g_slist_remove(session->req_queue, req);
+	}
+}
+
+static void handle_unanswered_req(struct avdtp *session,
+						struct avdtp_stream *stream)
+{
+	struct pending_req *req;
+	struct avdtp_local_sep *lsep;
+	struct avdtp_error err;
+
+	if (!session->req->timeout)
+		/* Request is in process */
+		return;
+
+	if (session->req->signal_id == AVDTP_ABORT) {
+		/* Avoid freeing the Abort request here */
+		DBG("handle_unanswered_req: Abort req, returning");
+		session->req->stream = NULL;
+		return;
+	}
+
+	req = session->req;
+	session->req = NULL;
+
+	avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+
+	lsep = stream->lsep;
+
+	switch (req->signal_id) {
+	case AVDTP_RECONFIGURE:
+		error("No reply to Reconfigure request");
+		if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+			lsep->cfm->reconfigure(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_OPEN:
+		error("No reply to Open request");
+		if (lsep && lsep->cfm && lsep->cfm->open)
+			lsep->cfm->open(session, lsep, stream, &err,
+					lsep->user_data);
+		break;
+	case AVDTP_START:
+		error("No reply to Start request");
+		if (lsep && lsep->cfm && lsep->cfm->start)
+			lsep->cfm->start(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_SUSPEND:
+		error("No reply to Suspend request");
+		if (lsep && lsep->cfm && lsep->cfm->suspend)
+			lsep->cfm->suspend(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_CLOSE:
+		error("No reply to Close request");
+		if (lsep && lsep->cfm && lsep->cfm->close)
+			lsep->cfm->close(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_SET_CONFIGURATION:
+		error("No reply to SetConfiguration request");
+		if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+			lsep->cfm->set_configuration(session, lsep, stream,
+							&err, lsep->user_data);
+	}
+
+	pending_req_free(req);
+}
+
+static void avdtp_sep_set_state(struct avdtp *session,
+				struct avdtp_local_sep *sep,
+				avdtp_state_t state)
+{
+	struct avdtp_stream *stream = sep->stream;
+	avdtp_state_t old_state;
+	struct avdtp_error err, *err_ptr = NULL;
+	GSList *l;
+
+	if (!stream) {
+		error("Error changing sep state: stream not available");
+		return;
+	}
+
+	if (sep->state == state) {
+		avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+		DBG("stream state change failed: %s", avdtp_strerror(&err));
+		err_ptr = &err;
+	} else {
+		err_ptr = NULL;
+		DBG("stream state changed: %s -> %s",
+				avdtp_statestr(sep->state),
+				avdtp_statestr(state));
+	}
+
+	old_state = sep->state;
+	sep->state = state;
+
+	switch (state) {
+	case AVDTP_STATE_CONFIGURED:
+		if (sep->info.type == AVDTP_SEP_TYPE_SINK)
+			avdtp_delay_report(session, stream, stream->delay);
+		break;
+	case AVDTP_STATE_OPEN:
+		stream->starting = FALSE;
+		break;
+	case AVDTP_STATE_STREAMING:
+		if (stream->start_timer) {
+			g_source_remove(stream->start_timer);
+			stream->start_timer = 0;
+		}
+		stream->open_acp = FALSE;
+		break;
+	case AVDTP_STATE_CLOSING:
+	case AVDTP_STATE_ABORTING:
+		if (stream->start_timer) {
+			g_source_remove(stream->start_timer);
+			stream->start_timer = 0;
+		}
+		break;
+	case AVDTP_STATE_IDLE:
+		if (stream->start_timer) {
+			g_source_remove(stream->start_timer);
+			stream->start_timer = 0;
+		}
+		if (session->pending_open == stream)
+			handle_transport_connect(session, NULL, 0, 0);
+		if (session->req && session->req->stream == stream)
+			handle_unanswered_req(session, stream);
+		/* Remove pending commands for this stream from the queue */
+		cleanup_queue(session, stream);
+		break;
+	default:
+		break;
+	}
+
+	l = stream->callbacks;
+	while (l != NULL) {
+		struct stream_callback *cb = l->data;
+		l = g_slist_next(l);
+		cb->cb(stream, old_state, state, err_ptr, cb->user_data);
+	}
+
+	if (state == AVDTP_STATE_IDLE &&
+				g_slist_find(session->streams, stream)) {
+		session->streams = g_slist_remove(session->streams, stream);
+		stream_free(stream);
+	}
+
+	if (session->io && session->shutdown && session->streams == NULL) {
+		int sock = g_io_channel_unix_get_fd(session->io);
+		shutdown(sock, SHUT_RDWR);
+	}
+}
+
+static void finalize_discovery(struct avdtp *session, int err)
+{
+	struct discover_callback *discover = session->discover;
+	struct avdtp_error avdtp_err;
+
+	if (!discover)
+		return;
+
+	session->discover = NULL;
+
+	avdtp_error_init(&avdtp_err, AVDTP_ERRNO, err);
+
+	if (discover->id > 0)
+		g_source_remove(discover->id);
+
+	if (discover->cb)
+		discover->cb(session, session->seps, err ? &avdtp_err : NULL,
+							discover->user_data);
+	g_free(discover);
+}
+
+static void release_stream(struct avdtp_stream *stream, struct avdtp *session)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	if (sep->cfm && sep->cfm->abort &&
+				(sep->state != AVDTP_STATE_ABORTING ||
+							stream->abort_int))
+		sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+}
+
+static void sep_free(gpointer data)
+{
+	struct avdtp_remote_sep *sep = data;
+
+	g_slist_free_full(sep->caps, g_free);
+	g_free(sep);
+}
+
+static void avdtp_free(void *data)
+{
+	struct avdtp *session = data;
+
+	DBG("%p", session);
+
+	g_slist_free_full(session->streams, stream_free);
+
+	if (session->io) {
+		g_io_channel_shutdown(session->io, FALSE, NULL);
+		g_io_channel_unref(session->io);
+	}
+
+	if (session->io_id) {
+		g_source_remove(session->io_id);
+		session->io_id = 0;
+	}
+
+	if (session->req)
+		pending_req_free(session->req);
+
+	g_slist_free_full(session->req_queue, pending_req_free);
+	g_slist_free_full(session->prio_queue, pending_req_free);
+	g_slist_free_full(session->seps, sep_free);
+	g_slist_free_full(session->disconnect, g_free);
+
+	/* Free copy of the SEP list */
+	session->lseps = NULL;
+
+	g_free(session->buf);
+
+	g_free(session);
+}
+
+static void process_disconnect(void *data)
+{
+	struct disconnect_callback *callback = data;
+
+	callback->cb(callback->user_data);
+
+	g_free(callback);
+}
+
+static void connection_lost(struct avdtp *session, int err)
+{
+	DBG("Disconnected: %s (%d)", strerror(err), err);
+
+	g_slist_foreach(session->streams, (GFunc) release_stream, session);
+	session->streams = NULL;
+
+	avdtp_ref(session);
+
+	finalize_discovery(session, err);
+
+	g_slist_free_full(session->disconnect, process_disconnect);
+	session->disconnect = NULL;
+
+	avdtp_unref(session);
+}
+
+void avdtp_unref(struct avdtp *session)
+{
+	if (!session)
+		return;
+
+	session->ref--;
+
+	DBG("%p: ref=%d", session, session->ref);
+
+	if (session->ref > 0)
+		return;
+
+	finalize_discovery(session, ECONNABORTED);
+
+	avdtp_free(session);
+}
+
+struct avdtp *avdtp_ref(struct avdtp *session)
+{
+	session->ref++;
+
+	DBG("%p: ref=%d", session, session->ref);
+
+	return session;
+}
+
+static bool match_by_seid(const void *data, const void *user_data)
+{
+	const struct avdtp_local_sep *sep = data;
+	uint8_t seid = PTR_TO_UINT(user_data);
+
+	return sep->info.seid == seid;
+}
+
+static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp *session,
+								uint8_t seid)
+{
+	return queue_find(session->lseps, match_by_seid, INT_TO_PTR(seid));
+}
+
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+						struct avdtp_local_sep *lsep)
+{
+	GSList *l;
+
+	if (lsep->info.inuse)
+		return NULL;
+
+	for (l = session->seps; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_remote_sep *sep = l->data;
+		struct avdtp_service_capability *cap;
+		struct avdtp_media_codec_capability *codec_data;
+
+		/* Type must be different: source <-> sink */
+		if (sep->type == lsep->info.type)
+			continue;
+
+		if (sep->media_type != lsep->info.media_type)
+			continue;
+
+		if (!sep->codec)
+			continue;
+
+		cap = sep->codec;
+		codec_data = (void *) cap->data;
+
+		if (codec_data->media_codec_type != lsep->codec)
+			continue;
+
+		/* FIXME: Add Vendor Specific Codec match to SEP callback */
+		if (lsep->codec == A2DP_CODEC_VENDOR) {
+			a2dp_vendor_codec_t *vndcodec =
+						(void *) codec_data->data;
+
+			if (btohl(vndcodec->vendor_id) != lsep->vndcodec_vendor)
+				continue;
+
+			if (btohs(vndcodec->codec_id) != lsep->vndcodec_codec)
+				continue;
+		}
+
+		if (sep->stream == NULL)
+			return sep;
+	}
+
+	return NULL;
+}
+
+static GSList *caps_to_list(uint8_t *data, int size,
+				struct avdtp_service_capability **codec,
+				gboolean *delay_reporting)
+{
+	GSList *caps;
+	int processed;
+
+	if (delay_reporting)
+		*delay_reporting = FALSE;
+
+	for (processed = 0, caps = NULL; processed + 2 <= size;) {
+		struct avdtp_service_capability *cap;
+		uint8_t length, category;
+
+		category = data[0];
+		length = data[1];
+
+		if (processed + 2 + length > size) {
+			error("Invalid capability data in getcap resp");
+			break;
+		}
+
+		cap = g_malloc(sizeof(struct avdtp_service_capability) +
+					length);
+		memcpy(cap, data, 2 + length);
+
+		processed += 2 + length;
+		data += 2 + length;
+
+		caps = g_slist_append(caps, cap);
+
+		if (category == AVDTP_MEDIA_CODEC &&
+				length >=
+				sizeof(struct avdtp_media_codec_capability))
+			*codec = cap;
+		else if (category == AVDTP_DELAY_REPORTING && delay_reporting)
+			*delay_reporting = TRUE;
+	}
+
+	return caps;
+}
+
+static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction,
+							uint8_t signal_id)
+{
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_GEN_REJECT,
+							signal_id, NULL, 0);
+}
+
+static void copy_seps(void *data, void *user_data)
+{
+	struct avdtp_local_sep *sep = data;
+	struct seid_info **p = user_data;
+
+	memcpy(*p, &sep->info, sizeof(struct seid_info));
+	*p = *p + 1;
+}
+
+static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
+							void *buf, int size)
+{
+	unsigned int rsp_size, sep_count;
+	struct seid_info *seps, *p;
+	gboolean ret;
+
+	sep_count = queue_length(session->lseps);
+
+	if (sep_count == 0) {
+		uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND;
+		return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_DISCOVER, &err, sizeof(err));
+	}
+
+	rsp_size = sep_count * sizeof(struct seid_info);
+
+	seps = g_new0(struct seid_info, sep_count);
+	p = seps;
+
+	queue_foreach(session->lseps, copy_seps, &p);
+
+	ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+				AVDTP_DISCOVER, seps, rsp_size);
+	g_free(seps);
+
+	return ret;
+}
+
+static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, unsigned int size,
+					gboolean get_all)
+{
+	GSList *l, *caps;
+	struct avdtp_local_sep *sep = NULL;
+	unsigned int rsp_size;
+	uint8_t err, buf[1024], *ptr = buf;
+	uint8_t cmd;
+
+	cmd = get_all ? AVDTP_GET_ALL_CAPABILITIES : AVDTP_GET_CAPABILITIES;
+
+	if (size < sizeof(struct seid_req)) {
+		err = AVDTP_BAD_LENGTH;
+		goto failed;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (!sep->ind->get_capability(session, sep, &caps, &err,
+							sep->user_data))
+		goto failed;
+
+	for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_service_capability *cap = l->data;
+
+		if (rsp_size + cap->length + 2 > sizeof(buf))
+			break;
+
+		memcpy(ptr, cap, cap->length + 2);
+		rsp_size += cap->length + 2;
+		ptr += cap->length + 2;
+
+		g_free(cap);
+	}
+
+	if (get_all && sep->delay_reporting) {
+		ptr[0] = AVDTP_DELAY_REPORTING;
+		ptr[1] = 0x00;
+		rsp_size += 2;
+	}
+
+	g_slist_free(caps);
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd,
+								buf, rsp_size);
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, cmd,
+							&err, sizeof(err));
+}
+
+static void setconf_cb(struct avdtp *session, struct avdtp_stream *stream,
+						struct avdtp_error *err)
+{
+	struct conf_rej rej;
+	struct avdtp_local_sep *sep;
+
+	if (err != NULL) {
+		rej.error = AVDTP_UNSUPPORTED_CONFIGURATION;
+		rej.category = err->err.error_code;
+		avdtp_send(session, session->in.transaction,
+				AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION,
+				&rej, sizeof(rej));
+		return;
+	}
+
+	if (!avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT,
+					AVDTP_SET_CONFIGURATION, NULL, 0)) {
+		stream_free(stream);
+		return;
+	}
+
+	sep = stream->lsep;
+	sep->stream = stream;
+	sep->info.inuse = 1;
+	session->streams = g_slist_append(session->streams, stream);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+}
+
+static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
+				struct setconf_req *req, unsigned int size)
+{
+	struct conf_rej rej;
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err, category = 0x00;
+	GSList *l;
+
+	if (size < sizeof(struct setconf_req)) {
+		error("Too short getcap request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (sep->stream) {
+		err = AVDTP_SEP_IN_USE;
+		goto failed;
+	}
+
+	stream = g_new0(struct avdtp_stream, 1);
+	stream->session = session;
+	stream->lsep = sep;
+	stream->rseid = req->int_seid;
+	stream->caps = caps_to_list(req->caps,
+					size - sizeof(struct setconf_req),
+					&stream->codec,
+					&stream->delay_reporting);
+
+	/*
+	 * Verify that the Media Transport capability's length = 0.
+	 * Reject otherwise
+	 */
+	for (l = stream->caps; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_service_capability *cap = l->data;
+
+		if (cap->category == AVDTP_MEDIA_TRANSPORT &&
+							cap->length != 0) {
+			err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT;
+			goto failed_stream;
+		}
+	}
+
+	if (stream->delay_reporting && session->version < 0x0103)
+		session->version = 0x0103;
+
+	if (sep->ind && sep->ind->set_configuration) {
+		if (!sep->ind->set_configuration(session, sep, stream,
+							stream->caps,
+							setconf_cb,
+							sep->user_data)) {
+			err = AVDTP_UNSUPPORTED_CONFIGURATION;
+			category = 0x00;
+			goto failed_stream;
+		}
+	} else {
+		if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+					AVDTP_SET_CONFIGURATION, NULL, 0)) {
+			stream_free(stream);
+			return FALSE;
+		}
+
+		sep->stream = stream;
+		sep->info.inuse = 1;
+		session->streams = g_slist_append(session->streams, stream);
+
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+	}
+
+	return TRUE;
+
+failed_stream:
+	stream_free(stream);
+failed:
+	rej.error = err;
+	rej.category = category;
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_SET_CONFIGURATION, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, int size)
+{
+	GSList *l;
+	struct avdtp_local_sep *sep = NULL;
+	int rsp_size;
+	uint8_t err;
+	uint8_t buf[1024];
+	uint8_t *ptr = buf;
+
+	if (size < (int) sizeof(struct seid_req)) {
+		error("Too short getconf request");
+		return FALSE;
+	}
+
+	memset(buf, 0, sizeof(buf));
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+	if (!sep->stream || !sep->stream->caps) {
+		err = AVDTP_UNSUPPORTED_CONFIGURATION;
+		goto failed;
+	}
+
+	for (l = sep->stream->caps, rsp_size = 0; l; l = g_slist_next(l)) {
+		struct avdtp_service_capability *cap = l->data;
+
+		if (rsp_size + cap->length + 2 > (int) sizeof(buf))
+			break;
+
+		memcpy(ptr, cap, cap->length + 2);
+		rsp_size += cap->length + 2;
+		ptr += cap->length + 2;
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+				AVDTP_GET_CONFIGURATION, buf, rsp_size);
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_GET_CONFIGURATION, &err, sizeof(err));
+}
+
+static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, int size)
+{
+	struct conf_rej rej;
+
+	rej.error = AVDTP_NOT_SUPPORTED_COMMAND;
+	rej.category = 0x00;
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_RECONFIGURE, &rej, sizeof(rej));
+}
+
+static void check_seid_collision(struct pending_req *req, uint8_t id)
+{
+	struct seid_req *seid = req->data;
+
+	if (seid->acp_seid == id)
+		req->collided = TRUE;
+}
+
+static void check_start_collision(struct pending_req *req, uint8_t id)
+{
+	struct start_req *start = req->data;
+	struct seid *seid = &start->first_seid;
+	int count = 1 + req->data_size - sizeof(struct start_req);
+	int i;
+
+	for (i = 0; i < count; i++, seid++) {
+		if (seid->seid == id) {
+			req->collided = TRUE;
+			return;
+		}
+	}
+}
+
+static void check_suspend_collision(struct pending_req *req, uint8_t id)
+{
+	struct suspend_req *suspend = req->data;
+	struct seid *seid = &suspend->first_seid;
+	int count = 1 + req->data_size - sizeof(struct suspend_req);
+	int i;
+
+	for (i = 0; i < count; i++, seid++) {
+		if (seid->seid == id) {
+			req->collided = TRUE;
+			return;
+		}
+	}
+}
+
+static void avdtp_check_collision(struct avdtp *session, uint8_t cmd,
+					struct avdtp_stream *stream)
+{
+	struct pending_req *req = session->req;
+
+	if (req == NULL || (req->signal_id != cmd && cmd != AVDTP_ABORT))
+		return;
+
+	if (cmd == AVDTP_ABORT)
+		cmd = req->signal_id;
+
+	switch (cmd) {
+	case AVDTP_OPEN:
+	case AVDTP_CLOSE:
+		check_seid_collision(req, stream->rseid);
+		break;
+	case AVDTP_START:
+		check_start_collision(req, stream->rseid);
+		break;
+	case AVDTP_SUSPEND:
+		check_suspend_collision(req, stream->rseid);
+		break;
+	}
+}
+
+static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction,
+				struct seid_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err;
+
+	if (size < sizeof(struct seid_req)) {
+		error("Too short abort request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (sep->state != AVDTP_STATE_CONFIGURED) {
+		err = AVDTP_BAD_STATE;
+		goto failed;
+	}
+
+	stream = sep->stream;
+
+	if (sep->ind && sep->ind->open) {
+		if (!sep->ind->open(session, sep, stream, &err,
+					sep->user_data))
+			goto failed;
+	}
+
+	avdtp_check_collision(session, AVDTP_OPEN, stream);
+
+	if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_OPEN, NULL, 0))
+		return FALSE;
+
+	stream->open_acp = TRUE;
+	session->pending_open = stream;
+	stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+						stream_open_timeout,
+						stream);
+
+	return TRUE;
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_OPEN, &err, sizeof(err));
+}
+
+static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction,
+				struct start_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	struct stream_rej rej;
+	struct seid *seid;
+	uint8_t err, failed_seid;
+	int seid_count, i;
+
+	if (size < sizeof(struct start_req)) {
+		error("Too short start request");
+		return FALSE;
+	}
+
+	seid_count = 1 + size - sizeof(struct start_req);
+
+	seid = &req->first_seid;
+
+	for (i = 0; i < seid_count; i++, seid++) {
+		failed_seid = seid->seid;
+
+		sep = find_local_sep_by_seid(session, seid->seid);
+		if (!sep || !sep->stream) {
+			err = AVDTP_BAD_ACP_SEID;
+			goto failed;
+		}
+
+		stream = sep->stream;
+
+		/* Also reject start cmd if state is not open */
+		if (sep->state != AVDTP_STATE_OPEN) {
+			err = AVDTP_BAD_STATE;
+			goto failed;
+		}
+		stream->starting = TRUE;
+
+		if (sep->ind && sep->ind->start) {
+			if (!sep->ind->start(session, sep, stream, &err,
+						sep->user_data))
+				goto failed;
+		}
+
+		avdtp_check_collision(session, AVDTP_START, stream);
+
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_START, NULL, 0);
+
+failed:
+	DBG("Rejecting (%d)", err);
+	memset(&rej, 0, sizeof(rej));
+	rej.acp_seid = failed_seid;
+	rej.error = err;
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_START, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction,
+				struct seid_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err;
+
+	if (size < sizeof(struct seid_req)) {
+		error("Too short close request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep || !sep->stream) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (sep->state != AVDTP_STATE_OPEN &&
+			sep->state != AVDTP_STATE_STREAMING) {
+		err = AVDTP_BAD_STATE;
+		goto failed;
+	}
+
+	stream = sep->stream;
+
+	if (sep->ind && sep->ind->close) {
+		if (!sep->ind->close(session, sep, stream, &err,
+					sep->user_data))
+			goto failed;
+	}
+
+	avdtp_check_collision(session, AVDTP_CLOSE, stream);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+	if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_CLOSE, NULL, 0))
+		return FALSE;
+
+	stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+					stream_close_timeout,
+					stream);
+
+	return TRUE;
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_CLOSE, &err, sizeof(err));
+}
+
+static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction,
+				struct suspend_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	struct stream_rej rej;
+	struct seid *seid;
+	uint8_t err, failed_seid;
+	int seid_count, i;
+
+	if (size < sizeof(struct suspend_req)) {
+		error("Too short suspend request");
+		return FALSE;
+	}
+
+	seid_count = 1 + size - sizeof(struct suspend_req);
+
+	seid = &req->first_seid;
+
+	for (i = 0; i < seid_count; i++, seid++) {
+		failed_seid = seid->seid;
+
+		sep = find_local_sep_by_seid(session, seid->seid);
+		if (!sep || !sep->stream) {
+			err = AVDTP_BAD_ACP_SEID;
+			goto failed;
+		}
+
+		stream = sep->stream;
+
+		if (sep->state != AVDTP_STATE_STREAMING) {
+			err = AVDTP_BAD_STATE;
+			goto failed;
+		}
+
+		if (sep->ind && sep->ind->suspend) {
+			if (!sep->ind->suspend(session, sep, stream, &err,
+						sep->user_data))
+				goto failed;
+		}
+
+		avdtp_check_collision(session, AVDTP_SUSPEND, stream);
+
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_SUSPEND, NULL, 0);
+
+failed:
+	memset(&rej, 0, sizeof(rej));
+	rej.acp_seid = failed_seid;
+	rej.error = err;
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_SUSPEND, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction,
+				struct seid_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	uint8_t err;
+	gboolean ret;
+
+	if (size < sizeof(struct seid_req)) {
+		error("Too short abort request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep || !sep->stream)
+		return TRUE;
+
+	if (sep->ind && sep->ind->abort)
+		sep->ind->abort(session, sep, sep->stream, &err,
+							sep->user_data);
+
+	avdtp_check_collision(session, AVDTP_ABORT, sep->stream);
+
+	ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_ABORT, NULL, 0);
+	if (ret)
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+	return ret;
+}
+
+static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, int size)
+{
+	return avdtp_unknown_cmd(session, transaction, AVDTP_SECURITY_CONTROL);
+}
+
+static gboolean avdtp_delayreport_cmd(struct avdtp *session,
+					uint8_t transaction,
+					struct delay_req *req,
+					unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err;
+
+	if (size < sizeof(struct delay_req)) {
+		error("Too short delay report request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep || !sep->stream) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	stream = sep->stream;
+
+	switch (sep->state) {
+	case AVDTP_STATE_IDLE:
+	case AVDTP_STATE_ABORTING:
+	case AVDTP_STATE_CLOSING:
+		err = AVDTP_BAD_STATE;
+		goto failed;
+	case AVDTP_STATE_CONFIGURED:
+	case AVDTP_STATE_OPEN:
+	case AVDTP_STATE_STREAMING:
+	default:
+		break;
+	}
+
+	stream->delay = ntohs(req->delay);
+
+	if (sep->ind && sep->ind->delayreport) {
+		if (!sep->ind->delayreport(session, sep, stream->rseid,
+						stream->delay, &err,
+						sep->user_data))
+			goto failed;
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_DELAY_REPORT, NULL, 0);
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_DELAY_REPORT, &err, sizeof(err));
+}
+
+static gboolean avdtp_parse_cmd(struct avdtp *session, uint8_t transaction,
+				uint8_t signal_id, void *buf, int size)
+{
+	switch (signal_id) {
+	case AVDTP_DISCOVER:
+		DBG("Received DISCOVER_CMD");
+		return avdtp_discover_cmd(session, transaction, buf, size);
+	case AVDTP_GET_CAPABILITIES:
+		DBG("Received  GET_CAPABILITIES_CMD");
+		return avdtp_getcap_cmd(session, transaction, buf, size,
+									FALSE);
+	case AVDTP_GET_ALL_CAPABILITIES:
+		DBG("Received  GET_ALL_CAPABILITIES_CMD");
+		return avdtp_getcap_cmd(session, transaction, buf, size, TRUE);
+	case AVDTP_SET_CONFIGURATION:
+		DBG("Received SET_CONFIGURATION_CMD");
+		return avdtp_setconf_cmd(session, transaction, buf, size);
+	case AVDTP_GET_CONFIGURATION:
+		DBG("Received GET_CONFIGURATION_CMD");
+		return avdtp_getconf_cmd(session, transaction, buf, size);
+	case AVDTP_RECONFIGURE:
+		DBG("Received RECONFIGURE_CMD");
+		return avdtp_reconf_cmd(session, transaction, buf, size);
+	case AVDTP_OPEN:
+		DBG("Received OPEN_CMD");
+		return avdtp_open_cmd(session, transaction, buf, size);
+	case AVDTP_START:
+		DBG("Received START_CMD");
+		return avdtp_start_cmd(session, transaction, buf, size);
+	case AVDTP_CLOSE:
+		DBG("Received CLOSE_CMD");
+		return avdtp_close_cmd(session, transaction, buf, size);
+	case AVDTP_SUSPEND:
+		DBG("Received SUSPEND_CMD");
+		return avdtp_suspend_cmd(session, transaction, buf, size);
+	case AVDTP_ABORT:
+		DBG("Received ABORT_CMD");
+		return avdtp_abort_cmd(session, transaction, buf, size);
+	case AVDTP_SECURITY_CONTROL:
+		DBG("Received SECURITY_CONTROL_CMD");
+		return avdtp_secctl_cmd(session, transaction, buf, size);
+	case AVDTP_DELAY_REPORT:
+		DBG("Received DELAY_REPORT_CMD");
+		return avdtp_delayreport_cmd(session, transaction, buf, size);
+	default:
+		DBG("Received unknown request id %u", signal_id);
+		return avdtp_unknown_cmd(session, transaction, signal_id);
+	}
+}
+
+enum avdtp_parse_result { PARSE_ERROR, PARSE_FRAGMENT, PARSE_SUCCESS };
+
+static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session,
+							void *buf, size_t size)
+{
+	struct avdtp_common_header *header = buf;
+	struct avdtp_single_header *single = (void *) session->buf;
+	struct avdtp_start_header *start = (void *) session->buf;
+	void *payload;
+	gsize payload_size;
+
+	switch (header->packet_type) {
+	case AVDTP_PKT_TYPE_SINGLE:
+		if (size < sizeof(*single)) {
+			error("Received too small single packet (%zu bytes)",
+									size);
+			return PARSE_ERROR;
+		}
+		if (session->in.active) {
+			error("SINGLE: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+
+		payload = session->buf + sizeof(*single);
+		payload_size = size - sizeof(*single);
+
+		session->in.active = TRUE;
+		session->in.data_size = 0;
+		session->in.no_of_packets = 1;
+		session->in.transaction = header->transaction;
+		session->in.message_type = header->message_type;
+		session->in.signal_id = single->signal_id;
+
+		break;
+	case AVDTP_PKT_TYPE_START:
+		if (size < sizeof(*start)) {
+			error("Received too small start packet (%zu bytes)",
+									size);
+			return PARSE_ERROR;
+		}
+		if (session->in.active) {
+			error("START: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+
+		session->in.active = TRUE;
+		session->in.data_size = 0;
+		session->in.transaction = header->transaction;
+		session->in.message_type = header->message_type;
+		session->in.no_of_packets = start->no_of_packets;
+		session->in.signal_id = start->signal_id;
+
+		payload = session->buf + sizeof(*start);
+		payload_size = size - sizeof(*start);
+
+		break;
+	case AVDTP_PKT_TYPE_CONTINUE:
+		if (size < sizeof(struct avdtp_continue_header)) {
+			error("Received too small continue packet (%zu bytes)",
+									size);
+			return PARSE_ERROR;
+		}
+		if (!session->in.active) {
+			error("CONTINUE: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+		if (session->in.transaction != header->transaction) {
+			error("Continue transaction id doesn't match");
+			return PARSE_ERROR;
+		}
+		if (session->in.no_of_packets <= 1) {
+			error("Too few continue packets");
+			return PARSE_ERROR;
+		}
+
+		payload = session->buf + sizeof(struct avdtp_continue_header);
+		payload_size = size - sizeof(struct avdtp_continue_header);
+
+		break;
+	case AVDTP_PKT_TYPE_END:
+		if (size < sizeof(struct avdtp_continue_header)) {
+			error("Received too small end packet (%zu bytes)",
+									size);
+			return PARSE_ERROR;
+		}
+		if (!session->in.active) {
+			error("END: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+		if (session->in.transaction != header->transaction) {
+			error("End transaction id doesn't match");
+			return PARSE_ERROR;
+		}
+		if (session->in.no_of_packets > 1) {
+			error("Got an end packet too early");
+			return PARSE_ERROR;
+		}
+
+		payload = session->buf + sizeof(struct avdtp_continue_header);
+		payload_size = size - sizeof(struct avdtp_continue_header);
+
+		break;
+	default:
+		error("Invalid AVDTP packet type 0x%02X", header->packet_type);
+		return PARSE_ERROR;
+	}
+
+	if (session->in.data_size + payload_size >
+					sizeof(session->in.buf)) {
+		error("Not enough incoming buffer space!");
+		return PARSE_ERROR;
+	}
+
+	memcpy(session->in.buf + session->in.data_size, payload, payload_size);
+	session->in.data_size += payload_size;
+
+	if (session->in.no_of_packets > 1) {
+		session->in.no_of_packets--;
+		DBG("Received AVDTP fragment. %d to go",
+						session->in.no_of_packets);
+		return PARSE_FRAGMENT;
+	}
+
+	session->in.active = FALSE;
+
+	return PARSE_SUCCESS;
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
+				gpointer data)
+{
+	struct avdtp *session = data;
+	struct avdtp_common_header *header;
+	ssize_t size;
+	int fd;
+
+	DBG("");
+
+	if (cond & G_IO_NVAL) {
+		session->io_id = 0;
+
+		return FALSE;
+	}
+
+	header = (void *) session->buf;
+
+	if (cond & (G_IO_HUP | G_IO_ERR))
+		goto failed;
+
+	fd = g_io_channel_unix_get_fd(chan);
+	size = read(fd, session->buf, session->imtu);
+	if (size < 0) {
+		error("IO Channel read error");
+		goto failed;
+	}
+
+	if ((size_t) size < sizeof(struct avdtp_common_header)) {
+		error("Received too small packet (%zu bytes)", size);
+		goto failed;
+	}
+
+	switch (avdtp_parse_data(session, session->buf, size)) {
+	case PARSE_ERROR:
+		goto failed;
+	case PARSE_FRAGMENT:
+		return TRUE;
+	case PARSE_SUCCESS:
+		break;
+	}
+
+	/* Take a reference to protect against callback destroying session */
+	avdtp_ref(session);
+
+	if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) {
+		if (!avdtp_parse_cmd(session, session->in.transaction,
+					session->in.signal_id,
+					session->in.buf,
+					session->in.data_size)) {
+			error("Unable to handle command. Disconnecting");
+			goto failed;
+		}
+
+		if (session->req && session->req->collided) {
+			DBG("Collision detected");
+			goto next;
+		}
+
+		avdtp_unref(session);
+		return TRUE;
+	}
+
+	if (session->req == NULL) {
+		error("No pending request, ignoring message");
+		avdtp_unref(session);
+		return TRUE;
+	}
+
+	if (header->transaction != session->req->transaction) {
+		error("Transaction label doesn't match");
+		avdtp_unref(session);
+		return TRUE;
+	}
+
+	if (session->in.signal_id != session->req->signal_id) {
+		error("Response signal doesn't match");
+		avdtp_unref(session);
+		return TRUE;
+	}
+
+	g_source_remove(session->req->timeout);
+	session->req->timeout = 0;
+
+	switch (header->message_type) {
+	case AVDTP_MSG_TYPE_ACCEPT:
+		if (!avdtp_parse_resp(session, session->req->stream,
+						session->in.transaction,
+						session->in.signal_id,
+						session->in.buf,
+						session->in.data_size)) {
+			error("Unable to parse accept response");
+			goto failed;
+		}
+		break;
+	case AVDTP_MSG_TYPE_REJECT:
+		if (!avdtp_parse_rej(session, session->req->stream,
+						session->in.transaction,
+						session->in.signal_id,
+						session->in.buf,
+						session->in.data_size)) {
+			error("Unable to parse reject response");
+			goto failed;
+		}
+		break;
+	case AVDTP_MSG_TYPE_GEN_REJECT:
+		error("Received a General Reject message");
+		break;
+	default:
+		error("Unknown message type 0x%02X", header->message_type);
+		break;
+	}
+
+next:
+	pending_req_free(session->req);
+	session->req = NULL;
+
+	if (session->ref > 1)
+		process_queue(session);
+
+	avdtp_unref(session);
+
+	return TRUE;
+
+failed:
+	session->io_id = 0;
+	connection_lost(session, EIO);
+
+	return FALSE;
+}
+
+static int set_priority(int fd, int priority)
+{
+	int err;
+
+	err = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority,
+							sizeof(priority));
+	if (err == 0 || errno == ENOTSOCK)
+		return 0;
+
+	err = -errno;
+	error("setsockopt(SO_PRIORITY): %s (%d)", strerror(-err), -err);
+
+	return err;
+}
+
+struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version,
+							struct queue *lseps)
+{
+	struct avdtp *session;
+	GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	int new_fd;
+
+	if (!lseps)
+		return NULL;
+
+	new_fd = dup(fd);
+	if (new_fd < 0) {
+		error("dup(): %s (%d)", strerror(errno), errno);
+		return NULL;
+	}
+
+	if (set_priority(new_fd, 6) < 0)
+		return NULL;
+
+	session = g_new0(struct avdtp, 1);
+	session->io = g_io_channel_unix_new(new_fd);
+	session->version = version;
+	session->imtu = imtu;
+	session->omtu = omtu;
+	session->buf = g_malloc0(MAX(session->imtu, session->omtu));
+
+	/* This watch should be low priority since otherwise the
+	 * connect callback might be dispatched before the session
+	 * callback if the kernel wakes us up at the same time for
+	 * them. This could happen if a headset is very quick in
+	 * sending the Start command after connecting the stream
+	 * transport channel.
+	 */
+	session->io_id = g_io_add_watch_full(session->io, G_PRIORITY_LOW, cond,
+						(GIOFunc) session_cb, session,
+						NULL);
+
+	session->lseps = lseps;
+
+	return avdtp_ref(session);
+}
+
+unsigned int avdtp_add_disconnect_cb(struct avdtp *session,
+						avdtp_disconnect_cb_t cb,
+						void *user_data)
+{
+	struct disconnect_callback *callback;
+	static unsigned int id = 0;
+
+	callback = g_new0(struct disconnect_callback, 1);
+	callback->id = ++id;
+	callback->cb = cb;
+	callback->user_data = user_data;
+	session->disconnect = g_slist_append(session->disconnect, callback);
+
+	return id;
+}
+
+gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id)
+{
+	GSList *l;
+
+	for (l = session->disconnect; l; l = g_slist_next(l)) {
+		struct disconnect_callback *callback = l->data;
+
+		if (callback->id != id)
+			continue;
+
+		session->disconnect = g_slist_remove(session->disconnect,
+								callback);
+		g_free(callback);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+void avdtp_shutdown(struct avdtp *session)
+{
+	GSList *l;
+	bool aborting = false;
+
+	if (!session->io)
+		return;
+
+	for (l = session->streams; l; l = g_slist_next(l)) {
+		struct avdtp_stream *stream = l->data;
+
+		if (stream->abort_int ||
+					avdtp_close(session, stream, TRUE) == 0)
+			aborting = true;
+	}
+
+	if (aborting) {
+		/* defer shutdown until all streams are aborted properly */
+		session->shutdown = true;
+	} else {
+		int sock = g_io_channel_unix_get_fd(session->io);
+
+		shutdown(sock, SHUT_RDWR);
+	}
+}
+
+static void queue_request(struct avdtp *session, struct pending_req *req,
+			gboolean priority)
+{
+	if (priority)
+		session->prio_queue = g_slist_append(session->prio_queue, req);
+	else
+		session->req_queue = g_slist_append(session->req_queue, req);
+}
+
+static uint8_t req_get_seid(struct pending_req *req)
+{
+	if (req->signal_id == AVDTP_DISCOVER)
+		return 0;
+
+	return ((struct seid_req *) (req->data))->acp_seid;
+}
+
+static int cancel_request(struct avdtp *session, int err)
+{
+	struct pending_req *req;
+	struct seid_req sreq;
+	struct avdtp_local_sep *lsep;
+	struct avdtp_stream *stream;
+	uint8_t seid;
+	struct avdtp_error averr;
+
+	req = session->req;
+	session->req = NULL;
+
+	avdtp_error_init(&averr, AVDTP_ERRNO, err);
+
+	seid = req_get_seid(req);
+	if (seid)
+		stream = find_stream_by_rseid(session, seid);
+	else
+		stream = NULL;
+
+	if (stream) {
+		stream->abort_int = TRUE;
+		lsep = stream->lsep;
+	} else
+		lsep = NULL;
+
+	switch (req->signal_id) {
+	case AVDTP_RECONFIGURE:
+		error("Reconfigure: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+			lsep->cfm->reconfigure(session, lsep, stream, &averr,
+						lsep->user_data);
+		break;
+	case AVDTP_OPEN:
+		error("Open: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->open)
+			lsep->cfm->open(session, lsep, stream, &averr,
+					lsep->user_data);
+		break;
+	case AVDTP_START:
+		error("Start: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->start) {
+			lsep->cfm->start(session, lsep, stream, &averr,
+						lsep->user_data);
+			if (stream)
+				stream->starting = FALSE;
+		}
+		break;
+	case AVDTP_SUSPEND:
+		error("Suspend: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->suspend)
+			lsep->cfm->suspend(session, lsep, stream, &averr,
+						lsep->user_data);
+		break;
+	case AVDTP_CLOSE:
+		error("Close: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->close) {
+			lsep->cfm->close(session, lsep, stream, &averr,
+						lsep->user_data);
+			if (stream)
+				stream->close_int = FALSE;
+		}
+		break;
+	case AVDTP_SET_CONFIGURATION:
+		error("SetConfiguration: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+			lsep->cfm->set_configuration(session, lsep, stream,
+						&averr, lsep->user_data);
+		goto failed;
+	case AVDTP_DISCOVER:
+		error("Discover: %s (%d)", strerror(err), err);
+		goto failed;
+	case AVDTP_GET_CAPABILITIES:
+		error("GetCapabilities: %s (%d)", strerror(err), err);
+		goto failed;
+	case AVDTP_ABORT:
+		error("Abort: %s (%d)", strerror(err), err);
+		goto failed;
+	}
+
+	if (!stream)
+		goto failed;
+
+	memset(&sreq, 0, sizeof(sreq));
+	sreq.acp_seid = seid;
+
+	err = send_request(session, TRUE, stream, AVDTP_ABORT, &sreq,
+				sizeof(sreq));
+	if (err < 0) {
+		error("Unable to send abort request");
+		goto failed;
+	}
+
+	goto done;
+
+failed:
+	connection_lost(session, err);
+done:
+	pending_req_free(req);
+	return err;
+}
+
+static gboolean request_timeout(gpointer user_data)
+{
+	struct avdtp *session = user_data;
+
+	cancel_request(session, ETIMEDOUT);
+
+	return FALSE;
+}
+
+static int send_req(struct avdtp *session, gboolean priority,
+			struct pending_req *req)
+{
+	static int transaction = 0;
+	int err;
+
+	if (session->req != NULL) {
+		queue_request(session, req, priority);
+		return 0;
+	}
+
+	req->transaction = transaction++;
+	transaction %= 16;
+
+	/* FIXME: Should we retry to send if the buffer
+	was not totally sent or in case of EINTR? */
+	if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,
+				req->signal_id, req->data, req->data_size)) {
+		err = -EIO;
+		goto failed;
+	}
+
+	session->req = req;
+
+	req->timeout = g_timeout_add_seconds(req->signal_id == AVDTP_ABORT ?
+					ABORT_TIMEOUT : REQ_TIMEOUT,
+					request_timeout,
+					session);
+	return 0;
+
+failed:
+	g_free(req->data);
+	g_free(req);
+	return err;
+}
+
+static int send_request(struct avdtp *session, gboolean priority,
+			struct avdtp_stream *stream, uint8_t signal_id,
+			void *buffer, size_t size)
+{
+	struct pending_req *req;
+
+	if (size > 0 && !buffer) {
+		DBG("Invalid buffer %p", buffer);
+		return -EINVAL;
+	}
+
+	if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {
+		DBG("Unable to send requests while aborting");
+		return -EINVAL;
+	}
+
+	req = g_new0(struct pending_req, 1);
+	req->signal_id = signal_id;
+	req->data_size = size;
+	req->stream = stream;
+
+	if (size > 0) {
+		req->data = g_malloc(size);
+		memcpy(req->data, buffer, size);
+	}
+
+	return send_req(session, priority, req);
+}
+
+static gboolean avdtp_discover_resp(struct avdtp *session,
+					struct discover_resp *resp, int size)
+{
+	int sep_count, i;
+	uint8_t getcap_cmd;
+	int ret = 0;
+	gboolean getcap_pending = FALSE;
+
+	if (session->version >= 0x0103)
+		getcap_cmd = AVDTP_GET_ALL_CAPABILITIES;
+	else
+		getcap_cmd = AVDTP_GET_CAPABILITIES;
+
+	sep_count = size / sizeof(struct seid_info);
+
+	for (i = 0; i < sep_count; i++) {
+		struct avdtp_remote_sep *sep;
+		struct avdtp_stream *stream;
+		struct seid_req req;
+
+		DBG("seid %d type %d media %d in use %d",
+				resp->seps[i].seid, resp->seps[i].type,
+				resp->seps[i].media_type, resp->seps[i].inuse);
+
+		stream = find_stream_by_rseid(session, resp->seps[i].seid);
+
+		sep = find_remote_sep(session->seps, resp->seps[i].seid);
+		if (!sep) {
+			if (resp->seps[i].inuse && !stream)
+				continue;
+			sep = g_new0(struct avdtp_remote_sep, 1);
+			session->seps = g_slist_append(session->seps, sep);
+		}
+
+		sep->stream = stream;
+		sep->seid = resp->seps[i].seid;
+		sep->type = resp->seps[i].type;
+		sep->media_type = resp->seps[i].media_type;
+
+		memset(&req, 0, sizeof(req));
+		req.acp_seid = sep->seid;
+
+		ret = send_request(session, TRUE, NULL, getcap_cmd,
+							&req, sizeof(req));
+		if (ret < 0)
+			break;
+		getcap_pending = TRUE;
+	}
+
+	if (!getcap_pending)
+		finalize_discovery(session, -ret);
+
+	return TRUE;
+}
+
+static gboolean avdtp_get_capabilities_resp(struct avdtp *session,
+						struct getcap_resp *resp,
+						unsigned int size)
+{
+	struct avdtp_remote_sep *sep;
+	uint8_t seid;
+
+	/* Check for minimum required packet size includes:
+	 *   1. getcap resp header
+	 *   2. media transport capability (2 bytes)
+	 *   3. media codec capability type + length (2 bytes)
+	 *   4. the actual media codec elements
+	 * */
+	if (size < (sizeof(struct getcap_resp) + 4 +
+				sizeof(struct avdtp_media_codec_capability))) {
+		error("Too short getcap resp packet");
+		return FALSE;
+	}
+
+	seid = ((struct seid_req *) session->req->data)->acp_seid;
+
+	sep = find_remote_sep(session->seps, seid);
+
+	DBG("seid %d type %d media %d", sep->seid,
+					sep->type, sep->media_type);
+
+	if (sep->caps) {
+		g_slist_free_full(sep->caps, g_free);
+		sep->caps = NULL;
+		sep->codec = NULL;
+		sep->delay_reporting = FALSE;
+	}
+
+	sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp),
+					&sep->codec, &sep->delay_reporting);
+
+	return TRUE;
+}
+
+static gboolean avdtp_set_configuration_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct avdtp_single_header *resp,
+					int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+
+	if (sep->cfm && sep->cfm->set_configuration)
+		sep->cfm->set_configuration(session, sep, stream, NULL,
+						sep->user_data);
+
+	return TRUE;
+}
+
+static gboolean avdtp_reconfigure_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct avdtp_single_header *resp,
+					int size)
+{
+	return TRUE;
+}
+
+static gboolean avdtp_open_resp(struct avdtp *session,
+				struct avdtp_stream *stream,
+				struct seid_rej *resp, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	session->pending_open = stream;
+
+	if (!stream->open_acp && sep->cfm && sep->cfm->open)
+		sep->cfm->open(session, sep, stream, NULL, sep->user_data);
+
+	return TRUE;
+}
+
+static gboolean avdtp_start_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct seid_rej *resp, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	/* We might be in STREAMING already if both sides send START_CMD at the
+	 * same time and the one in SNK role doesn't reject it as it should */
+	if (sep->state != AVDTP_STATE_STREAMING)
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+
+	if (sep->cfm && sep->cfm->start)
+		sep->cfm->start(session, sep, stream, NULL, sep->user_data);
+
+	return TRUE;
+}
+
+static gboolean avdtp_close_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct seid_rej *resp, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+	close_stream(stream);
+
+	return TRUE;
+}
+
+static gboolean avdtp_suspend_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					void *data, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+	if (sep->cfm && sep->cfm->suspend)
+		sep->cfm->suspend(session, sep, stream, NULL, sep->user_data);
+
+	return TRUE;
+}
+
+static gboolean avdtp_abort_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct seid_rej *resp, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+	if (sep->cfm && sep->cfm->abort)
+		sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+
+	return TRUE;
+}
+
+static gboolean avdtp_delay_report_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					void *data, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	if (sep->cfm && sep->cfm->delay_report)
+		sep->cfm->delay_report(session, sep, stream, NULL,
+							sep->user_data);
+
+	return TRUE;
+}
+
+static gboolean avdtp_parse_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size)
+{
+	struct pending_req *next;
+	const char *get_all = "";
+
+	if (session->prio_queue)
+		next = session->prio_queue->data;
+	else if (session->req_queue)
+		next = session->req_queue->data;
+	else
+		next = NULL;
+
+	switch (signal_id) {
+	case AVDTP_DISCOVER:
+		DBG("DISCOVER request succeeded");
+		return avdtp_discover_resp(session, buf, size);
+	case AVDTP_GET_ALL_CAPABILITIES:
+		get_all = "ALL_";
+	case AVDTP_GET_CAPABILITIES:
+		DBG("GET_%sCAPABILITIES request succeeded", get_all);
+		if (!avdtp_get_capabilities_resp(session, buf, size))
+			return FALSE;
+		if (!(next && (next->signal_id == AVDTP_GET_CAPABILITIES ||
+				next->signal_id == AVDTP_GET_ALL_CAPABILITIES)))
+			finalize_discovery(session, 0);
+		return TRUE;
+	}
+
+	/* The remaining commands require an existing stream so bail out
+	 * here if the stream got unexpectedly disconnected */
+	if (!stream) {
+		DBG("AVDTP: stream was closed while waiting for reply");
+		return TRUE;
+	}
+
+	switch (signal_id) {
+	case AVDTP_SET_CONFIGURATION:
+		DBG("SET_CONFIGURATION request succeeded");
+		return avdtp_set_configuration_resp(session, stream,
+								buf, size);
+	case AVDTP_RECONFIGURE:
+		DBG("RECONFIGURE request succeeded");
+		return avdtp_reconfigure_resp(session, stream, buf, size);
+	case AVDTP_OPEN:
+		DBG("OPEN request succeeded");
+		return avdtp_open_resp(session, stream, buf, size);
+	case AVDTP_SUSPEND:
+		DBG("SUSPEND request succeeded");
+		return avdtp_suspend_resp(session, stream, buf, size);
+	case AVDTP_START:
+		DBG("START request succeeded");
+		return avdtp_start_resp(session, stream, buf, size);
+	case AVDTP_CLOSE:
+		DBG("CLOSE request succeeded");
+		return avdtp_close_resp(session, stream, buf, size);
+	case AVDTP_ABORT:
+		DBG("ABORT request succeeded");
+		return avdtp_abort_resp(session, stream, buf, size);
+	case AVDTP_DELAY_REPORT:
+		DBG("DELAY_REPORT request succeeded");
+		return avdtp_delay_report_resp(session, stream, buf, size);
+	}
+
+	error("Unknown signal id in accept response: %u", signal_id);
+	return TRUE;
+}
+
+static gboolean seid_rej_to_err(struct seid_rej *rej, unsigned int size,
+					struct avdtp_error *err)
+{
+	if (size < sizeof(struct seid_rej)) {
+		error("Too small packet for seid_rej");
+		return FALSE;
+	}
+
+	avdtp_error_init(err, 0x00, rej->error);
+
+	return TRUE;
+}
+
+static gboolean conf_rej_to_err(struct conf_rej *rej, unsigned int size,
+				struct avdtp_error *err)
+{
+	if (size < sizeof(struct conf_rej)) {
+		error("Too small packet for conf_rej");
+		return FALSE;
+	}
+
+	avdtp_error_init(err, rej->category, rej->error);
+
+	return TRUE;
+}
+
+static gboolean stream_rej_to_err(struct stream_rej *rej, unsigned int size,
+					struct avdtp_error *err,
+					uint8_t *acp_seid)
+{
+	if (size < sizeof(struct stream_rej)) {
+		error("Too small packet for stream_rej");
+		return FALSE;
+	}
+
+	avdtp_error_init(err, 0x00, rej->error);
+
+	if (acp_seid)
+		*acp_seid = rej->acp_seid;
+
+	return TRUE;
+}
+
+static gboolean avdtp_parse_rej(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size)
+{
+	struct avdtp_error err;
+	uint8_t acp_seid;
+	struct avdtp_local_sep *sep = stream ? stream->lsep : NULL;
+
+	switch (signal_id) {
+	case AVDTP_DISCOVER:
+	case AVDTP_GET_CAPABILITIES:
+	case AVDTP_GET_ALL_CAPABILITIES:
+		if (!seid_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("%s request rejected: %s (%d)",
+			signal_id == AVDTP_DISCOVER ? "DISCOVER" :
+			signal_id == AVDTP_GET_CAPABILITIES ?
+			"GET_CAPABILITIES" : "GET_ALL_CAPABILITIES",
+			avdtp_strerror(&err), err.err.error_code);
+		if (session->discover) {
+			session->discover->cb(session, session->seps, &err,
+						session->discover->user_data);
+			g_free(session->discover);
+			session->discover = NULL;
+		}
+		return TRUE;
+	case AVDTP_OPEN:
+		if (!seid_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("OPEN request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->open)
+			sep->cfm->open(session, sep, stream, &err,
+					sep->user_data);
+		return TRUE;
+	case AVDTP_SET_CONFIGURATION:
+		if (!conf_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("SET_CONFIGURATION request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->set_configuration)
+			sep->cfm->set_configuration(session, sep, stream,
+							&err, sep->user_data);
+		return TRUE;
+	case AVDTP_GET_CONFIGURATION:
+		if (!seid_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("GET_CONFIGURATION request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->get_configuration)
+			sep->cfm->get_configuration(session, sep, stream, &err,
+								sep->user_data);
+		return TRUE;
+	case AVDTP_RECONFIGURE:
+		if (!conf_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("RECONFIGURE request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->reconfigure)
+			sep->cfm->reconfigure(session, sep, stream, &err,
+						sep->user_data);
+		return TRUE;
+	case AVDTP_START:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("START request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->start) {
+			stream->starting = FALSE;
+			sep->cfm->start(session, sep, stream, &err,
+					sep->user_data);
+		}
+		return TRUE;
+	case AVDTP_SUSPEND:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("SUSPEND request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->suspend)
+			sep->cfm->suspend(session, sep, stream, &err,
+						sep->user_data);
+		return TRUE;
+	case AVDTP_CLOSE:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("CLOSE request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->close) {
+			sep->cfm->close(session, sep, stream, &err,
+					sep->user_data);
+			stream->close_int = FALSE;
+		}
+		return TRUE;
+	case AVDTP_ABORT:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("ABORT request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->abort)
+			sep->cfm->abort(session, sep, stream, &err,
+					sep->user_data);
+		return FALSE;
+	case AVDTP_DELAY_REPORT:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("DELAY_REPORT request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->delay_report)
+			sep->cfm->delay_report(session, sep, stream, &err,
+							sep->user_data);
+		return TRUE;
+	default:
+		error("Unknown reject response signal id: %u", signal_id);
+		return TRUE;
+	}
+}
+
+struct avdtp_service_capability *avdtp_stream_get_codec(
+						struct avdtp_stream *stream)
+{
+	GSList *l;
+
+	for (l = stream->caps; l; l = l->next) {
+		struct avdtp_service_capability *cap = l->data;
+
+		if (cap->category == AVDTP_MEDIA_CODEC)
+			return cap;
+	}
+
+	return NULL;
+}
+
+static gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
+					struct avdtp_service_capability *cap)
+{
+	GSList *l;
+	struct avdtp_service_capability *stream_cap;
+
+	for (l = stream->caps; l; l = g_slist_next(l)) {
+		stream_cap = l->data;
+
+		if (stream_cap->category != cap->category ||
+			stream_cap->length != cap->length)
+			continue;
+
+		if (memcmp(stream_cap->data, cap->data, cap->length) == 0)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+					GSList *caps)
+{
+	for (; caps; caps = g_slist_next(caps)) {
+		struct avdtp_service_capability *cap = caps->data;
+
+		if (!avdtp_stream_has_capability(stream, cap))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+						struct avdtp_stream *stream)
+{
+	GSList *l;
+
+	for (l = stream->session->seps; l; l = l->next) {
+		struct avdtp_remote_sep *sep = l->data;
+
+		if (sep->seid == stream->rseid)
+			return sep;
+	}
+
+	return NULL;
+}
+
+gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
+						size_t imtu, size_t omtu)
+{
+	GIOChannel *io;
+
+	if (stream != stream->session->pending_open)
+		return FALSE;
+
+	if (set_priority(fd, 5) < 0)
+		return FALSE;
+
+	io = g_io_channel_unix_new(fd);
+
+	handle_transport_connect(stream->session, io, imtu, omtu);
+
+	g_io_channel_unref(io);
+
+	return TRUE;
+}
+
+gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
+					uint16_t *imtu, uint16_t *omtu,
+					GSList **caps)
+{
+	if (stream->io == NULL)
+		return FALSE;
+
+	if (sock)
+		*sock = g_io_channel_unix_get_fd(stream->io);
+
+	if (omtu)
+		*omtu = stream->omtu;
+
+	if (imtu)
+		*imtu = stream->imtu;
+
+	if (caps)
+		*caps = stream->caps;
+
+	return TRUE;
+}
+
+static int process_queue(struct avdtp *session)
+{
+	GSList **queue, *l;
+	struct pending_req *req;
+
+	if (session->req)
+		return 0;
+
+	if (session->prio_queue)
+		queue = &session->prio_queue;
+	else
+		queue = &session->req_queue;
+
+	if (!*queue)
+		return 0;
+
+	l = *queue;
+	req = l->data;
+
+	*queue = g_slist_remove(*queue, req);
+
+	return send_req(session, FALSE, req);
+}
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep)
+{
+	return sep->codec;
+}
+
+struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
+							const void *data,
+							int length)
+{
+	struct avdtp_service_capability *cap;
+
+	if (category < AVDTP_MEDIA_TRANSPORT ||
+					category > AVDTP_DELAY_REPORTING)
+		return NULL;
+
+	if (length > 0 && !data)
+		return NULL;
+
+	cap = g_malloc(sizeof(struct avdtp_service_capability) + length);
+	cap->category = category;
+	cap->length = length;
+
+	if (length > 0)
+		memcpy(cap->data, data, length);
+
+	return cap;
+}
+
+static gboolean process_discover(gpointer data)
+{
+	struct avdtp *session = data;
+
+	session->discover->id = 0;
+
+	finalize_discovery(session, 0);
+
+	return FALSE;
+}
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+			void *user_data)
+{
+	int err;
+
+	if (session->discover)
+		return -EBUSY;
+
+	session->discover = g_new0(struct discover_callback, 1);
+
+	if (session->seps) {
+		session->discover->cb = cb;
+		session->discover->user_data = user_data;
+		session->discover->id = g_idle_add(process_discover, session);
+		return 0;
+	}
+
+	err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0);
+	if (err == 0) {
+		session->discover->cb = cb;
+		session->discover->user_data = user_data;
+	}
+
+	return err;
+}
+
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+				struct avdtp_stream *stream,
+				unsigned int id)
+{
+	GSList *l;
+	struct stream_callback *cb;
+
+	if (!stream)
+		return FALSE;
+
+	for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) {
+		struct stream_callback *tmp = l->data;
+		if (tmp && tmp->id == id) {
+			cb = tmp;
+			break;
+		}
+	}
+
+	if (!cb)
+		return FALSE;
+
+	stream->callbacks = g_slist_remove(stream->callbacks, cb);
+	g_free(cb);
+
+	return TRUE;
+}
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+					struct avdtp_stream *stream,
+					avdtp_stream_state_cb cb, void *data)
+{
+	struct stream_callback *stream_cb;
+	static unsigned int id = 0;
+
+	stream_cb = g_new(struct stream_callback, 1);
+	stream_cb->cb = cb;
+	stream_cb->user_data = data;
+	stream_cb->id = ++id;
+
+	stream->callbacks = g_slist_append(stream->callbacks, stream_cb);
+
+	return stream_cb->id;
+}
+
+int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	return send_request(session, FALSE, stream, AVDTP_GET_CONFIGURATION,
+							&req, sizeof(req));
+}
+
+static void copy_capabilities(gpointer data, gpointer user_data)
+{
+	struct avdtp_service_capability *src_cap = data;
+	struct avdtp_service_capability *dst_cap;
+	GSList **l = user_data;
+
+	dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data,
+					src_cap->length);
+
+	*l = g_slist_append(*l, dst_cap);
+}
+
+int avdtp_set_configuration(struct avdtp *session,
+				struct avdtp_remote_sep *rsep,
+				struct avdtp_local_sep *lsep,
+				GSList *caps,
+				struct avdtp_stream **stream)
+{
+	struct setconf_req *req;
+	struct avdtp_stream *new_stream;
+	unsigned char *ptr;
+	int err, caps_len;
+	struct avdtp_service_capability *cap;
+	GSList *l;
+
+	if (!(lsep && rsep))
+		return -EINVAL;
+
+	DBG("%p: int_seid=%u, acp_seid=%u", session,
+			lsep->info.seid, rsep->seid);
+
+	new_stream = g_new0(struct avdtp_stream, 1);
+	new_stream->session = session;
+	new_stream->lsep = lsep;
+	new_stream->rseid = rsep->seid;
+
+	if (rsep->delay_reporting && lsep->delay_reporting) {
+		struct avdtp_service_capability *delay_reporting;
+
+		delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+								NULL, 0);
+		caps = g_slist_append(caps, delay_reporting);
+		new_stream->delay_reporting = TRUE;
+	}
+
+	g_slist_foreach(caps, copy_capabilities, &new_stream->caps);
+
+	/* Calculate total size of request */
+	for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) {
+		cap = l->data;
+		caps_len += cap->length + 2;
+	}
+
+	req = g_malloc0(sizeof(struct setconf_req) + caps_len);
+
+	req->int_seid = lsep->info.seid;
+	req->acp_seid = rsep->seid;
+
+	/* Copy the capabilities into the request */
+	for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) {
+		cap = l->data;
+		memcpy(ptr, cap, cap->length + 2);
+		ptr += cap->length + 2;
+	}
+
+	err = send_request(session, FALSE, new_stream,
+				AVDTP_SET_CONFIGURATION, req,
+				sizeof(struct setconf_req) + caps_len);
+	if (err < 0)
+		stream_free(new_stream);
+	else {
+		lsep->info.inuse = 1;
+		lsep->stream = new_stream;
+		rsep->stream = new_stream;
+		session->streams = g_slist_append(session->streams, new_stream);
+		if (stream)
+			*stream = new_stream;
+	}
+
+	g_free(req);
+
+	return err;
+}
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state > AVDTP_STATE_CONFIGURED)
+		return -EINVAL;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	return send_request(session, FALSE, stream, AVDTP_OPEN,
+							&req, sizeof(req));
+}
+
+static gboolean start_timeout(gpointer user_data)
+{
+	struct avdtp_stream *stream = user_data;
+	struct avdtp *session = stream->session;
+
+	stream->open_acp = FALSE;
+
+	if (avdtp_start(session, stream) < 0)
+		error("wait_timeout: avdtp_start failed");
+
+	stream->start_timer = 0;
+
+	return FALSE;
+}
+
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct start_req req;
+	int ret;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state != AVDTP_STATE_OPEN)
+		return -EINVAL;
+
+	/* Recommendation 12:
+	 *  If the RD has configured and opened a stream it is also responsible
+	 *  to start the streaming via GAVDP_START.
+	 */
+	if (stream->open_acp) {
+		/* If timer already active wait it */
+		if (stream->start_timer)
+			return 0;
+
+		stream->start_timer = g_timeout_add_seconds(START_TIMEOUT,
+								start_timeout,
+								stream);
+		return 0;
+	}
+
+	if (stream->close_int == TRUE) {
+		error("avdtp_start: rejecting start since close is initiated");
+		return -EINVAL;
+	}
+
+	if (stream->starting == TRUE) {
+		DBG("stream already started");
+		return -EINPROGRESS;
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.first_seid.seid = stream->rseid;
+
+	ret = send_request(session, FALSE, stream, AVDTP_START,
+							&req, sizeof(req));
+	if (ret == 0)
+		stream->starting = TRUE;
+
+	return ret;
+}
+
+int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
+		gboolean immediate)
+{
+	struct seid_req req;
+	int ret;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->close_int == TRUE) {
+		error("avdtp_close: rejecting since close is already initiated");
+		return -EINVAL;
+	}
+
+	/* If stream is not yet in the OPEN state, let's use ABORT_CMD */
+	if (stream->lsep->state < AVDTP_STATE_OPEN)
+		return avdtp_abort(session, stream);
+
+	if (immediate && session->req && stream == session->req->stream)
+		return avdtp_abort(session, stream);
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	ret = send_request(session, FALSE, stream, AVDTP_CLOSE,
+							&req, sizeof(req));
+	if (ret == 0)
+		stream->close_int = TRUE;
+
+	return ret;
+}
+
+int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state <= AVDTP_STATE_OPEN || stream->close_int)
+		return -EINVAL;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	return send_request(session, FALSE, stream, AVDTP_SUSPEND,
+							&req, sizeof(req));
+}
+
+int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+	int ret;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state == AVDTP_STATE_ABORTING)
+		return -EINVAL;
+
+	if (session->req && session->req->timeout > 0 &&
+						stream == session->req->stream)
+		return cancel_request(session, ECANCELED);
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	ret = send_request(session, TRUE, stream, AVDTP_ABORT,
+							&req, sizeof(req));
+	if (ret == 0)
+		stream->abort_int = TRUE;
+
+	return ret;
+}
+
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+							uint16_t delay)
+{
+	struct delay_req req;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state != AVDTP_STATE_CONFIGURED &&
+				stream->lsep->state != AVDTP_STATE_STREAMING)
+		return -EINVAL;
+
+	if (!stream->delay_reporting || session->version < 0x0103)
+		return -EINVAL;
+
+	stream->delay = delay;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+	req.delay = htons(delay);
+
+	return send_request(session, TRUE, stream, AVDTP_DELAY_REPORT,
+							&req, sizeof(req));
+}
+
+struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
+						uint8_t media_type,
+						uint8_t codec_type,
+						gboolean delay_reporting,
+						struct avdtp_sep_ind *ind,
+						struct avdtp_sep_cfm *cfm,
+						void *user_data)
+{
+	struct avdtp_local_sep *sep;
+	uint8_t seid = util_get_uid(&seids, MAX_SEID);
+
+	if (!seid)
+		return NULL;
+
+	sep = g_new0(struct avdtp_local_sep, 1);
+
+	sep->state = AVDTP_STATE_IDLE;
+	sep->info.seid = seid;
+	sep->info.type = type;
+	sep->info.media_type = media_type;
+	sep->codec = codec_type;
+	sep->ind = ind;
+	sep->cfm = cfm;
+	sep->user_data = user_data;
+	sep->delay_reporting = delay_reporting;
+
+	DBG("SEP %p registered: type:%d codec:%d seid:%d", sep,
+			sep->info.type, sep->codec, sep->info.seid);
+
+	queue_push_tail(lseps, sep);
+
+	return sep;
+}
+
+void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
+							uint16_t codec_id)
+{
+	sep->vndcodec_vendor = vendor_id;
+	sep->vndcodec_codec = codec_id;
+}
+
+int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep)
+{
+	if (!sep)
+		return -EINVAL;
+
+	if (sep->stream)
+		release_stream(sep->stream, sep->stream->session);
+
+	DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep,
+			sep->info.type, sep->codec, sep->info.seid);
+
+	util_clear_uid(&seids, sep->info.seid);
+	queue_remove(lseps, sep);
+	g_free(sep);
+
+	return 0;
+}
+
+const char *avdtp_strerror(struct avdtp_error *err)
+{
+	if (err->category == AVDTP_ERRNO)
+		return strerror(err->err.posix_errno);
+
+	switch (err->err.error_code) {
+	case AVDTP_BAD_HEADER_FORMAT:
+		return "Bad Header Format";
+	case AVDTP_BAD_LENGTH:
+		return "Bad Packet Length";
+	case AVDTP_BAD_ACP_SEID:
+		return "Bad Acceptor SEID";
+	case AVDTP_SEP_IN_USE:
+		return "Stream End Point in Use";
+	case AVDTP_SEP_NOT_IN_USE:
+		return "Stream End Point Not in Use";
+	case AVDTP_BAD_SERV_CATEGORY:
+		return "Bad Service Category";
+	case AVDTP_BAD_PAYLOAD_FORMAT:
+		return "Bad Payload format";
+	case AVDTP_NOT_SUPPORTED_COMMAND:
+		return "Command Not Supported";
+	case AVDTP_INVALID_CAPABILITIES:
+		return "Invalid Capabilities";
+	case AVDTP_BAD_RECOVERY_TYPE:
+		return "Bad Recovery Type";
+	case AVDTP_BAD_MEDIA_TRANSPORT_FORMAT:
+		return "Bad Media Transport Format";
+	case AVDTP_BAD_RECOVERY_FORMAT:
+		return "Bad Recovery Format";
+	case AVDTP_BAD_ROHC_FORMAT:
+		return "Bad Header Compression Format";
+	case AVDTP_BAD_CP_FORMAT:
+		return "Bad Content Protection Format";
+	case AVDTP_BAD_MULTIPLEXING_FORMAT:
+		return "Bad Multiplexing Format";
+	case AVDTP_UNSUPPORTED_CONFIGURATION:
+		return "Configuration not supported";
+	case AVDTP_BAD_STATE:
+		return "Bad State";
+	default:
+		return "Unknown error";
+	}
+}
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep)
+{
+	return sep->state;
+}
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream)
+{
+	return g_slist_find(session->streams, stream) ? TRUE : FALSE;
+}
diff --git a/repo/android/avdtp.h b/repo/android/avdtp.h
new file mode 100644
index 0000000..07516a8
--- /dev/null
+++ b/repo/android/avdtp.h
@@ -0,0 +1,291 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-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
+ *
+ */
+
+struct avdtp;
+struct avdtp_stream;
+struct avdtp_local_sep;
+struct avdtp_remote_sep;
+struct avdtp_error {
+	uint8_t category;
+	union {
+		uint8_t error_code;
+		int posix_errno;
+	} err;
+};
+
+#define AVDTP_PSM 25
+
+/* SEP capability categories */
+#define AVDTP_MEDIA_TRANSPORT			0x01
+#define AVDTP_REPORTING				0x02
+#define AVDTP_RECOVERY				0x03
+#define AVDTP_CONTENT_PROTECTION		0x04
+#define AVDTP_HEADER_COMPRESSION		0x05
+#define AVDTP_MULTIPLEXING			0x06
+#define AVDTP_MEDIA_CODEC			0x07
+#define AVDTP_DELAY_REPORTING			0x08
+#define AVDTP_ERRNO				0xff
+
+/* AVDTP error definitions */
+#define AVDTP_BAD_HEADER_FORMAT			0x01
+#define AVDTP_BAD_LENGTH			0x11
+#define AVDTP_BAD_ACP_SEID			0x12
+#define AVDTP_SEP_IN_USE			0x13
+#define AVDTP_SEP_NOT_IN_USE			0x14
+#define AVDTP_BAD_SERV_CATEGORY			0x17
+#define AVDTP_BAD_PAYLOAD_FORMAT		0x18
+#define AVDTP_NOT_SUPPORTED_COMMAND		0x19
+#define AVDTP_INVALID_CAPABILITIES		0x1A
+#define AVDTP_BAD_RECOVERY_TYPE			0x22
+#define AVDTP_BAD_MEDIA_TRANSPORT_FORMAT	0x23
+#define AVDTP_BAD_RECOVERY_FORMAT		0x25
+#define AVDTP_BAD_ROHC_FORMAT			0x26
+#define AVDTP_BAD_CP_FORMAT			0x27
+#define AVDTP_BAD_MULTIPLEXING_FORMAT		0x28
+#define AVDTP_UNSUPPORTED_CONFIGURATION		0x29
+#define AVDTP_BAD_STATE				0x31
+
+/* SEP types definitions */
+#define AVDTP_SEP_TYPE_SOURCE			0x00
+#define AVDTP_SEP_TYPE_SINK			0x01
+
+/* Media types definitions */
+#define AVDTP_MEDIA_TYPE_AUDIO			0x00
+#define AVDTP_MEDIA_TYPE_VIDEO			0x01
+#define AVDTP_MEDIA_TYPE_MULTIMEDIA		0x02
+
+typedef enum {
+	AVDTP_STATE_IDLE,
+	AVDTP_STATE_CONFIGURED,
+	AVDTP_STATE_OPEN,
+	AVDTP_STATE_STREAMING,
+	AVDTP_STATE_CLOSING,
+	AVDTP_STATE_ABORTING,
+} avdtp_state_t;
+
+struct avdtp_service_capability {
+	uint8_t category;
+	uint8_t length;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_media_codec_capability {
+	uint8_t rfa0:4;
+	uint8_t media_type:4;
+	uint8_t media_codec_type;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_media_codec_capability {
+	uint8_t media_type:4;
+	uint8_t rfa0:4;
+	uint8_t media_codec_type;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream,
+					avdtp_state_t old_state,
+					avdtp_state_t new_state,
+					struct avdtp_error *err,
+					void *user_data);
+
+typedef void (*avdtp_set_configuration_cb) (struct avdtp *session,
+						struct avdtp_stream *stream,
+						struct avdtp_error *err);
+
+/* Callbacks for when a reply is received to a command that we sent */
+struct avdtp_sep_cfm {
+	void (*set_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					struct avdtp_error *err,
+					void *user_data);
+	void (*get_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					struct avdtp_error *err,
+					void *user_data);
+	void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data);
+	void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data);
+	void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*reconfigure) (struct avdtp *session,
+				struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*delay_report) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+};
+
+/*
+ * Callbacks for indicating when we received a new command. The return value
+ * indicates whether the command should be rejected or accepted
+ */
+struct avdtp_sep_ind {
+	gboolean (*get_capability) (struct avdtp *session,
+					struct avdtp_local_sep *sep,
+					GSList **caps, uint8_t *err,
+					void *user_data);
+	gboolean (*set_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					GSList *caps,
+					avdtp_set_configuration_cb cb,
+					void *user_data);
+	gboolean (*get_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					uint8_t *err, void *user_data);
+	gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*suspend) (struct avdtp *session,
+				struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	void (*abort) (struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*reconfigure) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					uint8_t *err, void *user_data);
+	gboolean (*delayreport) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					uint8_t rseid, uint16_t delay,
+					uint8_t *err, void *user_data);
+};
+
+typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps,
+					struct avdtp_error *err, void *user_data);
+typedef void (*avdtp_disconnect_cb_t) (void *user_data);
+
+struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version,
+							struct queue *lseps);
+
+unsigned int avdtp_add_disconnect_cb(struct avdtp *session,
+						avdtp_disconnect_cb_t cb,
+						void *user_data);
+gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id);
+
+void avdtp_shutdown(struct avdtp *session);
+
+void avdtp_unref(struct avdtp *session);
+struct avdtp *avdtp_ref(struct avdtp *session);
+
+struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
+							const void *data,
+							int size);
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep);
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+			void *user_data);
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream);
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+					struct avdtp_stream *stream,
+					avdtp_stream_state_cb cb, void *data);
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+				struct avdtp_stream *stream,
+				unsigned int id);
+
+gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
+						size_t imtu, size_t omtu);
+gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
+					uint16_t *imtu, uint16_t *omtu,
+					GSList **caps);
+struct avdtp_service_capability *avdtp_stream_get_codec(
+						struct avdtp_stream *stream);
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+					GSList *caps);
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+						struct avdtp_stream *stream);
+
+int avdtp_set_configuration(struct avdtp *session,
+				struct avdtp_remote_sep *rsep,
+				struct avdtp_local_sep *lsep,
+				GSList *caps,
+				struct avdtp_stream **stream);
+
+int avdtp_get_configuration(struct avdtp *session,
+				struct avdtp_stream *stream);
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
+		gboolean immediate);
+int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+							uint16_t delay);
+
+struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
+						uint8_t media_type,
+						uint8_t codec_type,
+						gboolean delay_reporting,
+						struct avdtp_sep_ind *ind,
+						struct avdtp_sep_cfm *cfm,
+						void *user_data);
+void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
+							uint16_t codec_id);
+
+/* Find a matching pair of local and remote SEP ID's */
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+						struct avdtp_local_sep *lsep);
+
+int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep);
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep);
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id);
+const char *avdtp_strerror(struct avdtp_error *err);
+uint8_t avdtp_error_category(struct avdtp_error *err);
+int avdtp_error_error_code(struct avdtp_error *err);
+int avdtp_error_posix_errno(struct avdtp_error *err);
diff --git a/repo/android/avdtptest.c b/repo/android/avdtptest.c
new file mode 100644
index 0000000..ce34443
--- /dev/null
+++ b/repo/android/avdtptest.c
@@ -0,0 +1,909 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014 Intel 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 <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "btio/btio.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "avdtp.h"
+
+static GMainLoop *mainloop = NULL;
+static int dev_role = AVDTP_SEP_TYPE_SOURCE;
+static bool preconf = false;
+static struct avdtp *avdtp = NULL;
+struct avdtp_stream *avdtp_stream = NULL;
+struct avdtp_local_sep *local_sep = NULL;
+struct avdtp_remote_sep *remote_sep = NULL;
+static GIOChannel *io = NULL;
+static bool reject = false;
+static bdaddr_t src;
+static bdaddr_t dst;
+static uint16_t version = 0x0103;
+static guint media_player = 0;
+static guint media_recorder = 0;
+static guint idle_id = 0;
+static struct queue *lseps = NULL;
+
+static bool fragment = false;
+
+static enum {
+	CMD_GET_CONF,
+	CMD_OPEN,
+	CMD_START,
+	CMD_SUSPEND,
+	CMD_CLOSE,
+	CMD_ABORT,
+	CMD_DELAY,
+	CMD_NONE,
+} command = CMD_NONE;
+
+static const char sbc_codec[] = {0x00, 0x00, 0x11, 0x15, 0x02, 0x40};
+static const char sbc_media_frame[] = {
+	0x00, 0x60, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0x01, 0x9c, 0xfd, 0x40, 0xbd, 0xde, 0xa9, 0x75, 0x43, 0x20, 0x87, 0x64,
+	0x44, 0x32, 0x7f, 0xbe, 0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x7f, 0xbe,
+	0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x7f, 0xbe, 0xf7, 0x76, 0xfe, 0xf7,
+	0xbb, 0xbb, 0x80, 0x3e, 0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x83, 0x41,
+	0x07, 0x77, 0x09, 0x07, 0x43, 0xb3, 0x81, 0xbc, 0xf8, 0x77, 0x02, 0xe5,
+	0xa4, 0x3a, 0xa0, 0xcb, 0x38, 0xbb, 0x57, 0x90, 0xd9, 0x08, 0x9c, 0x1d,
+	0x86, 0x59, 0x01, 0x0c, 0x21, 0x44, 0x68, 0x35, 0xa8, 0x57, 0x97, 0x0e,
+	0x9b, 0xbb, 0x62, 0xc4, 0xca, 0x57, 0x04, 0xa1, 0xca, 0x3b, 0xa3, 0x48,
+	0xd2, 0x66, 0x11, 0x33, 0x6a, 0x3b, 0xb4, 0xbb, 0x08, 0x77, 0x17, 0x03,
+	0xb4, 0x3b, 0x79, 0x3b, 0x46, 0x97, 0x0e, 0xf7, 0x3d, 0xbb, 0x3d, 0x49,
+	0x25, 0x86, 0x88, 0xb4, 0xad, 0x3b, 0x62, 0xbb, 0xa4, 0x47, 0x29, 0x99,
+	0x3b, 0x3b, 0xaf, 0xc6, 0xd4, 0x37, 0x68, 0x94, 0x0a, 0xbb
+	};
+
+static void parse_command(const char *cmd)
+{
+	if (!strncmp(cmd, "getconf", sizeof("getconf"))) {
+		command = CMD_GET_CONF;
+	} else if (!strncmp(cmd, "open", sizeof("open"))) {
+		command = CMD_OPEN;
+	} else if (!strncmp(cmd, "start", sizeof("start"))) {
+		command = CMD_START;
+	} else if (!strncmp(cmd, "suspend", sizeof("suspend"))) {
+		command = CMD_SUSPEND;
+	} else if (!strncmp(cmd, "close", sizeof("close"))) {
+		command = CMD_CLOSE;
+	} else if (!strncmp(cmd, "abort", sizeof("abort"))) {
+		command = CMD_ABORT;
+	} else if (!strncmp(cmd, "delay", sizeof("delay"))) {
+		command = CMD_DELAY;
+	} else {
+		printf("Unknown command '%s'\n", cmd);
+		printf("(getconf open start suspend close abort delay)\n");
+		exit(1);
+	}
+}
+
+static void send_command(void)
+{
+	avdtp_state_t state = avdtp_sep_get_state(local_sep);
+
+	switch (command) {
+	case CMD_GET_CONF:
+		avdtp_get_configuration(avdtp, avdtp_stream);
+		break;
+	case CMD_OPEN:
+		if (state == AVDTP_STATE_CONFIGURED)
+			avdtp_open(avdtp, avdtp_stream);
+		break;
+	case CMD_START:
+		if (state == AVDTP_STATE_OPEN)
+			avdtp_start(avdtp, avdtp_stream);
+		break;
+	case CMD_SUSPEND:
+		if (state == AVDTP_STATE_STREAMING)
+			avdtp_suspend(avdtp , avdtp_stream);
+		break;
+	case CMD_CLOSE:
+		if (state == AVDTP_STATE_STREAMING)
+			avdtp_close(avdtp, avdtp_stream, FALSE);
+		break;
+	case CMD_ABORT:
+		avdtp_abort(avdtp , avdtp_stream);
+		break;
+	case CMD_DELAY:
+		avdtp_delay_report(avdtp , avdtp_stream , 250);
+		break;
+	case CMD_NONE:
+	default:
+		break;
+	}
+}
+
+static gboolean media_writer(gpointer user_data)
+{
+	uint16_t omtu;
+	int fd;
+	int to_write;
+
+	if (!avdtp_stream_get_transport(avdtp_stream, &fd, NULL, &omtu, NULL))
+		return TRUE;
+
+	if (omtu < sizeof(sbc_media_frame))
+		to_write = omtu;
+	else
+		to_write = sizeof(sbc_media_frame);
+
+	if (write(fd, sbc_media_frame, to_write) < 0)
+		return TRUE;
+
+	send_command();
+
+	return TRUE;
+}
+
+static bool start_media_player(void)
+{
+	int fd;
+	uint16_t omtu;
+
+	printf("Media streaming started\n");
+
+	if (media_player || !avdtp_stream)
+		return false;
+
+	if (!avdtp_stream_get_transport(avdtp_stream, &fd, NULL, &omtu, NULL))
+		return false;
+
+	media_player = g_timeout_add(200, media_writer, NULL);
+	if (!media_player)
+		return false;
+
+	return true;
+}
+
+static void stop_media_player(void)
+{
+	if (!media_player)
+		return;
+
+	printf("Media streaming stopped\n");
+
+	g_source_remove(media_player);
+	media_player = 0;
+}
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_header {
+	unsigned cc:4;
+	unsigned x:1;
+	unsigned p:1;
+	unsigned v:2;
+
+	unsigned pt:7;
+	unsigned m:1;
+
+	uint16_t sequence_number;
+	uint32_t timestamp;
+	uint32_t ssrc;
+	uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_header {
+	unsigned v:2;
+	unsigned p:1;
+	unsigned x:1;
+	unsigned cc:4;
+
+	unsigned m:1;
+	unsigned pt:7;
+
+	uint16_t sequence_number;
+	uint32_t timestamp;
+	uint32_t ssrc;
+	uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+static gboolean media_reader(GIOChannel *source, GIOCondition condition,
+								gpointer data)
+{
+	char buf[UINT16_MAX];
+	struct rtp_header *rtp = (void *) buf;
+	static bool decode = false;
+	uint16_t imtu;
+	int fd, ret;
+
+	if (!avdtp_stream_get_transport(avdtp_stream, &fd, &imtu, NULL, NULL))
+		return TRUE;
+
+	ret = read(fd, buf, imtu);
+	if (ret < 0) {
+		printf("Reading failed (%s)\n", strerror(errno));
+		return TRUE;
+	}
+
+	if (ret < (int) sizeof(*rtp)) {
+		printf("Not enough media data received (%u bytes)", ret);
+		return TRUE;
+	}
+
+	if (!decode) {
+		printf("V=%u P=%u X=%u CC=%u M=%u PT=%u SeqNr=%d\n",
+			rtp->v, rtp->p, rtp->x, rtp->cc, rtp->m, rtp->pt,
+			be16_to_cpu(rtp->sequence_number));
+		decode = true;
+	}
+
+	send_command();
+
+	return TRUE;
+}
+
+static bool start_media_recorder(void)
+{
+	int fd;
+	uint16_t omtu;
+	GIOChannel *chan;
+
+	printf("Media recording started\n");
+
+	if (media_recorder || !avdtp_stream)
+		return false;
+
+	if (!avdtp_stream_get_transport(avdtp_stream, &fd, NULL, &omtu, NULL))
+		return false;
+
+	chan = g_io_channel_unix_new(fd);
+
+	media_recorder = g_io_add_watch(chan, G_IO_IN, media_reader, NULL);
+	g_io_channel_unref(chan);
+
+	if (!media_recorder)
+		return false;
+
+	return true;
+}
+
+static void stop_media_recorder(void)
+{
+	if (!media_recorder)
+		return;
+
+	printf("Media recording stopped\n");
+
+	g_source_remove(media_recorder);
+	media_recorder = 0;
+}
+
+static void set_configuration_cfm(struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					struct avdtp_error *err,
+					void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (preconf)
+		avdtp_open(avdtp, avdtp_stream);
+}
+
+static void get_configuration_cfm(struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					struct avdtp_error *err,
+					void *user_data)
+	{
+	printf("%s\n", __func__);
+}
+
+static void disconnect_cb(void *user_data)
+{
+	printf("Disconnected\n");
+
+	g_main_loop_quit(mainloop);
+}
+
+static void discover_cb(struct avdtp *session, GSList *seps,
+				struct avdtp_error *err, void *user_data)
+{
+	struct avdtp_service_capability *service;
+	GSList *caps = NULL;
+	int ret;
+
+	remote_sep = avdtp_find_remote_sep(avdtp, local_sep);
+	if (!remote_sep) {
+		printf("Unable to find matching endpoint\n");
+		avdtp_shutdown(session);
+		return;
+	}
+
+	printf("Matching endpoint found\n");
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
+	caps = g_slist_append(caps, service);
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, sbc_codec,
+							sizeof(sbc_codec));
+	caps = g_slist_append(caps, service);
+
+	ret = avdtp_set_configuration(avdtp, remote_sep, local_sep, caps,
+								&avdtp_stream);
+
+	g_slist_free_full(caps, g_free);
+
+	if (ret < 0) {
+		printf("Failed to set configuration (%s)\n", strerror(-ret));
+		avdtp_shutdown(session);
+	}
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	uint16_t imtu, omtu;
+	GError *gerr = NULL;
+	int fd;
+
+	if (err) {
+		printf("%s\n", err->message);
+		g_main_loop_quit(mainloop);
+		return;
+	}
+
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_IMTU, &imtu,
+			BT_IO_OPT_OMTU, &omtu,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		printf("%s\n", gerr->message);
+		g_main_loop_quit(mainloop);
+		return;
+	}
+
+	printf("Connected (imtu=%d omtu=%d)\n", imtu, omtu);
+
+	fd = g_io_channel_unix_get_fd(chan);
+
+	if (avdtp && avdtp_stream) {
+		if (!avdtp_stream_set_transport(avdtp_stream, fd, imtu, omtu)) {
+			printf("avdtp_stream_set_transport: failed\n");
+			g_main_loop_quit(mainloop);
+		}
+
+		g_io_channel_set_close_on_unref(chan, FALSE);
+
+		send_command();
+
+		return;
+	}
+
+	avdtp = avdtp_new(fd, imtu, omtu, version, lseps);
+	if (!avdtp) {
+		printf("Failed to create avdtp instance\n");
+		g_main_loop_quit(mainloop);
+		return;
+	}
+
+	avdtp_add_disconnect_cb(avdtp, disconnect_cb, NULL);
+
+	if (preconf) {
+		int ret;
+
+		ret = avdtp_discover(avdtp, discover_cb, NULL);
+		if (ret < 0) {
+			printf("avdtp_discover failed: %s", strerror(-ret));
+			g_main_loop_quit(mainloop);
+		}
+	}
+}
+
+static GIOChannel *do_connect(GError **err)
+{
+	if (fragment)
+		return bt_io_connect(connect_cb, NULL, NULL, err,
+					BT_IO_OPT_SOURCE_BDADDR, &src,
+					BT_IO_OPT_DEST_BDADDR, &dst,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_PSM, AVDTP_PSM,
+					BT_IO_OPT_MTU, 48,
+					BT_IO_OPT_INVALID);
+
+	return bt_io_connect(connect_cb, NULL, NULL, err,
+				BT_IO_OPT_SOURCE_BDADDR, &src,
+				BT_IO_OPT_DEST_BDADDR, &dst,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_PSM, AVDTP_PSM,
+				BT_IO_OPT_INVALID);
+}
+
+static void open_cfm(struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	GError *gerr = NULL;
+
+	printf("%s\n", __func__);
+
+	do_connect(&gerr);
+	if (gerr) {
+		printf("connect failed: %s\n", gerr->message);
+		g_error_free(gerr);
+		g_main_loop_quit(mainloop);
+	}
+}
+
+static void start_cfm(struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		start_media_player();
+	else
+		start_media_recorder();
+}
+
+static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream,
+			struct avdtp_error *err, void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		stop_media_player();
+	else
+		stop_media_recorder();
+}
+
+static void close_cfm(struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream,
+			struct avdtp_error *err, void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		stop_media_player();
+	else
+		stop_media_recorder();
+
+	avdtp_stream = NULL;
+}
+
+static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream,
+			struct avdtp_error *err, void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		stop_media_player();
+	else
+		stop_media_recorder();
+
+	avdtp_stream = NULL;
+}
+
+static void reconfigure_cfm(struct avdtp *session,
+				struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data)
+{
+	printf("%s\n", __func__);
+}
+
+static void delay_report_cfm(struct avdtp *session,
+				struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data)
+{
+	printf("%s\n", __func__);
+}
+
+static struct avdtp_sep_cfm sep_cfm = {
+	.set_configuration	= set_configuration_cfm,
+	.get_configuration	= get_configuration_cfm,
+	.open			= open_cfm,
+	.start			= start_cfm,
+	.suspend		= suspend_cfm,
+	.close			= close_cfm,
+	.abort			= abort_cfm,
+	.reconfigure		= reconfigure_cfm,
+	.delay_report		= delay_report_cfm,
+};
+
+static gboolean get_capability_ind(struct avdtp *session,
+					struct avdtp_local_sep *sep,
+					GSList **caps, uint8_t *err,
+					void *user_data)
+{
+	struct avdtp_service_capability *service;
+	int i;
+
+	printf("%s\n", __func__);
+
+	if (idle_id > 0) {
+		g_source_remove(idle_id);
+		idle_id = 0;
+	}
+
+	if (reject)
+		return FALSE;
+
+	*caps = NULL;
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
+	*caps = g_slist_append(*caps, service);
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, sbc_codec,
+						sizeof(sbc_codec));
+	*caps = g_slist_append(*caps, service);
+
+	if (fragment)
+		for (i = 0; i < 10; i++) {
+			service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC,
+							sbc_codec,
+							sizeof(sbc_codec));
+			*caps = g_slist_append(*caps, service);
+		}
+
+	return TRUE;
+}
+
+static gboolean set_configuration_ind(struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					GSList *caps,
+					avdtp_set_configuration_cb cb,
+					void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	if (idle_id > 0) {
+		g_source_remove(idle_id);
+		idle_id = 0;
+	}
+
+	avdtp_stream = stream;
+
+	cb(session, stream, NULL);
+
+	send_command();
+
+	return TRUE;
+}
+
+static gboolean get_configuration_ind(struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					uint8_t *err, void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	send_command();
+
+	return TRUE;
+}
+
+static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		start_media_player();
+	else
+		start_media_recorder();
+
+	send_command();
+
+	return TRUE;
+}
+
+static gboolean suspend_ind(struct avdtp *session,
+				struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		stop_media_player();
+	else
+		stop_media_recorder();
+
+	return TRUE;
+}
+
+static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		stop_media_player();
+	else
+		stop_media_recorder();
+
+	avdtp_stream = NULL;
+
+	return TRUE;
+}
+
+static void abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, uint8_t *err,
+			void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		stop_media_player();
+	else
+		stop_media_recorder();
+
+	avdtp_stream = NULL;
+}
+
+static gboolean reconfigure_ind(struct avdtp *session,
+				struct avdtp_local_sep *lsep,
+				uint8_t *err, void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean delayreport_ind(struct avdtp *session,
+				struct avdtp_local_sep *lsep,
+				uint8_t rseid, uint16_t delay,
+				uint8_t *err, void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	return TRUE;
+}
+
+static struct avdtp_sep_ind sep_ind = {
+	.get_capability		= get_capability_ind,
+	.set_configuration	= set_configuration_ind,
+	.get_configuration	= get_configuration_ind,
+	.open			= open_ind,
+	.close			= close_ind,
+	.start			= start_ind,
+	.suspend		= suspend_ind,
+	.abort			= abort_ind,
+	.reconfigure		= reconfigure_ind,
+	.delayreport		= delayreport_ind,
+};
+
+static void usage(void)
+{
+	printf("avdtptest - AVDTP testing ver %s\n", VERSION);
+	printf("Usage:\n"
+		"\tavdtptest [options]\n");
+	printf("options:\n"
+		"\t-d <device_role>   SRC (source) or SINK (sink)\n"
+		"\t-i <hcidev>        HCI adapter\n"
+		"\t-c <bdaddr>        connect\n"
+		"\t-l                 listen\n"
+		"\t-r                 reject commands\n"
+		"\t-f                 fragment\n"
+		"\t-p                 configure stream\n"
+		"\t-s <command>       send command\n"
+		"\t-v <version>       set version (0x0100, 0x0102, 0x0103\n");
+}
+
+static struct option main_options[] = {
+	{ "help",		0, 0, 'h' },
+	{ "device_role",	1, 0, 'd' },
+	{ "adapter",		1, 0, 'i' },
+	{ "connect",		1, 0, 'c' },
+	{ "listen",		0, 0, 'l' },
+	{ "reject",		0, 0, 'r' },
+	{ "fragment",		0, 0, 'f' },
+	{ "preconf",		0, 0, 'p' },
+	{ "send",		1, 0, 's' },
+	{ "version",		1, 0, 'v' },
+	{ 0, 0, 0, 0 }
+};
+
+static GIOChannel *do_listen(GError **err)
+{
+	if (fragment)
+		return bt_io_listen(connect_cb, NULL, NULL, NULL, err,
+					BT_IO_OPT_SOURCE_BDADDR, &src,
+					BT_IO_OPT_PSM, AVDTP_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_MTU, 48,
+					BT_IO_OPT_INVALID);
+
+	return bt_io_listen(connect_cb, NULL, NULL, NULL, err,
+					BT_IO_OPT_SOURCE_BDADDR, &src,
+					BT_IO_OPT_PSM, AVDTP_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+}
+
+int main(int argc, char *argv[])
+{
+	GError *err = NULL;
+	int opt;
+
+	bacpy(&src, BDADDR_ANY);
+	bacpy(&dst, BDADDR_ANY);
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+	if (!mainloop) {
+		printf("Failed to create main loop\n");
+
+		exit(1);
+	}
+
+	while ((opt = getopt_long(argc, argv, "d:hi:s:c:v:lrfp",
+						main_options, NULL)) != EOF) {
+		switch (opt) {
+		case 'i':
+			if (!strncmp(optarg, "hci", 3))
+				hci_devba(atoi(optarg + 3), &src);
+			else
+				str2ba(optarg, &src);
+			break;
+		case 'd':
+			if (!strncasecmp(optarg, "SRC", sizeof("SRC"))) {
+				dev_role = AVDTP_SEP_TYPE_SOURCE;
+			} else if (!strncasecmp(optarg, "SINK",
+							sizeof("SINK"))) {
+				dev_role = AVDTP_SEP_TYPE_SINK;
+			} else {
+				usage();
+				exit(1);
+			}
+			break;
+		case 'c':
+			if (str2ba(optarg, &dst) < 0) {
+				usage();
+				exit(1);
+			}
+			break;
+		case 'l':
+			bacpy(&dst, BDADDR_ANY);
+			break;
+		case 'r':
+			reject = true;
+			break;
+		case 'f':
+			fragment = true;
+			break;
+		case 'p':
+			preconf = true;
+			break;
+		case 's':
+			parse_command(optarg);
+			break;
+		case 'v':
+			version = strtol(optarg, NULL, 0);
+			if (version != 0x0100 && version != 0x0102 &&
+							version != 0x0103) {
+				printf("invalid version\n");
+				exit(1);
+			}
+
+			break;
+		case 'h':
+			usage();
+			exit(0);
+		default:
+			usage();
+			exit(1);
+		}
+	}
+
+	lseps = queue_new();
+
+	local_sep = avdtp_register_sep(lseps, dev_role, AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, TRUE, &sep_ind, &sep_cfm, NULL);
+	if (!local_sep) {
+		printf("Failed to register sep\n");
+		exit(1);
+	}
+
+	queue_push_tail(lseps, local_sep);
+
+	if (!bacmp(&dst, BDADDR_ANY)) {
+		printf("Listening...\n");
+		io = do_listen(&err);
+	} else {
+		printf("Connecting...\n");
+		io = do_connect(&err);
+	}
+
+	if (!io) {
+		printf("Failed: %s\n", err->message);
+		g_error_free(err);
+		exit(1);
+	}
+
+	g_main_loop_run(mainloop);
+
+	printf("Done\n");
+
+	queue_destroy(lseps, NULL);
+
+	avdtp_unref(avdtp);
+	avdtp = NULL;
+
+	g_main_loop_unref(mainloop);
+	mainloop = NULL;
+
+	return 0;
+}
diff --git a/repo/android/avrcp-lib.c b/repo/android/avrcp-lib.c
new file mode 100644
index 0000000..4edfd0e
--- /dev/null
+++ b/repo/android/avrcp-lib.c
@@ -0,0 +1,3615 @@
+/*
+ *
+ *  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <glib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "lib/bluetooth.h"
+
+#include "src/shared/util.h"
+#include "src/log.h"
+
+#include "avctp.h"
+#include "avrcp-lib.h"
+
+
+/* Packet types */
+#define AVRCP_PACKET_TYPE_SINGLE		0x00
+#define AVRCP_PACKET_TYPE_START			0x01
+#define AVRCP_PACKET_TYPE_CONTINUING		0x02
+#define AVRCP_PACKET_TYPE_END			0x03
+
+#define AVRCP_CHARSET_UTF8	0x006a
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avrcp_header {
+	uint8_t company_id[3];
+	uint8_t pdu_id;
+	uint8_t packet_type:2;
+	uint8_t rsvd:6;
+	uint16_t params_len;
+	uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_HEADER_LENGTH 7
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avrcp_header {
+	uint8_t company_id[3];
+	uint8_t pdu_id;
+	uint8_t rsvd:6;
+	uint8_t packet_type:2;
+	uint16_t params_len;
+	uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_HEADER_LENGTH 7
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct avrcp_browsing_header {
+	uint8_t pdu_id;
+	uint16_t params_len;
+	uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_BROWSING_HEADER_LENGTH 3
+
+struct get_capabilities_req {
+	uint8_t cap;
+	uint8_t params[0];
+} __attribute__ ((packed));
+
+struct get_capabilities_rsp {
+	uint8_t cap;
+	uint8_t number;
+	uint8_t params[0];
+} __attribute__ ((packed));
+
+struct list_attributes_rsp {
+	uint8_t number;
+	uint8_t params[0];
+} __attribute__ ((packed));
+
+struct list_values_req {
+	uint8_t attr;
+} __attribute__ ((packed));
+
+struct list_values_rsp {
+	uint8_t number;
+	uint8_t params[0];
+} __attribute__ ((packed));
+
+struct get_value_req {
+	uint8_t number;
+	uint8_t attrs[0];
+} __attribute__ ((packed));
+
+struct attr_value {
+	uint8_t attr;
+	uint8_t value;
+} __attribute__ ((packed));
+
+struct value_rsp {
+	uint8_t number;
+	struct attr_value values[0];
+} __attribute__ ((packed));
+
+struct set_value_req {
+	uint8_t number;
+	struct attr_value values[0];
+} __attribute__ ((packed));
+
+struct get_attribute_text_req {
+	uint8_t number;
+	uint8_t attrs[0];
+} __attribute__ ((packed));
+
+struct text_value {
+	uint8_t attr;
+	uint16_t charset;
+	uint8_t len;
+	char data[0];
+} __attribute__ ((packed));
+
+struct get_attribute_text_rsp {
+	uint8_t number;
+	struct text_value values[0];
+} __attribute__ ((packed));
+
+struct get_value_text_req {
+	uint8_t attr;
+	uint8_t number;
+	uint8_t values[0];
+} __attribute__ ((packed));
+
+struct get_value_text_rsp {
+	uint8_t number;
+	struct text_value values[0];
+} __attribute__ ((packed));
+
+struct media_item {
+	uint32_t attr;
+	uint16_t charset;
+	uint16_t len;
+	char data[0];
+} __attribute__ ((packed));
+
+struct get_element_attributes_req {
+	uint64_t id;
+	uint8_t number;
+	uint32_t attrs[0];
+} __attribute__ ((packed));
+
+struct get_element_attributes_rsp {
+	uint8_t number;
+	struct media_item items[0];
+} __attribute__ ((packed));
+
+struct get_play_status_rsp {
+	uint32_t duration;
+	uint32_t position;
+	uint8_t status;
+} __attribute__ ((packed));
+
+struct register_notification_req {
+	uint8_t event;
+	uint32_t interval;
+} __attribute__ ((packed));
+
+struct register_notification_rsp {
+	uint8_t event;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+struct set_volume_req {
+	uint8_t value;
+} __attribute__ ((packed));
+
+struct set_volume_rsp {
+	uint8_t value;
+} __attribute__ ((packed));
+
+struct set_addressed_req {
+	uint16_t id;
+} __attribute__ ((packed));
+
+struct set_addressed_rsp {
+	uint8_t status;
+} __attribute__ ((packed));
+
+struct set_browsed_req {
+	uint16_t id;
+} __attribute__ ((packed));
+
+struct set_browsed_rsp {
+	uint8_t status;
+	uint16_t counter;
+	uint32_t items;
+	uint16_t charset;
+	uint8_t depth;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+struct get_folder_items_req {
+	uint8_t scope;
+	uint32_t start;
+	uint32_t end;
+	uint8_t number;
+	uint32_t attrs[0];
+} __attribute__ ((packed));
+
+struct get_folder_items_rsp {
+	uint8_t status;
+	uint16_t counter;
+	uint16_t number;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+struct change_path_req {
+	uint16_t counter;
+	uint8_t direction;
+	uint64_t uid;
+} __attribute__ ((packed));
+
+struct change_path_rsp {
+	uint8_t status;
+	uint32_t items;
+} __attribute__ ((packed));
+
+struct get_item_attributes_req {
+	uint8_t scope;
+	uint64_t uid;
+	uint16_t counter;
+	uint8_t number;
+	uint32_t attrs[0];
+} __attribute__ ((packed));
+
+struct get_item_attributes_rsp {
+	uint8_t status;
+	uint8_t number;
+	struct media_item items[0];
+} __attribute__ ((packed));
+
+struct play_item_req {
+	uint8_t scope;
+	uint64_t uid;
+	uint16_t counter;
+} __attribute__ ((packed));
+
+struct play_item_rsp {
+	uint8_t status;
+} __attribute__ ((packed));
+
+struct search_req {
+	uint16_t charset;
+	uint16_t len;
+	char string[0];
+} __attribute__ ((packed));
+
+struct search_rsp {
+	uint8_t status;
+	uint16_t counter;
+	uint32_t items;
+} __attribute__ ((packed));
+
+struct add_to_now_playing_req {
+	uint8_t scope;
+	uint64_t uid;
+	uint16_t counter;
+} __attribute__ ((packed));
+
+struct add_to_now_playing_rsp {
+	uint8_t status;
+} __attribute__ ((packed));
+
+struct avrcp_control_handler {
+	uint8_t id;
+	uint8_t code;
+	uint8_t rsp;
+	ssize_t (*func) (struct avrcp *session, uint8_t transaction,
+			uint16_t params_len, uint8_t *params, void *user_data);
+};
+
+struct avrcp_browsing_handler {
+	uint8_t id;
+	ssize_t (*func) (struct avrcp *session, uint8_t transaction,
+			uint16_t params_len, uint8_t *params, void *user_data);
+};
+
+struct avrcp_continuing {
+	uint8_t pdu_id;
+	struct iovec pdu;
+};
+
+struct avrcp {
+	struct avctp *conn;
+	struct avrcp_player *player;
+
+	const struct avrcp_control_handler *control_handlers;
+	void *control_data;
+	unsigned int control_id;
+	uint16_t control_mtu;
+
+	struct avrcp_continuing *continuing;
+
+	const struct avrcp_passthrough_handler *passthrough_handlers;
+	void *passthrough_data;
+	unsigned int passthrough_id;
+
+	const struct avrcp_browsing_handler *browsing_handlers;
+	void *browsing_data;
+	unsigned int browsing_id;
+
+	avrcp_destroy_cb_t destroy;
+	void *destroy_data;
+};
+
+struct avrcp_player {
+	const struct avrcp_control_ind *ind;
+	const struct avrcp_control_cfm *cfm;
+
+	void *user_data;
+};
+
+static inline uint32_t ntoh24(const uint8_t src[3])
+{
+	return src[0] << 16 | src[1] << 8 | src[2];
+}
+
+static inline void hton24(uint8_t dst[3], uint32_t src)
+{
+	dst[0] = (src & 0xff0000) >> 16;
+	dst[1] = (src & 0x00ff00) >> 8;
+	dst[2] = (src & 0x0000ff);
+}
+
+static void continuing_free(struct avrcp_continuing *continuing)
+{
+	g_free(continuing->pdu.iov_base);
+	g_free(continuing);
+}
+
+void avrcp_shutdown(struct avrcp *session)
+{
+	if (session->conn) {
+		if (session->control_id > 0)
+			avctp_unregister_pdu_handler(session->conn,
+							session->control_id);
+		if (session->passthrough_id > 0)
+			avctp_unregister_passthrough_handler(session->conn,
+						session->passthrough_id);
+
+		if (session->browsing_id > 0)
+			avctp_unregister_browsing_pdu_handler(session->conn,
+							session->browsing_id);
+
+		/* clear destroy callback that would call shutdown again */
+		avctp_set_destroy_cb(session->conn, NULL, NULL);
+		avctp_shutdown(session->conn);
+	}
+
+	if (session->destroy)
+		session->destroy(session->destroy_data);
+
+	if (session->continuing)
+		continuing_free(session->continuing);
+
+	g_free(session->player);
+	g_free(session);
+}
+
+static struct avrcp_header *parse_pdu(uint8_t *operands, size_t operand_count)
+{
+	struct avrcp_header *pdu;
+
+	if (!operands || operand_count < sizeof(*pdu)) {
+		error("AVRCP: packet too small (%zu bytes)", operand_count);
+		return NULL;
+	}
+
+	pdu = (void *) operands;
+	pdu->params_len = ntohs(pdu->params_len);
+
+	if (operand_count != pdu->params_len + sizeof(*pdu)) {
+		error("AVRCP: invalid parameter length (%u bytes)",
+							pdu->params_len);
+		return NULL;
+	}
+
+	return pdu;
+}
+
+static struct avrcp_browsing_header *parse_browsing_pdu(uint8_t *operands,
+							size_t operand_count)
+{
+	struct avrcp_browsing_header *pdu;
+
+	if (!operands || operand_count < sizeof(*pdu)) {
+		error("AVRCP: packet too small (%zu bytes)", operand_count);
+		return NULL;
+	}
+
+	pdu = (void *) operands;
+	pdu->params_len = ntohs(pdu->params_len);
+
+	if (operand_count != pdu->params_len + sizeof(*pdu)) {
+		error("AVRCP: invalid parameter length (%u bytes)",
+							pdu->params_len);
+		return NULL;
+	}
+
+	return pdu;
+}
+
+static uint8_t errno2status(int err)
+{
+	switch (err) {
+	case -ENOSYS:
+		return AVRCP_STATUS_INVALID_COMMAND;
+	case -EINVAL:
+		return AVRCP_STATUS_INVALID_PARAM;
+	case 0:
+		return AVRCP_STATUS_SUCCESS;
+	case -ENOTDIR:
+		return AVRCP_STATUS_NOT_DIRECTORY;
+	case -EBADRQC:
+		return AVRCP_STATUS_INVALID_SCOPE;
+	case -ERANGE:
+		return AVRCP_STATUS_OUT_OF_BOUNDS;
+	case -ENOENT:
+		return AVRCP_STATUS_DOES_NOT_EXIST;
+	default:
+		return AVRCP_STATUS_INTERNAL_ERROR;
+	}
+}
+
+static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
+					uint8_t *code, uint8_t *subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	const struct avrcp_control_handler *handler;
+	struct avrcp_header *pdu;
+	uint32_t company_id;
+	ssize_t ret;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		pdu = (void *) operands;
+		pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+		goto reject;
+	}
+
+	company_id = ntoh24(pdu->company_id);
+	if (company_id != IEEEID_BTSIG) {
+		*code = AVC_CTYPE_NOT_IMPLEMENTED;
+		return 0;
+	}
+
+	DBG("AVRCP PDU 0x%02X, len 0x%04X", pdu->pdu_id, pdu->params_len);
+
+	pdu->packet_type = 0;
+	pdu->rsvd = 0;
+
+	if (!session->control_handlers)
+		goto reject;
+
+	for (handler = session->control_handlers; handler->id; handler++) {
+		if (handler->id == pdu->pdu_id)
+			break;
+	}
+
+	if (handler->id != pdu->pdu_id || handler->code != *code) {
+		pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+		goto reject;
+	}
+
+	if (!handler->func) {
+		pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+		goto reject;
+	}
+
+	ret = handler->func(session, transaction, pdu->params_len, pdu->params,
+							session->control_data);
+	if (ret == 0)
+		return -EAGAIN;
+
+	if (ret < 0) {
+		if (ret == -EAGAIN)
+			return ret;
+		pdu->params[0] = errno2status(ret);
+		goto reject;
+	}
+
+	*code = handler->rsp;
+	pdu->params_len = htons(ret);
+
+	return AVRCP_HEADER_LENGTH + ret;
+
+reject:
+	pdu->params_len = htons(1);
+	*code = AVC_CTYPE_REJECTED;
+
+	return AVRCP_HEADER_LENGTH + 1;
+}
+
+static bool handle_passthrough_pdu(struct avctp *conn, uint8_t op,
+						bool pressed, void *user_data)
+{
+	struct avrcp *session = user_data;
+	const struct avrcp_passthrough_handler *handler;
+
+	if (!session->passthrough_handlers)
+		return false;
+
+	for (handler = session->passthrough_handlers; handler->func;
+								handler++) {
+		if (handler->op == op)
+			break;
+	}
+
+	if (handler->func == NULL)
+		return false;
+
+	return handler->func(session, pressed, session->passthrough_data);
+}
+
+static void disconnect_cb(void *data)
+{
+	struct avrcp *session = data;
+
+	session->conn = NULL;
+
+	avrcp_shutdown(session);
+}
+
+struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version)
+{
+	struct avrcp *session;
+
+	session = g_new0(struct avrcp, 1);
+
+	session->conn = avctp_new(fd, imtu, omtu, version);
+	if (!session->conn) {
+		g_free(session);
+		return NULL;
+	}
+
+	session->passthrough_id = avctp_register_passthrough_handler(
+							session->conn,
+							handle_passthrough_pdu,
+							session);
+	session->control_id = avctp_register_pdu_handler(session->conn,
+							AVC_OP_VENDORDEP,
+							handle_vendordep_pdu,
+							session);
+	session->control_mtu = omtu - AVC_DATA_OFFSET;
+
+	/*
+	 * 27.1.2 AV/C Command Frame
+	 * An AV/C command frame contains up to 512 octets of data
+	 */
+	if (session->control_mtu > AVC_DATA_MTU)
+		session->control_mtu = AVC_DATA_MTU;
+
+	avctp_set_destroy_cb(session->conn, disconnect_cb, session);
+
+	return session;
+}
+
+static ssize_t handle_browsing_pdu(struct avctp *conn,
+					uint8_t transaction, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	const struct avrcp_browsing_handler *handler;
+	struct avrcp_browsing_header *pdu;
+	int ret;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		pdu = (void *) operands;
+		pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+		goto reject;
+	}
+
+	DBG("AVRCP Browsing PDU 0x%02X, len 0x%04X", pdu->pdu_id,
+							pdu->params_len);
+
+	if (!session->browsing_handlers) {
+		pdu->pdu_id = AVRCP_GENERAL_REJECT;
+		pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR;
+		goto reject;
+	}
+
+	for (handler = session->browsing_handlers; handler->id; handler++) {
+		if (handler->id == pdu->pdu_id)
+			break;
+	}
+
+	if (handler->id != pdu->pdu_id) {
+		pdu->pdu_id = AVRCP_GENERAL_REJECT;
+		pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+		goto reject;
+	}
+
+	if (!handler->func) {
+		pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+		goto reject;
+	}
+
+	ret = handler->func(session, transaction, pdu->params_len, pdu->params,
+							session->control_data);
+	if (ret == 0)
+		return -EAGAIN;
+
+	if (ret < 0) {
+		if (ret == -EAGAIN)
+			return ret;
+		pdu->params[0] = errno2status(ret);
+		goto reject;
+	}
+
+	pdu->params_len = htons(ret);
+
+	return AVRCP_BROWSING_HEADER_LENGTH + ret;
+
+reject:
+	pdu->params_len = htons(1);
+
+	return AVRCP_BROWSING_HEADER_LENGTH + 1;
+}
+
+static void browsing_disconnect_cb(void *data)
+{
+	struct avrcp *session = data;
+
+	session->browsing_id = 0;
+}
+
+int avrcp_connect_browsing(struct avrcp *session, int fd, size_t imtu,
+								size_t omtu)
+{
+	int err;
+
+	err = avctp_connect_browsing(session->conn, fd, imtu, omtu);
+	if (err < 0)
+		return err;
+
+	session->browsing_id = avctp_register_browsing_pdu_handler(
+							session->conn,
+							handle_browsing_pdu,
+							session,
+							browsing_disconnect_cb);
+
+	return 0;
+}
+
+void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb,
+							void *user_data)
+{
+	session->destroy = cb;
+	session->destroy_data = user_data;
+}
+
+static ssize_t get_capabilities(struct avrcp *session, uint8_t transaction,
+				uint16_t params_len, uint8_t *params,
+				void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct get_capabilities_req *req;
+
+	if (!params || params_len != sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	switch (req->cap) {
+	case CAP_COMPANY_ID:
+		req->params[0] = 1;
+		hton24(&req->params[1], IEEEID_BTSIG);
+		return 5;
+	case CAP_EVENTS_SUPPORTED:
+		if (!player->ind || !player->ind->get_capabilities)
+			return -ENOSYS;
+		return player->ind->get_capabilities(session, transaction,
+							player->user_data);
+	}
+
+	return -EINVAL;
+}
+
+static ssize_t list_attributes(struct avrcp *session, uint8_t transaction,
+				uint16_t params_len, uint8_t *params,
+				void *user_data)
+{
+	struct avrcp_player *player = user_data;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->list_attributes)
+		return -ENOSYS;
+
+	return player->ind->list_attributes(session, transaction,
+							player->user_data);
+}
+
+static bool check_attributes(uint8_t number, const uint8_t *attrs)
+{
+	int i;
+
+	for (i = 0; i < number; i++) {
+		if (attrs[i] > AVRCP_ATTRIBUTE_LAST ||
+					attrs[i] == AVRCP_ATTRIBUTE_ILEGAL)
+			return false;
+	}
+
+	return true;
+}
+
+static ssize_t get_attribute_text(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct get_attribute_text_req *req;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->get_attribute_text)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+	if (params_len != sizeof(*req) + req->number)
+		return -EINVAL;
+
+	if (!check_attributes(req->number, req->attrs))
+		return -EINVAL;
+
+	return player->ind->get_attribute_text(session, transaction,
+						req->number, req->attrs,
+						player->user_data);
+}
+
+static ssize_t list_values(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct list_values_req *req;
+
+	DBG("");
+
+	if (!params || params_len != sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+	if (req->attr > AVRCP_ATTRIBUTE_LAST ||
+					req->attr == AVRCP_ATTRIBUTE_ILEGAL)
+		return -EINVAL;
+
+	if (!player->ind || !player->ind->list_values)
+		return -ENOSYS;
+
+	return player->ind->list_values(session, transaction, req->attr,
+							player->user_data);
+}
+
+static bool check_value(uint8_t attr, uint8_t number, const uint8_t *values)
+{
+	int i;
+
+	for (i = 0; i < number; i++) {
+		/* Check for invalid value */
+		switch (attr) {
+		case AVRCP_ATTRIBUTE_EQUALIZER:
+			if (values[i] < AVRCP_EQUALIZER_OFF ||
+						values[i] > AVRCP_EQUALIZER_ON)
+				return false;
+			break;
+		case AVRCP_ATTRIBUTE_REPEAT_MODE:
+			if (values[i] < AVRCP_REPEAT_MODE_OFF ||
+					values[i] > AVRCP_REPEAT_MODE_GROUP)
+				return false;
+			break;
+		case AVRCP_ATTRIBUTE_SHUFFLE:
+			if (values[i] < AVRCP_SHUFFLE_OFF ||
+					values[i] > AVRCP_SHUFFLE_GROUP)
+				return false;
+			break;
+		case AVRCP_ATTRIBUTE_SCAN:
+			if (values[i] < AVRCP_SCAN_OFF ||
+					values[i] > AVRCP_SCAN_GROUP)
+				return false;
+			break;
+		}
+	}
+
+	return true;
+}
+
+static ssize_t get_value_text(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct get_value_text_req *req;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->get_value_text)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+	if (params_len != sizeof(*req) + req->number)
+		return -EINVAL;
+
+	if (req->number > AVRCP_ATTRIBUTE_LAST ||
+					req->number == AVRCP_ATTRIBUTE_ILEGAL)
+		return -EINVAL;
+
+	if (!check_value(req->attr, req->number, req->values))
+		return -EINVAL;
+
+	return player->ind->get_value_text(session, transaction, params[0],
+						params[1], &params[2],
+						player->user_data);
+}
+
+static ssize_t get_value(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct get_value_req *req;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->get_value)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+	if (params_len < sizeof(*req) + req->number)
+		return -EINVAL;
+
+	if (!check_attributes(req->number, req->attrs))
+		return -EINVAL;
+
+	return player->ind->get_value(session, transaction, params[0],
+					&params[1], player->user_data);
+}
+
+static ssize_t set_value(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct set_value_req *req;
+	uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
+	uint8_t values[AVRCP_ATTRIBUTE_LAST];
+	int i;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->set_value)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+	if (params_len < sizeof(*req) + req->number * sizeof(*req->values))
+		return -EINVAL;
+
+	for (i = 0; i < req->number; i++) {
+		attrs[i] = req->values[i].attr;
+		values[i] = req->values[i].value;
+
+		if (!check_value(attrs[i], 1, &values[i]))
+			return -EINVAL;
+	}
+
+	return player->ind->set_value(session, transaction, req->number,
+					attrs, values, player->user_data);
+}
+
+static ssize_t get_play_status(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->get_play_status)
+		return -ENOSYS;
+
+	return player->ind->get_play_status(session, transaction,
+							player->user_data);
+}
+
+static bool parse_attributes(uint32_t *params, uint16_t params_len,
+					uint8_t number, uint32_t *attrs)
+{
+	int i;
+
+	for (i = 0; i < number && params_len >= sizeof(*attrs); i++,
+					params_len -= sizeof(*attrs)) {
+		attrs[i] = be32_to_cpu(params[i]);
+
+		if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
+				attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST)
+			return false;
+	}
+
+	return true;
+}
+
+static ssize_t get_element_attributes(struct avrcp *session,
+						uint8_t transaction,
+						uint16_t params_len,
+						uint8_t *params,
+						void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct get_element_attributes_req *req;
+	uint64_t uid;
+	uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+
+	DBG("");
+
+	if (!player->ind || !player->ind->get_element_attributes)
+		return -ENOSYS;
+
+	req = (void *) params;
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	if (!parse_attributes(req->attrs, params_len - sizeof(*req),
+							req->number, attrs))
+		return -EINVAL;
+
+	uid = get_be64(params);
+
+	return player->ind->get_element_attributes(session, transaction, uid,
+							req->number, attrs,
+							player->user_data);
+}
+
+static ssize_t register_notification(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct register_notification_req *req;
+	uint32_t interval;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->register_notification)
+		return -ENOSYS;
+
+	if (!params || params_len != sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	interval = be32_to_cpu(req->interval);
+
+	return player->ind->register_notification(session, transaction,
+							req->event, interval,
+							player->user_data);
+}
+
+static ssize_t set_volume(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct set_volume_req *req;
+	uint8_t volume;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->set_volume)
+		return -ENOSYS;
+
+	if (!params || params_len != sizeof(volume))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	volume = req->value & 0x7f;
+
+	return player->ind->set_volume(session, transaction, volume,
+							player->user_data);
+}
+
+static ssize_t set_addressed(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct set_addressed_req *req;
+	uint16_t id;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->set_addressed)
+		return -ENOSYS;
+
+	if (!params || params_len != sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	id = be16_to_cpu(req->id);
+
+	return player->ind->set_addressed(session, transaction, id,
+							player->user_data);
+}
+
+static void continuing_new(struct avrcp *session, uint8_t pdu_id,
+					const struct iovec *iov, int iov_cnt,
+					size_t offset)
+{
+	struct avrcp_continuing *continuing;
+	int i;
+	size_t len = 0;
+
+	continuing = g_new0(struct avrcp_continuing, 1);
+	continuing->pdu_id = pdu_id;
+
+	for (i = 0; i < iov_cnt; i++) {
+		if (i == 0 && offset) {
+			len += iov[i].iov_len - offset;
+			continue;
+		}
+
+		len += iov[i].iov_len;
+	}
+
+	continuing->pdu.iov_base = g_malloc0(len);
+
+	DBG("len %zu", len);
+
+	for (i = 0; i < iov_cnt; i++) {
+		if (i == 0 && offset) {
+			memcpy(continuing->pdu.iov_base,
+						iov[i].iov_base + offset,
+						iov[i].iov_len - offset);
+			continuing->pdu.iov_len += iov[i].iov_len - offset;
+			continue;
+		}
+
+		memcpy(continuing->pdu.iov_base + continuing->pdu.iov_len,
+					iov[i].iov_base, iov[i].iov_len);
+		continuing->pdu.iov_len += iov[i].iov_len;
+	}
+
+	session->continuing = continuing;
+}
+
+static int avrcp_send_internal(struct avrcp *session, uint8_t transaction,
+					uint8_t code, uint8_t subunit,
+					uint8_t pdu_id, uint8_t type,
+					const struct iovec *iov, int iov_cnt)
+{
+	struct iovec pdu[iov_cnt + 1];
+	struct avrcp_header hdr;
+	int i;
+
+	/*
+	 * If a receiver receives a start fragment or non-fragmented AVRCP
+	 * Specific AV/C message when it already has an incomplete fragment
+	 * from that sender then the receiver shall consider the first PDU
+	 * aborted.
+	 */
+	if (session->continuing) {
+		continuing_free(session->continuing);
+		session->continuing = NULL;
+	}
+
+	memset(&hdr, 0, sizeof(hdr));
+
+	pdu[0].iov_base = &hdr;
+	pdu[0].iov_len = sizeof(hdr);
+
+	hdr.packet_type = type;
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i + 1].iov_base = iov[i].iov_base;
+
+		if (pdu[0].iov_len + hdr.params_len + iov[i].iov_len <=
+							session->control_mtu) {
+			pdu[i + 1].iov_len = iov[i].iov_len;
+			hdr.params_len += iov[i].iov_len;
+			if (hdr.packet_type != AVRCP_PACKET_TYPE_SINGLE)
+				hdr.packet_type = AVRCP_PACKET_TYPE_END;
+			continue;
+		}
+
+		/*
+		 * Only send what can fit and store the remaining in the
+		 * continuing iovec
+		 */
+		pdu[i + 1].iov_len = session->control_mtu -
+					(pdu[0].iov_len + hdr.params_len);
+		hdr.params_len += pdu[i + 1].iov_len;
+
+		continuing_new(session, pdu_id, &iov[i], iov_cnt - i,
+							pdu[i + 1].iov_len);
+
+		hdr.packet_type = hdr.packet_type != AVRCP_PACKET_TYPE_SINGLE ?
+						AVRCP_PACKET_TYPE_CONTINUING :
+						AVRCP_PACKET_TYPE_START;
+		break;
+	}
+
+	hton24(hdr.company_id, IEEEID_BTSIG);
+	hdr.pdu_id = pdu_id;
+	hdr.params_len = htons(hdr.params_len);
+
+	return avctp_send_vendor(session->conn, transaction, code, subunit,
+							pdu, iov_cnt + 1);
+}
+
+static ssize_t request_continuing(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct iovec iov;
+	int err;
+
+	DBG("");
+
+	if (!params || params_len != 1 || !session->continuing ||
+				session->continuing->pdu_id != params[0])
+		return -EINVAL;
+
+	iov.iov_base = session->continuing->pdu.iov_base;
+	iov.iov_len = session->continuing->pdu.iov_len;
+
+	DBG("len %zu", iov.iov_len);
+
+	session->continuing->pdu.iov_base = NULL;
+
+	err = avrcp_send_internal(session, transaction, AVC_CTYPE_STABLE,
+					AVC_SUBUNIT_PANEL, params[0],
+					AVRCP_PACKET_TYPE_CONTINUING, &iov, 1);
+
+	g_free(iov.iov_base);
+
+	if (err < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static ssize_t abort_continuing(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	DBG("");
+
+	if (!params || params_len != 1 || !session->continuing)
+		return -EINVAL;
+
+	continuing_free(session->continuing);
+	session->continuing = NULL;
+
+	avrcp_send_internal(session, transaction, AVC_CTYPE_ACCEPTED,
+				AVC_SUBUNIT_PANEL, AVRCP_ABORT_CONTINUING,
+				AVRCP_PACKET_TYPE_SINGLE, NULL, 0);
+
+	return 0;
+}
+
+static const struct avrcp_control_handler player_handlers[] = {
+		{ AVRCP_GET_CAPABILITIES,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					get_capabilities },
+		{ AVRCP_LIST_PLAYER_ATTRIBUTES,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					list_attributes },
+		{ AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					get_attribute_text },
+		{ AVRCP_LIST_PLAYER_VALUES,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					list_values },
+		{ AVRCP_GET_PLAYER_VALUE_TEXT,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					get_value_text },
+		{ AVRCP_GET_CURRENT_PLAYER_VALUE,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					get_value },
+		{ AVRCP_SET_PLAYER_VALUE,
+					AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+					set_value },
+		{ AVRCP_GET_PLAY_STATUS,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					get_play_status },
+		{ AVRCP_GET_ELEMENT_ATTRIBUTES,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					get_element_attributes },
+		{ AVRCP_REGISTER_NOTIFICATION,
+					AVC_CTYPE_NOTIFY, AVC_CTYPE_INTERIM,
+					register_notification },
+		{ AVRCP_SET_ABSOLUTE_VOLUME,
+					AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+					set_volume },
+		{ AVRCP_SET_ADDRESSED_PLAYER,
+					AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+					set_addressed },
+		{ AVRCP_REQUEST_CONTINUING,
+					AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+					request_continuing },
+		{ AVRCP_ABORT_CONTINUING,
+					AVC_CTYPE_CONTROL, AVC_CTYPE_ACCEPTED,
+					abort_continuing },
+		{ },
+};
+
+static void avrcp_set_control_handlers(struct avrcp *session,
+				const struct avrcp_control_handler *handlers,
+				void *user_data)
+{
+	session->control_handlers = handlers;
+	session->control_data = user_data;
+}
+
+static ssize_t set_browsed(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct set_browsed_req *req;
+	uint16_t id;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->set_browsed)
+		return -ENOSYS;
+
+	if (!params || params_len != sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	id = be16_to_cpu(req->id);
+
+	return player->ind->set_browsed(session, transaction, id,
+							player->user_data);
+}
+
+static ssize_t get_folder_items(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct get_folder_items_req *req;
+	uint32_t start, end;
+	uint16_t number;
+	uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+	int i;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->get_folder_items)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	if (req->scope > AVRCP_MEDIA_NOW_PLAYING)
+		return -EBADRQC;
+
+	start = be32_to_cpu(req->start);
+	end = be32_to_cpu(req->end);
+
+	if (start > end)
+		return -ERANGE;
+
+	number = be16_to_cpu(req->number);
+
+	for (i = 0; i < number; i++) {
+		attrs[i] = be32_to_cpu(req->attrs[i]);
+
+		if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
+				attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST)
+			return -EINVAL;
+	}
+
+	return player->ind->get_folder_items(session, transaction, req->scope,
+						start, end, number, attrs,
+						player->user_data);
+}
+
+static ssize_t change_path(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct change_path_req *req;
+	uint16_t counter;
+	uint64_t uid;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->change_path)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	counter = be16_to_cpu(req->counter);
+	uid = be64_to_cpu(req->uid);
+
+	return player->ind->change_path(session, transaction, counter,
+					req->direction, uid, player->user_data);
+}
+
+static ssize_t get_item_attributes(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct get_item_attributes_req *req;
+	uint64_t uid;
+	uint16_t counter;
+	uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+	int i;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->get_item_attributes)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	if (req->scope > AVRCP_MEDIA_NOW_PLAYING)
+		return -EBADRQC;
+
+	uid = be64_to_cpu(req->uid);
+	counter = be16_to_cpu(req->counter);
+
+	for (i = 0; i < req->number; i++) {
+		attrs[i] = be32_to_cpu(req->attrs[i]);
+
+		if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
+				attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST)
+			return -EINVAL;
+	}
+
+	return player->ind->get_item_attributes(session, transaction,
+						req->scope, uid, counter,
+						req->number, attrs,
+						player->user_data);
+}
+
+static ssize_t play_item(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct play_item_req *req;
+	uint64_t uid;
+	uint16_t counter;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->play_item)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	if (req->scope > AVRCP_MEDIA_NOW_PLAYING)
+		return -EBADRQC;
+
+	uid = be64_to_cpu(req->uid);
+	counter = be16_to_cpu(req->counter);
+
+	return player->ind->play_item(session, transaction, req->scope, uid,
+						counter, player->user_data);
+}
+
+static ssize_t search(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct search_req *req;
+	char *string;
+	uint16_t len;
+	int ret;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->search)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	len = be16_to_cpu(req->len);
+	if (!len)
+		return -EINVAL;
+
+	string = strndup(req->string, len);
+
+	ret = player->ind->search(session, transaction, string,
+							player->user_data);
+
+	free(string);
+
+	return ret;
+}
+
+static ssize_t add_to_now_playing(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct add_to_now_playing_req *req;
+	uint64_t uid;
+	uint16_t counter;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->add_to_now_playing)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	if (req->scope > AVRCP_MEDIA_NOW_PLAYING)
+		return -EBADRQC;
+
+	uid = be64_to_cpu(req->uid);
+	counter = be16_to_cpu(req->counter);
+
+	return player->ind->add_to_now_playing(session, transaction, req->scope,
+							uid, counter,
+							player->user_data);
+}
+
+static const struct avrcp_browsing_handler browsing_handlers[] = {
+		{ AVRCP_SET_BROWSED_PLAYER, set_browsed },
+		{ AVRCP_GET_FOLDER_ITEMS, get_folder_items },
+		{ AVRCP_CHANGE_PATH, change_path },
+		{ AVRCP_GET_ITEM_ATTRIBUTES, get_item_attributes },
+		{ AVRCP_PLAY_ITEM, play_item },
+		{ AVRCP_SEARCH, search },
+		{ AVRCP_ADD_TO_NOW_PLAYING, add_to_now_playing },
+		{ },
+};
+
+static void avrcp_set_browsing_handlers(struct avrcp *session,
+				const struct avrcp_browsing_handler *handlers,
+				void *user_data)
+{
+	session->browsing_handlers = handlers;
+	session->browsing_data = user_data;
+}
+
+void avrcp_register_player(struct avrcp *session,
+				const struct avrcp_control_ind *ind,
+				const struct avrcp_control_cfm *cfm,
+				void *user_data)
+{
+	struct avrcp_player *player;
+
+	player = g_new0(struct avrcp_player, 1);
+	player->ind = ind;
+	player->cfm = cfm;
+	player->user_data = user_data;
+
+	avrcp_set_control_handlers(session, player_handlers, player);
+	avrcp_set_browsing_handlers(session, browsing_handlers, player);
+	session->player = player;
+}
+
+void avrcp_set_passthrough_handlers(struct avrcp *session,
+			const struct avrcp_passthrough_handler *handlers,
+			void *user_data)
+{
+	session->passthrough_handlers = handlers;
+	session->passthrough_data = user_data;
+}
+
+int avrcp_init_uinput(struct avrcp *session, const char *name,
+							const char *address)
+{
+	return avctp_init_uinput(session->conn, name, address);
+}
+
+int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code,
+					uint8_t subunit, uint8_t pdu_id,
+					const struct iovec *iov, int iov_cnt)
+{
+	return avrcp_send_internal(session, transaction, code, subunit, pdu_id,
+					AVRCP_PACKET_TYPE_SINGLE, iov, iov_cnt);
+}
+
+static int status2errno(uint8_t status)
+{
+	switch (status) {
+	case AVRCP_STATUS_INVALID_COMMAND:
+		return -ENOSYS;
+	case AVRCP_STATUS_INVALID_PARAM:
+		return -EINVAL;
+	case AVRCP_STATUS_SUCCESS:
+		return 0;
+	case AVRCP_STATUS_NOT_DIRECTORY:
+		return -ENOTDIR;
+	case AVRCP_STATUS_INVALID_SCOPE:
+		return -EBADRQC;
+	case AVRCP_STATUS_OUT_OF_BOUNDS:
+		return -ERANGE;
+	case AVRCP_STATUS_INTERNAL_ERROR:
+	case AVRCP_STATUS_INVALID_PLAYER_ID:
+	case AVRCP_STATUS_PLAYER_NOT_BROWSABLE:
+	case AVRCP_STATUS_NO_AVAILABLE_PLAYERS:
+	case AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED:
+		return -EPERM;
+	default:
+		return -EPROTO;
+	}
+}
+
+static int parse_status(struct avrcp_header *pdu)
+{
+	if (pdu->params_len < 1)
+		return -EPROTO;
+
+	return status2errno(pdu->params[0]);
+}
+
+static int parse_browsing_status(struct avrcp_browsing_header *pdu)
+{
+	if (pdu->params_len < 1)
+		return -EPROTO;
+
+	return status2errno(pdu->params[0]);
+}
+
+static int avrcp_send_req(struct avrcp *session, uint8_t code, uint8_t subunit,
+					uint8_t pdu_id, const struct iovec *iov,
+					int iov_cnt, avctp_rsp_cb func,
+					void *user_data)
+{
+	struct iovec pdu[iov_cnt + 1];
+	struct avrcp_header hdr;
+	int i;
+
+	memset(&hdr, 0, sizeof(hdr));
+
+	pdu[0].iov_base = &hdr;
+	pdu[0].iov_len = sizeof(hdr);
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i + 1].iov_base = iov[i].iov_base;
+		pdu[i + 1].iov_len = iov[i].iov_len;
+		hdr.params_len += iov[i].iov_len;
+	}
+
+	hton24(hdr.company_id, IEEEID_BTSIG);
+	hdr.pdu_id = pdu_id;
+	hdr.packet_type = AVRCP_PACKET_TYPE_SINGLE;
+	hdr.params_len = htons(hdr.params_len);
+
+	return avctp_send_vendor_req(session->conn, code, subunit, pdu,
+						iov_cnt + 1, func, user_data);
+}
+
+static int avrcp_send_browsing_req(struct avrcp *session, uint8_t pdu_id,
+					const struct iovec *iov, int iov_cnt,
+					avctp_browsing_rsp_cb func,
+					void *user_data)
+{
+	struct iovec pdu[iov_cnt + 1];
+	struct avrcp_browsing_header hdr;
+	int i;
+
+	memset(&hdr, 0, sizeof(hdr));
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i + 1].iov_base = iov[i].iov_base;
+		pdu[i + 1].iov_len = iov[i].iov_len;
+		hdr.params_len += iov[i].iov_len;
+	}
+
+	hdr.pdu_id = pdu_id;
+	hdr.params_len = htons(hdr.params_len);
+
+	pdu[0].iov_base = &hdr;
+	pdu[0].iov_len = sizeof(hdr);
+
+	return avctp_send_browsing_req(session->conn, pdu, iov_cnt + 1,
+							func, user_data);
+}
+
+static int avrcp_send_browsing(struct avrcp *session, uint8_t transaction,
+				uint8_t pdu_id, const struct iovec *iov,
+				int iov_cnt)
+{
+	struct iovec pdu[iov_cnt + 1];
+	struct avrcp_browsing_header hdr;
+	int i;
+
+	memset(&hdr, 0, sizeof(hdr));
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i + 1].iov_base = iov[i].iov_base;
+		pdu[i + 1].iov_len = iov[i].iov_len;
+		hdr.params_len += iov[i].iov_len;
+	}
+
+	hdr.pdu_id = pdu_id;
+	hdr.params_len = htons(hdr.params_len);
+
+	pdu[0].iov_base = &hdr;
+	pdu[0].iov_len = sizeof(hdr);
+
+	return avctp_send_browsing(session->conn, transaction, pdu,
+								iov_cnt + 1);
+}
+
+static gboolean get_capabilities_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	struct get_capabilities_rsp *rsp;
+	uint8_t number = 0;
+	uint8_t *params = NULL;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_capabilities)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	switch (rsp->cap) {
+	case CAP_COMPANY_ID:
+	case CAP_EVENTS_SUPPORTED:
+		break;
+	default:
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (rsp->number > 0) {
+		number = rsp->number;
+		params = rsp->params;
+	}
+
+	err = 0;
+
+done:
+	player->cfm->get_capabilities(session, err, number, params,
+							player->user_data);
+
+	return FALSE;
+}
+
+
+int avrcp_get_capabilities(struct avrcp *session, uint8_t param)
+{
+	struct iovec iov;
+	struct get_capabilities_req req;
+
+	req.cap = param;
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+					AVRCP_GET_CAPABILITIES, &iov, 1,
+					get_capabilities_rsp, session);
+}
+
+static gboolean register_notification_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	struct register_notification_rsp *rsp;
+	uint8_t event = 0;
+	uint16_t value16, value16_2[2];
+	uint32_t value32;
+	uint64_t value64;
+	uint8_t *params = NULL;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->register_notification)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+	event = rsp->event;
+
+	if (event > AVRCP_EVENT_LAST) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	switch (event) {
+	case AVRCP_EVENT_STATUS_CHANGED:
+		if (pdu->params_len != sizeof(*rsp) + sizeof(uint8_t)) {
+			err = -EPROTO;
+			goto done;
+		}
+		params = rsp->data;
+		break;
+	case AVRCP_EVENT_VOLUME_CHANGED:
+		if (pdu->params_len != sizeof(*rsp) + sizeof(uint8_t)) {
+			err = -EPROTO;
+			goto done;
+		}
+		params = rsp->data;
+		params[0] &= 0x7f;
+		break;
+	case AVRCP_EVENT_TRACK_CHANGED:
+		if (pdu->params_len != sizeof(*rsp) + sizeof(value64)) {
+			err = -EPROTO;
+			goto done;
+		}
+		value64 = get_be64(rsp->data);
+		params = (uint8_t *) &value64;
+		break;
+	case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+		if (pdu->params_len != sizeof(*rsp) + sizeof(value32)) {
+			err = -EPROTO;
+			goto done;
+		}
+		value32 = get_be32(rsp->data);
+		params = (uint8_t *) &value32;
+		break;
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		if (pdu->params_len < sizeof(*rsp) + sizeof(value16_2)) {
+			err = -EPROTO;
+			goto done;
+		}
+		value16_2[0] = get_be16(rsp->data);
+		value16_2[1] = get_be16(rsp->data + 2);
+		params = (uint8_t *) value16_2;
+		break;
+	case AVRCP_EVENT_SETTINGS_CHANGED:
+		if (pdu->params_len < sizeof(*rsp) + sizeof(uint8_t)) {
+			err = -EPROTO;
+			goto done;
+		}
+		params = rsp->data;
+		break;
+	case AVRCP_EVENT_UIDS_CHANGED:
+		if (pdu->params_len < sizeof(*rsp) + sizeof(value16)) {
+			err = -EPROTO;
+			goto done;
+		}
+		value16 = get_be16(rsp->data);
+		params = (uint8_t *) &value16;
+		break;
+	}
+
+	err = 0;
+
+done:
+	return player->cfm->register_notification(session, err, code, event,
+						params, player->user_data);
+}
+
+int avrcp_register_notification(struct avrcp *session, uint8_t event,
+							uint32_t interval)
+{
+	struct iovec iov;
+	struct register_notification_req req;
+
+	if (event > AVRCP_EVENT_LAST)
+		return -EINVAL;
+
+	req.event = event;
+	req.interval = cpu_to_be32(interval);
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_req(session, AVC_CTYPE_NOTIFY, AVC_SUBUNIT_PANEL,
+				AVRCP_REGISTER_NOTIFICATION, &iov, 1,
+				register_notification_rsp, session);
+}
+
+static gboolean list_attributes_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu = (void *) operands;
+	struct list_attributes_rsp *rsp;
+	uint8_t number = 0;
+	uint8_t *attrs = NULL;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->list_attributes)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	number = rsp->number;
+	if (number > 0)
+		attrs = rsp->params;
+
+	err = 0;
+
+done:
+	player->cfm->list_attributes(session, err, number, attrs,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_list_player_attributes(struct avrcp *session)
+{
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_LIST_PLAYER_ATTRIBUTES, NULL, 0,
+				list_attributes_rsp, session);
+}
+
+static int parse_text_rsp(struct avrcp_header *pdu, uint8_t *number,
+					uint8_t *attrs, char **text)
+{
+	uint8_t *ptr;
+	uint16_t params_len;
+	int i;
+
+	if (pdu->params_len < 1)
+		return -EPROTO;
+
+	*number = pdu->params[0];
+	if (*number > AVRCP_ATTRIBUTE_LAST) {
+		*number = 0;
+		return -EPROTO;
+	}
+
+	params_len = pdu->params_len - 1;
+	for (i = 0, ptr = &pdu->params[1]; i < *number && params_len > 0; i++) {
+		struct text_value *val;
+
+		if (params_len < sizeof(*val))
+			goto fail;
+
+		val = (void *) ptr;
+
+		attrs[i] = val->attr;
+
+		params_len -= sizeof(*val);
+		ptr += sizeof(*val);
+
+		if (val->len > params_len)
+			goto fail;
+
+		if (val->len > 0) {
+			text[i] = g_strndup(val->data, val->len);
+			params_len -= val->len;
+			ptr += val->len;
+		}
+	}
+
+	if (i != *number)
+		goto fail;
+
+	return 0;
+
+fail:
+	for (i -= 1; i >= 0; i--)
+		g_free(text[i]);
+
+	*number = 0;
+
+	return -EPROTO;
+}
+
+static gboolean get_attribute_text_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	uint8_t number = 0;
+	uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
+	char *text[AVRCP_ATTRIBUTE_LAST];
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_attribute_text)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	err = parse_text_rsp(pdu, &number, attrs, text);
+
+done:
+	player->cfm->get_attribute_text(session, err, number, attrs, text,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t number,
+								uint8_t *attrs)
+{
+	struct iovec iov[2];
+
+	if (!number || number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &number;
+	iov[0].iov_len = sizeof(number);
+
+	iov[1].iov_base = attrs;
+	iov[1].iov_len = number;
+
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, iov, 2,
+				get_attribute_text_rsp, session);
+}
+
+static gboolean list_values_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	struct list_values_rsp *rsp;
+	uint8_t number = 0;
+	uint8_t *values = NULL;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->list_values)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	if (rsp->number > 0) {
+		number = rsp->number;
+		values = rsp->params;
+	}
+
+	err = 0;
+
+done:
+	player->cfm->list_values(session, err, number, values,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_list_player_values(struct avrcp *session, uint8_t attr)
+{
+	struct iovec iov;
+
+	iov.iov_base = &attr;
+	iov.iov_len = sizeof(attr);
+
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+					AVRCP_LIST_PLAYER_VALUES, &iov, 1,
+					list_values_rsp, session);
+}
+
+static gboolean get_value_text_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	uint8_t number = 0;
+	uint8_t values[AVRCP_ATTRIBUTE_LAST];
+	char *text[AVRCP_ATTRIBUTE_LAST];
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_value_text)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	err = parse_text_rsp(pdu, &number, values, text);
+
+done:
+	player->cfm->get_value_text(session, err, number, values, text,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_get_player_value_text(struct avrcp *session, uint8_t attr,
+					uint8_t number, uint8_t *values)
+{
+	struct iovec iov[2];
+	struct get_value_text_req req;
+
+	if (!number)
+		return -EINVAL;
+
+	req.attr = attr;
+	req.number = number;
+
+	iov[0].iov_base = &req;
+	iov[0].iov_len = sizeof(req);
+
+	iov[1].iov_base = values;
+	iov[1].iov_len = number;
+
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+					AVRCP_GET_PLAYER_VALUE_TEXT, iov, 2,
+					get_value_text_rsp, session);
+}
+
+static int parse_value(struct avrcp_header *pdu, uint8_t *number,
+					uint8_t *attrs, uint8_t *values)
+{
+	int i;
+	struct value_rsp *rsp;
+
+	if (pdu->params_len < sizeof(*rsp))
+		return -EPROTO;
+
+	rsp = (void *) pdu->params;
+
+	/*
+	 * Check if PDU is big enough to hold the number of (attribute, value)
+	 * tuples.
+	 */
+	if (rsp->number > AVRCP_ATTRIBUTE_LAST ||
+			sizeof(*rsp) + rsp->number * 2 != pdu->params_len) {
+		*number = 0;
+		return -EPROTO;
+	}
+
+	for (i = 0; i < rsp->number; i++) {
+		attrs[i] = rsp->values[i].attr;
+		values[i] = rsp->values[i].value;
+	}
+
+	*number = rsp->number;
+
+	return 0;
+}
+
+static gboolean get_value_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	uint8_t number = 0;
+	uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
+	uint8_t values[AVRCP_ATTRIBUTE_LAST];
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_value)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	err = parse_value(pdu, &number, attrs, values);
+
+done:
+	player->cfm->get_value(session, err, number, attrs, values,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_get_current_player_value(struct avrcp *session, uint8_t number,
+							uint8_t *attrs)
+
+{
+	struct iovec iov[2];
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &number;
+	iov[0].iov_len = sizeof(number);
+
+	iov[1].iov_base = attrs;
+	iov[1].iov_len = number;
+
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_GET_CURRENT_PLAYER_VALUE, iov, 2,
+				get_value_rsp, session);
+}
+
+static gboolean set_value_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->set_value)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	err = 0;
+
+done:
+	player->cfm->set_value(session, err, player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_set_player_value(struct avrcp *session, uint8_t number,
+					uint8_t *attrs, uint8_t *values)
+{
+	struct iovec iov[2];
+	struct attr_value val[AVRCP_ATTRIBUTE_LAST];
+	int i;
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &number;
+	iov[0].iov_len = sizeof(number);
+
+	for (i = 0; i < number; i++) {
+		val[i].attr = attrs[i];
+		val[i].value = values[i];
+	}
+
+	iov[1].iov_base = val;
+	iov[1].iov_len = sizeof(*val) * number;
+
+	return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+					AVRCP_SET_PLAYER_VALUE, iov, 2,
+					set_value_rsp, session);
+}
+
+static gboolean get_play_status_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	struct get_play_status_rsp *rsp;
+	uint8_t status = 0;
+	uint32_t position = 0;
+	uint32_t duration = 0;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_play_status)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	duration = be32_to_cpu(rsp->duration);
+	position = be32_to_cpu(rsp->position);
+	status = rsp->status;
+	err = 0;
+
+done:
+	player->cfm->get_play_status(session, err, status, position, duration,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_get_play_status(struct avrcp *session)
+{
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_GET_PLAY_STATUS, NULL, 0,
+				get_play_status_rsp, session);
+}
+
+static gboolean set_volume_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	struct set_volume_rsp *rsp;
+	uint8_t value = 0;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->set_volume)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	value = rsp->value & 0x7f;
+	err = 0;
+
+done:
+	player->cfm->set_volume(session, err, value, player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_set_volume(struct avrcp *session, uint8_t volume)
+{
+	struct iovec iov;
+
+	iov.iov_base = &volume;
+	iov.iov_len = sizeof(volume);
+
+	return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+				AVRCP_SET_ABSOLUTE_VOLUME, &iov, 1,
+				set_volume_rsp, session);
+}
+
+static int parse_attribute_list(uint8_t *params, uint16_t params_len,
+				uint8_t number, uint32_t *attrs, char **text)
+{
+	struct media_item *item;
+	int i;
+
+	if (number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+		return -EPROTO;
+
+	for (i = 0; i < number && params_len >= sizeof(*item); i++) {
+		item = (void *) params;
+
+		item->attr = be32_to_cpu(item->attr);
+		item->charset = be16_to_cpu(item->charset);
+		item->len = be16_to_cpu(item->len);
+
+		params_len -= sizeof(*item);
+		params += sizeof(*item);
+		if (item->len > params_len)
+			goto fail;
+
+		if (item->len > 0) {
+			text[i] = g_strndup(item->data, item->len);
+			attrs[i] = item->attr;
+			params_len -= item->len;
+			params += item->len;
+		} else {
+			text[i] = NULL;
+			attrs[i] = 0;
+		}
+	}
+
+	return 0;
+
+fail:
+	for (i -= 1; i >= 0; i--)
+		g_free(text[i]);
+
+	return -EPROTO;
+}
+
+static void free_attribute_list(uint8_t number, char **text)
+{
+	while(number--)
+		g_free(text[number]);
+}
+
+static int parse_elements(struct avrcp_header *pdu, uint8_t *number,
+						uint32_t *attrs, char **text)
+{
+	struct get_element_attributes_rsp *rsp;
+
+	if (pdu->params_len < sizeof(*rsp))
+		return -EPROTO;
+
+	rsp = (void *) pdu->params;
+	if (rsp->number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+		return -EPROTO;
+
+	*number = rsp->number;
+
+	return parse_attribute_list(pdu->params + sizeof(*rsp),
+						pdu->params_len - sizeof(*rsp),
+						*number, attrs, text);
+}
+
+static int parse_items(struct avrcp_browsing_header *pdu, uint8_t *number,
+						uint32_t *attrs, char **text)
+{
+	struct get_item_attributes_rsp *rsp;
+
+	if (pdu->params_len < sizeof(*rsp))
+		return -EPROTO;
+
+	rsp = (void *) pdu->params;
+
+	if (rsp->number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+		return -EPROTO;
+
+	*number = rsp->number;
+
+	return parse_attribute_list(pdu->params + sizeof(*rsp),
+						pdu->params_len - sizeof(*rsp),
+						*number, attrs, text);
+}
+
+static gboolean get_element_attributes_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	uint8_t number = 0;
+	uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+	char *text[AVRCP_MEDIA_ATTRIBUTE_LAST];
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_element_attributes)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	err = parse_elements(pdu, &number, attrs, text);
+
+done:
+	player->cfm->get_element_attributes(session, err, number, attrs, text,
+							player->user_data);
+
+	if (err == 0)
+		free_attribute_list(number, text);
+
+	return FALSE;
+}
+
+int avrcp_get_element_attributes(struct avrcp *session)
+{
+	struct iovec iov;
+	struct get_element_attributes_req req;
+
+	/* This returns all attributes */
+	memset(&req, 0, sizeof(req));
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_GET_ELEMENT_ATTRIBUTES, &iov, 1,
+				get_element_attributes_rsp, session);
+}
+
+static gboolean set_addressed_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->set_addressed)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_status(pdu);
+
+done:
+	player->cfm->set_addressed(session, err, player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id)
+{
+	struct iovec iov;
+	struct set_addressed_req req;
+
+	req.id = cpu_to_be16(player_id);
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+					AVRCP_SET_ADDRESSED_PLAYER, &iov, 1,
+					set_addressed_rsp, session);
+}
+
+static char *parse_folder_list(uint8_t *params, uint16_t params_len,
+								uint8_t depth)
+{
+	char **folders, *path;
+	uint8_t count;
+	size_t i;
+
+	folders = g_new0(char *, depth + 2);
+	folders[0] = g_strdup("/Filesystem");
+
+	for (i = 0, count = 1; count <= depth && i < params_len; count++) {
+		uint8_t len;
+
+		len = params[i++];
+
+		if (i + len > params_len || len == 0) {
+			g_strfreev(folders);
+			return NULL;
+		}
+
+		folders[count] = g_memdup(&params[i], len);
+		i += len;
+	}
+
+	path = g_build_pathv("/", folders);
+	g_strfreev(folders);
+
+	return path;
+}
+
+static gboolean set_browsed_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu;
+	struct set_browsed_rsp *rsp;
+	uint16_t counter = 0;
+	uint32_t items = 0;
+	char *path = NULL;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->set_browsed)
+		return FALSE;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_browsing_status(pdu);
+	if (err < 0)
+		goto done;
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	counter = be16_to_cpu(rsp->counter);
+	items = be32_to_cpu(rsp->items);
+
+	path = parse_folder_list(rsp->data, pdu->params_len - sizeof(*rsp),
+								rsp->depth);
+	if (!path)
+		err = -EPROTO;
+
+done:
+	player->cfm->set_browsed(session, err, counter, items, path,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id)
+{
+	struct iovec iov;
+	struct set_browsed_req req;
+
+	req.id = cpu_to_be16(player_id);
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_browsing_req(session, AVRCP_SET_BROWSED_PLAYER,
+					&iov, 1, set_browsed_rsp, session);
+}
+
+static gboolean get_folder_items_rsp(struct avctp *conn,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu;
+	struct get_folder_items_rsp *rsp;
+	uint16_t counter = 0, number = 0;
+	uint8_t *params = NULL;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_folder_items)
+		return FALSE;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_browsing_status(pdu);
+	if (err < 0)
+		goto done;
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	counter = be16_to_cpu(rsp->counter);
+	number = be16_to_cpu(rsp->number);
+	params = rsp->data;
+
+	/* FIXME: Add proper parsing for each item type */
+
+done:
+	player->cfm->get_folder_items(session, err, counter, number, params,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_get_folder_items(struct avrcp *session, uint8_t scope,
+				uint32_t start, uint32_t end, uint8_t number,
+				uint32_t *attrs)
+{
+
+	struct iovec iov[2];
+	struct get_folder_items_req req;
+	int i;
+
+	req.scope = scope;
+	req.start = cpu_to_be32(start);
+	req.end = cpu_to_be32(end);
+	req.number = number;
+
+	iov[0].iov_base = &req;
+	iov[0].iov_len = sizeof(req);
+
+	if (!number)
+		return avrcp_send_browsing_req(session, AVRCP_GET_FOLDER_ITEMS,
+						iov, 1, get_folder_items_rsp,
+						session);
+
+	for (i = 0; i < number; i++)
+		attrs[i] = cpu_to_be32(attrs[i]);
+
+	iov[1].iov_base = attrs;
+	iov[1].iov_len = number * sizeof(*attrs);
+
+	return avrcp_send_browsing_req(session, AVRCP_GET_FOLDER_ITEMS,
+					iov, 2, get_folder_items_rsp, session);
+}
+
+static gboolean change_path_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu;
+	struct change_path_rsp *rsp;
+	uint32_t items = 0;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->change_path)
+		return FALSE;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_browsing_status(pdu);
+	if (err < 0)
+		goto done;
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	items = be32_to_cpu(rsp->items);
+
+done:
+	player->cfm->change_path(session, err, items, player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_change_path(struct avrcp *session, uint8_t direction, uint64_t uid,
+							uint16_t counter)
+{
+	struct iovec iov;
+	struct change_path_req req;
+
+	req.counter = cpu_to_be16(counter);
+	req.direction = direction;
+	req.uid = cpu_to_be64(uid);
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_browsing_req(session, AVRCP_CHANGE_PATH,
+					&iov, 1, change_path_rsp, session);
+}
+
+static gboolean get_item_attributes_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu;
+	uint8_t number = 0;
+	uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+	char *text[AVRCP_MEDIA_ATTRIBUTE_LAST];
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_item_attributes)
+		return FALSE;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_browsing_status(pdu);
+	if (err < 0)
+		goto done;
+
+	err = parse_items(pdu, &number, attrs, text);
+
+done:
+	player->cfm->get_item_attributes(session, err, number, attrs, text,
+							player->user_data);
+
+	if (err == 0)
+		free_attribute_list(number, text);
+
+	return FALSE;
+}
+
+int avrcp_get_item_attributes(struct avrcp *session, uint8_t scope,
+				uint64_t uid, uint16_t counter, uint8_t number,
+				uint32_t *attrs)
+{
+	struct iovec iov[2];
+	struct get_item_attributes_req req;
+	int i;
+
+	req.scope = scope;
+	req.uid = cpu_to_be64(uid);
+	req.counter = cpu_to_be16(counter);
+	req.number = number;
+
+	iov[0].iov_base = &req;
+	iov[0].iov_len = sizeof(req);
+
+	if (!number)
+		return avrcp_send_browsing_req(session,
+						AVRCP_GET_ITEM_ATTRIBUTES,
+						iov, 1, get_item_attributes_rsp,
+						session);
+
+	if (number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	for (i = 0; i < number; i++) {
+		if (attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST ||
+				attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL)
+			return -EINVAL;
+		attrs[i] = cpu_to_be32(attrs[i]);
+	}
+
+	iov[1].iov_base = attrs;
+	iov[1].iov_len = number * sizeof(*attrs);
+
+	return avrcp_send_browsing_req(session, AVRCP_GET_ITEM_ATTRIBUTES,
+					iov, 2, get_item_attributes_rsp,
+					session);
+}
+
+static gboolean play_item_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->play_item)
+		return FALSE;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_browsing_status(pdu);
+
+done:
+	player->cfm->play_item(session, err, player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_play_item(struct avrcp *session, uint8_t scope, uint64_t uid,
+							uint16_t counter)
+{
+	struct iovec iov;
+	struct play_item_req req;
+
+	if (scope > AVRCP_MEDIA_NOW_PLAYING)
+		return -EINVAL;
+
+	req.scope = scope;
+	req.uid = cpu_to_be64(uid);
+	req.counter = cpu_to_be16(counter);
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_browsing_req(session, AVRCP_PLAY_ITEM, &iov, 1,
+						play_item_rsp, session);
+}
+
+static gboolean search_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu;
+	struct search_rsp *rsp;
+	uint16_t counter = 0;
+	uint32_t items = 0;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->search)
+		return FALSE;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_browsing_status(pdu);
+	if (err < 0)
+		goto done;
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	counter = be16_to_cpu(rsp->counter);
+	items = be32_to_cpu(rsp->items);
+
+	err = 0;
+
+done:
+	player->cfm->search(session, err, counter, items, player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_search(struct avrcp *session, const char *string)
+{
+	struct iovec iov[2];
+	struct search_req req;
+	size_t len;
+
+	if (!string)
+		return -EINVAL;
+
+	len = strnlen(string, UINT8_MAX);
+
+	req.charset = cpu_to_be16(AVRCP_CHARSET_UTF8);
+	req.len = cpu_to_be16(len);
+
+	iov[0].iov_base = &req;
+	iov[0].iov_len = sizeof(req);
+
+	iov[1].iov_base = (void *) string;
+	iov[1].iov_len = len;
+
+	return avrcp_send_browsing_req(session, AVRCP_SEARCH, iov, 2,
+							search_rsp, session);
+}
+
+static gboolean add_to_now_playing_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->add_to_now_playing)
+		return FALSE;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_browsing_status(pdu);
+
+done:
+	player->cfm->add_to_now_playing(session, err, player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_add_to_now_playing(struct avrcp *session, uint8_t scope, uint64_t uid,
+							uint16_t counter)
+{
+	struct iovec iov;
+	struct add_to_now_playing_req req;
+
+	if (scope > AVRCP_MEDIA_NOW_PLAYING)
+		return -EINVAL;
+
+	req.scope = scope;
+	req.uid = cpu_to_be64(uid);
+	req.counter = cpu_to_be16(counter);
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_browsing_req(session, AVRCP_ADD_TO_NOW_PLAYING,
+					&iov, 1, add_to_now_playing_rsp,
+					session);
+}
+
+int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction,
+						uint8_t number, uint8_t *events)
+{
+	struct iovec iov[2];
+	struct get_capabilities_rsp rsp;
+
+	if (number > AVRCP_EVENT_LAST)
+		return -EINVAL;
+
+	rsp.cap = CAP_EVENTS_SUPPORTED;
+	rsp.number = number;
+
+	iov[0].iov_base = &rsp;
+	iov[0].iov_len = sizeof(rsp);
+
+	iov[1].iov_base = events;
+	iov[1].iov_len = number;
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_GET_CAPABILITIES,
+				iov, 2);
+}
+
+int avrcp_list_player_attributes_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *attrs)
+{
+	struct iovec iov[2];
+	struct list_attributes_rsp rsp;
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	rsp.number = number;
+
+	iov[0].iov_base = &rsp;
+	iov[0].iov_len = sizeof(rsp);
+
+	if (!number)
+		return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES,
+				iov, 1);
+
+	iov[1].iov_base = attrs;
+	iov[1].iov_len = number;
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES,
+				iov, 2);
+}
+
+int avrcp_get_player_attribute_text_rsp(struct avrcp *session,
+					uint8_t transaction, uint8_t number,
+					uint8_t *attrs, const char **text)
+{
+	struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST * 2];
+	struct text_value val[AVRCP_ATTRIBUTE_LAST];
+	int i;
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &number;
+	iov[0].iov_len = sizeof(number);
+
+	for (i = 0; i < number; i++) {
+		uint8_t len = 0;
+
+		if (attrs[i] > AVRCP_ATTRIBUTE_LAST ||
+					attrs[i] == AVRCP_ATTRIBUTE_ILEGAL)
+			return -EINVAL;
+
+		if (text[i])
+			len = strlen(text[i]);
+
+		val[i].attr = attrs[i];
+		val[i].charset = cpu_to_be16(AVRCP_CHARSET_UTF8);
+		val[i].len = len;
+
+		iov[i + 1].iov_base = &val[i];
+		iov[i + 1].iov_len = sizeof(val[i]);
+
+		iov[i + 2].iov_base = (void *) text[i];
+		iov[i + 2].iov_len = len;
+	}
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+			AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+			iov, 1 + i * 2);
+}
+
+int avrcp_list_player_values_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *values)
+{
+	struct iovec iov[2];
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &number;
+	iov[0].iov_len = sizeof(number);
+
+	iov[1].iov_base = values;
+	iov[1].iov_len = number;
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_VALUES,
+				iov, 2);
+}
+
+int avrcp_get_play_status_rsp(struct avrcp *session, uint8_t transaction,
+				uint32_t position, uint32_t duration,
+				uint8_t status)
+{
+	struct iovec iov;
+	struct get_play_status_rsp rsp;
+
+	rsp.duration = cpu_to_be32(duration);
+	rsp.position = cpu_to_be32(position);
+	rsp.status = status;
+
+	iov.iov_base = &rsp;
+	iov.iov_len = sizeof(rsp);
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_GET_PLAY_STATUS,
+				&iov, 1);
+}
+
+int avrcp_get_player_values_text_rsp(struct avrcp *session,
+					uint8_t transaction, uint8_t number,
+					uint8_t *values, const char **text)
+{
+	struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST * 2];
+	struct text_value val[AVRCP_ATTRIBUTE_LAST];
+	int i;
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &number;
+	iov[0].iov_len = sizeof(number);
+
+	for (i = 0; i < number; i++) {
+		uint8_t len = 0;
+
+		if (text[i])
+			len = strlen(text[i]);
+
+		val[i].attr = values[i];
+		val[i].charset = cpu_to_be16(AVRCP_CHARSET_UTF8);
+		val[i].len = len;
+
+		iov[i + 1].iov_base = &val[i];
+		iov[i + 1].iov_len = sizeof(val[i]);
+
+		iov[i + 2].iov_base = (void *) text[i];
+		iov[i + 2].iov_len = len;
+	}
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_VALUE_TEXT,
+				iov, 1 + i * 2);
+}
+
+int avrcp_get_current_player_value_rsp(struct avrcp *session,
+					uint8_t transaction, uint8_t number,
+					uint8_t *attrs, uint8_t *values)
+{
+	struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST];
+	struct attr_value val[AVRCP_ATTRIBUTE_LAST];
+	int i;
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &number;
+	iov[0].iov_len = sizeof(number);
+
+	for (i = 0; i < number; i++) {
+		val[i].attr = attrs[i];
+		val[i].value = values[i];
+
+		iov[i + 1].iov_base = &val[i];
+		iov[i + 1].iov_len = sizeof(val[i]);
+	}
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+			AVC_SUBUNIT_PANEL, AVRCP_GET_CURRENT_PLAYER_VALUE,
+			iov, 1 + i);
+}
+
+int avrcp_set_player_value_rsp(struct avrcp *session, uint8_t transaction)
+{
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+			AVC_SUBUNIT_PANEL, AVRCP_SET_PLAYER_VALUE, NULL, 0);
+}
+
+int avrcp_get_element_attrs_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t *params, size_t params_len)
+{
+	struct iovec iov;
+
+	iov.iov_base = params;
+	iov.iov_len = params_len;
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				&iov, 1);
+}
+
+int avrcp_register_notification_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t code, uint8_t event,
+					void *data, size_t len)
+{
+	struct iovec iov[2];
+	uint16_t *player;
+	uint8_t *volume;
+
+	if (event > AVRCP_EVENT_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &event;
+	iov[0].iov_len = sizeof(event);
+
+	switch (event) {
+	case AVRCP_EVENT_STATUS_CHANGED:
+		if (len != sizeof(uint8_t))
+			return -EINVAL;
+		break;
+	case AVRCP_EVENT_VOLUME_CHANGED:
+		if (len != sizeof(uint8_t))
+			return -EINVAL;
+		volume = data;
+		if (volume[0] > 127)
+			return -EINVAL;
+		break;
+	case AVRCP_EVENT_TRACK_CHANGED:
+		if (len != sizeof(uint64_t))
+			return -EINVAL;
+
+		put_be64(*(uint64_t *) data, data);
+		break;
+	case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+		if (len != sizeof(uint32_t))
+			return -EINVAL;
+
+		put_be32(*(uint32_t *) data, data);
+		break;
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		if (len != 4)
+			return -EINVAL;
+
+		player = data;
+		player[0] = cpu_to_be16(player[0]);
+		player[1] = cpu_to_be16(player[1]);
+
+		break;
+	case AVRCP_EVENT_SETTINGS_CHANGED:
+		if (len < sizeof(uint8_t))
+			return -EINVAL;
+		break;
+	case AVRCP_EVENT_UIDS_CHANGED:
+		if (len != sizeof(uint16_t))
+			return -EINVAL;
+
+		put_be16(*(uint16_t *) data, data);
+		break;
+	default:
+		return avrcp_send(session, transaction, code, AVC_SUBUNIT_PANEL,
+					AVRCP_REGISTER_NOTIFICATION, iov, 1);
+	}
+
+	iov[1].iov_base = data;
+	iov[1].iov_len = len;
+
+	return avrcp_send(session, transaction, code, AVC_SUBUNIT_PANEL,
+					AVRCP_REGISTER_NOTIFICATION, iov, 2);
+}
+
+int avrcp_set_volume_rsp(struct avrcp *session, uint8_t transaction,
+							uint8_t volume)
+{
+	struct iovec iov;
+
+	if (volume > 127)
+		return -EINVAL;
+
+	iov.iov_base = &volume;
+	iov.iov_len = sizeof(volume);
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_SET_ABSOLUTE_VOLUME,
+				&iov, 1);
+}
+
+int avrcp_set_addressed_player_rsp(struct avrcp *session, uint8_t transaction,
+							uint8_t status)
+{
+	struct iovec iov;
+
+	iov.iov_base = &status;
+	iov.iov_len = sizeof(status);
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_SET_ADDRESSED_PLAYER,
+				&iov, 1);
+}
+
+static int avrcp_status_rsp(struct avrcp *session, uint8_t transaction,
+						uint8_t pdu_id, uint8_t status)
+{
+	struct iovec iov;
+
+	if (status > AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED)
+		return -EINVAL;
+
+	iov.iov_base = &status;
+	iov.iov_len = sizeof(status);
+
+	return avrcp_send_browsing(session, transaction, pdu_id, &iov, 1);
+}
+
+int avrcp_set_browsed_player_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t status, uint16_t counter,
+					uint32_t items, uint8_t depth,
+					const char **folders)
+{
+	struct iovec iov[UINT8_MAX * 2 + 1];
+	struct set_browsed_rsp rsp;
+	uint16_t len[UINT8_MAX];
+	int i;
+
+	if (status != AVRCP_STATUS_SUCCESS)
+		return avrcp_status_rsp(session, transaction,
+					AVRCP_SET_BROWSED_PLAYER, status);
+
+	rsp.status = status;
+	rsp.counter = cpu_to_be16(counter);
+	rsp.items = cpu_to_be32(items);
+	rsp.charset = cpu_to_be16(AVRCP_CHARSET_UTF8);
+	rsp.depth = depth;
+
+	iov[0].iov_base = &rsp;
+	iov[0].iov_len = sizeof(rsp);
+
+	if (!depth)
+		return avrcp_send_browsing(session, transaction,
+						AVRCP_SET_BROWSED_PLAYER,
+						iov, 1);
+
+	for (i = 0; i < depth; i++) {
+		if (!folders[i])
+			return -EINVAL;
+
+		len[i] = strlen(folders[i]);
+
+		iov[i * 2 + 2].iov_base = (void *) folders[i];
+		iov[i * 2 + 2].iov_len = len[i];
+
+		len[i] = cpu_to_be16(len[i]);
+
+		iov[i * 2 + 1].iov_base = &len[i];
+		iov[i * 2 + 1].iov_len = sizeof(len[i]);
+	}
+
+	return avrcp_send_browsing(session, transaction,
+					AVRCP_SET_BROWSED_PLAYER, iov,
+					depth * 2 + 1);
+}
+
+int avrcp_get_folder_items_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t status, uint16_t counter,
+					uint8_t number, uint8_t *type,
+					uint16_t *len, uint8_t **params)
+{
+	struct iovec iov[UINT8_MAX * 2 + 1];
+	struct get_folder_items_rsp rsp;
+	uint8_t item[UINT8_MAX][3];
+	int i;
+
+	if (status != AVRCP_STATUS_SUCCESS)
+		return avrcp_status_rsp(session, transaction,
+					AVRCP_GET_FOLDER_ITEMS, status);
+
+	rsp.status = status;
+	rsp.counter = cpu_to_be16(counter);
+	rsp.number = cpu_to_be16(number);
+
+	iov[0].iov_base = &rsp;
+	iov[0].iov_len = sizeof(rsp);
+
+	for (i = 0; i < number; i++) {
+		item[i][0] = type[i];
+		put_be16(len[i], &item[i][1]);
+
+		iov[i * 2 + 1].iov_base = item[i];
+		iov[i * 2 + 1].iov_len = sizeof(item[i]);
+
+		iov[i * 2 + 2].iov_base = params[i];
+		iov[i * 2 + 2].iov_len = len[i];
+	}
+
+	return avrcp_send_browsing(session, transaction, AVRCP_GET_FOLDER_ITEMS,
+							iov, number * 2 + 1);
+}
+
+int avrcp_change_path_rsp(struct avrcp *session, uint8_t transaction,
+						uint8_t status, uint32_t items)
+{
+	struct iovec iov;
+	struct change_path_rsp rsp;
+
+	if (status != AVRCP_STATUS_SUCCESS)
+		return avrcp_status_rsp(session, transaction, AVRCP_CHANGE_PATH,
+									status);
+
+	rsp.status = status;
+	rsp.items = cpu_to_be32(items);
+
+	iov.iov_base = &rsp;
+	iov.iov_len = sizeof(rsp);
+
+	return avrcp_send_browsing(session, transaction, AVRCP_CHANGE_PATH,
+								&iov, 1);
+}
+
+static bool pack_attribute_list(struct iovec *iov, uint8_t number,
+					uint32_t *attrs, const char **text)
+{
+	int i;
+	struct media_item val[AVRCP_MEDIA_ATTRIBUTE_LAST];
+
+	for (i = 0; i < number; i++) {
+		uint16_t len = 0;
+
+		if (attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST ||
+				attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL)
+			return false;
+
+		if (text[i])
+			len = strlen(text[i]);
+
+		val[i].attr = cpu_to_be32(attrs[i]);
+		val[i].charset = cpu_to_be16(AVRCP_CHARSET_UTF8);
+		val[i].len = cpu_to_be16(len);
+
+		iov[i].iov_base = &val[i];
+		iov[i].iov_len = sizeof(val[i]);
+
+		iov[i + 1].iov_base = (void *) text[i];
+		iov[i + 1].iov_len = len;
+	}
+
+	return true;
+}
+
+int avrcp_get_item_attributes_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t status, uint8_t number,
+					uint32_t *attrs, const char **text)
+{
+	struct iovec iov[AVRCP_MEDIA_ATTRIBUTE_LAST * 2 + 1];
+	struct get_item_attributes_rsp rsp;
+
+	if (number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	if (status != AVRCP_STATUS_SUCCESS)
+		return avrcp_status_rsp(session, transaction,
+					AVRCP_GET_ITEM_ATTRIBUTES, status);
+
+	rsp.status = status;
+	rsp.number = number;
+
+	iov[0].iov_base = &rsp;
+	iov[0].iov_len = sizeof(rsp);
+
+	if (!pack_attribute_list(&iov[1], number, attrs, text))
+		return -EINVAL;
+
+	return avrcp_send_browsing(session, transaction,
+					AVRCP_GET_ITEM_ATTRIBUTES, iov,
+					number * 2 + 1);
+}
+
+int avrcp_play_item_rsp(struct avrcp *session, uint8_t transaction,
+								uint8_t status)
+{
+	return avrcp_status_rsp(session, transaction, AVRCP_PLAY_ITEM,
+								status);
+}
+
+int avrcp_search_rsp(struct avrcp *session, uint8_t transaction, uint8_t status,
+					uint16_t counter, uint32_t items)
+{
+	struct iovec iov;
+	struct search_rsp rsp;
+
+	if (status != AVRCP_STATUS_SUCCESS)
+		return avrcp_status_rsp(session, transaction, AVRCP_SEARCH,
+								status);
+
+	rsp.status = status;
+	rsp.counter = cpu_to_be16(counter);
+	rsp.items = cpu_to_be32(items);
+
+	iov.iov_base = &rsp;
+	iov.iov_len = sizeof(rsp);
+
+	return avrcp_send_browsing(session, transaction, AVRCP_SEARCH,
+								&iov, 1);
+}
+
+int avrcp_add_to_now_playing_rsp(struct avrcp *session, uint8_t transaction,
+								uint8_t status)
+{
+	return avrcp_status_rsp(session, transaction, AVRCP_ADD_TO_NOW_PLAYING,
+								status);
+}
+
+int avrcp_send_passthrough(struct avrcp *session, uint32_t vendor, uint8_t op)
+{
+	uint8_t params[5];
+
+	if (!vendor)
+		return avctp_send_passthrough(session->conn, op, NULL, 0);
+
+	hton24(params, vendor);
+	put_be16(op, &params[3]);
+
+	return avctp_send_passthrough(session->conn, AVC_VENDOR_UNIQUE, params,
+								sizeof(params));
+}
diff --git a/repo/android/avrcp-lib.h b/repo/android/avrcp-lib.h
new file mode 100644
index 0000000..6554b12
--- /dev/null
+++ b/repo/android/avrcp-lib.h
@@ -0,0 +1,356 @@
+/*
+ *
+ *  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
+ *
+ */
+
+/* Control PDU ids */
+#define AVRCP_GET_CAPABILITIES		0x10
+#define AVRCP_LIST_PLAYER_ATTRIBUTES	0X11
+#define AVRCP_LIST_PLAYER_VALUES	0x12
+#define AVRCP_GET_CURRENT_PLAYER_VALUE	0x13
+#define AVRCP_SET_PLAYER_VALUE		0x14
+#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT	0x15
+#define AVRCP_GET_PLAYER_VALUE_TEXT	0x16
+#define AVRCP_DISPLAYABLE_CHARSET	0x17
+#define AVRCP_CT_BATTERY_STATUS		0x18
+#define AVRCP_GET_ELEMENT_ATTRIBUTES	0x20
+#define AVRCP_GET_PLAY_STATUS		0x30
+#define AVRCP_REGISTER_NOTIFICATION	0x31
+#define AVRCP_REQUEST_CONTINUING	0x40
+#define AVRCP_ABORT_CONTINUING		0x41
+#define AVRCP_SET_ABSOLUTE_VOLUME	0x50
+#define AVRCP_SET_ADDRESSED_PLAYER	0x60
+#define AVRCP_SET_BROWSED_PLAYER	0x70
+#define AVRCP_GET_FOLDER_ITEMS		0x71
+#define AVRCP_CHANGE_PATH		0x72
+#define AVRCP_GET_ITEM_ATTRIBUTES	0x73
+#define AVRCP_PLAY_ITEM			0x74
+#define AVRCP_SEARCH			0x80
+#define AVRCP_ADD_TO_NOW_PLAYING	0x90
+#define AVRCP_GENERAL_REJECT		0xA0
+
+/* Notification events */
+#define AVRCP_EVENT_STATUS_CHANGED		0x01
+#define AVRCP_EVENT_TRACK_CHANGED		0x02
+#define AVRCP_EVENT_TRACK_REACHED_END		0x03
+#define AVRCP_EVENT_TRACK_REACHED_START		0x04
+#define AVRCP_EVENT_PLAYBACK_POS_CHANGED	0x05
+#define AVRCP_EVENT_SETTINGS_CHANGED		0x08
+#define AVRCP_EVENT_NOW_PLAYING_CONTENT_CHANGED	0x09
+#define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED	0x0a
+#define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED	0x0b
+#define AVRCP_EVENT_UIDS_CHANGED		0x0c
+#define AVRCP_EVENT_VOLUME_CHANGED		0x0d
+#define AVRCP_EVENT_LAST			AVRCP_EVENT_VOLUME_CHANGED
+
+/* Status codes */
+#define AVRCP_STATUS_INVALID_COMMAND		0x00
+#define AVRCP_STATUS_INVALID_PARAM		0x01
+#define AVRCP_STATUS_PARAM_NOT_FOUND		0x02
+#define AVRCP_STATUS_INTERNAL_ERROR		0x03
+#define AVRCP_STATUS_SUCCESS			0x04
+#define AVRCP_STATUS_UID_CHANGED		0x05
+#define AVRCP_STATUS_NOT_DIRECTORY		0x08
+#define AVRCP_STATUS_DOES_NOT_EXIST		0x09
+#define AVRCP_STATUS_INVALID_SCOPE		0x0a
+#define AVRCP_STATUS_OUT_OF_BOUNDS		0x0b
+#define AVRCP_STATUS_INVALID_PLAYER_ID		0x11
+#define AVRCP_STATUS_PLAYER_NOT_BROWSABLE	0x12
+#define AVRCP_STATUS_NO_AVAILABLE_PLAYERS	0x15
+#define AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED	0x16
+
+/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
+#define CAP_COMPANY_ID				0x02
+#define CAP_EVENTS_SUPPORTED			0x03
+
+/* Player Attributes */
+#define AVRCP_ATTRIBUTE_ILEGAL			0x00
+#define AVRCP_ATTRIBUTE_EQUALIZER		0x01
+#define AVRCP_ATTRIBUTE_REPEAT_MODE		0x02
+#define AVRCP_ATTRIBUTE_SHUFFLE			0x03
+#define AVRCP_ATTRIBUTE_SCAN			0x04
+#define AVRCP_ATTRIBUTE_LAST			AVRCP_ATTRIBUTE_SCAN
+
+/* equalizer values */
+#define AVRCP_EQUALIZER_OFF		0x01
+#define AVRCP_EQUALIZER_ON		0x02
+
+/* repeat mode values */
+#define AVRCP_REPEAT_MODE_OFF		0x01
+#define AVRCP_REPEAT_MODE_SINGLE	0x02
+#define AVRCP_REPEAT_MODE_ALL		0x03
+#define AVRCP_REPEAT_MODE_GROUP		0x04
+
+/* shuffle values */
+#define AVRCP_SHUFFLE_OFF		0x01
+#define AVRCP_SHUFFLE_ALL		0x02
+#define AVRCP_SHUFFLE_GROUP		0x03
+
+/* scan values */
+#define AVRCP_SCAN_OFF			0x01
+#define AVRCP_SCAN_ALL			0x02
+#define AVRCP_SCAN_GROUP		0x03
+
+/* media attributes */
+#define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL	0x00
+#define AVRCP_MEDIA_ATTRIBUTE_TITLE	0x01
+#define AVRCP_MEDIA_ATTRIBUTE_ARTIST	0x02
+#define AVRCP_MEDIA_ATTRIBUTE_ALBUM	0x03
+#define AVRCP_MEDIA_ATTRIBUTE_TRACK	0x04
+#define AVRCP_MEDIA_ATTRIBUTE_N_TRACKS	0x05
+#define AVRCP_MEDIA_ATTRIBUTE_GENRE	0x06
+#define AVRCP_MEDIA_ATTRIBUTE_DURATION	0x07
+#define AVRCP_MEDIA_ATTRIBUTE_LAST	AVRCP_MEDIA_ATTRIBUTE_DURATION
+
+/* Media Scope */
+#define AVRCP_MEDIA_PLAYER_LIST			0x00
+#define AVRCP_MEDIA_PLAYER_VFS			0x01
+#define AVRCP_MEDIA_SEARCH			0x02
+#define AVRCP_MEDIA_NOW_PLAYING			0x03
+
+/* SDP features */
+#define AVRCP_FEATURE_CATEGORY_1	0x0001
+#define AVRCP_FEATURE_CATEGORY_2	0x0002
+#define AVRCP_FEATURE_CATEGORY_3	0x0004
+#define AVRCP_FEATURE_CATEGORY_4	0x0008
+#define AVRCP_FEATURE_PLAYER_SETTINGS	0x0010
+#define AVRCP_FEATURE_GROUP_NAVIGATION	0x0020
+#define AVRCP_FEATURE_BROWSING		0x0040
+#define AVRCP_FEATURE_MULTIPLE_PLAYERS	0x0080
+
+/* Company IDs for vendor dependent commands */
+#define IEEEID_BTSIG		0x001958
+
+struct avrcp;
+
+struct avrcp_control_ind {
+	int (*get_capabilities) (struct avrcp *session, uint8_t transaction,
+							void *user_data);
+	int (*list_attributes) (struct avrcp *session, uint8_t transaction,
+							void *user_data);
+	int (*get_attribute_text) (struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *attrs,
+					void *user_data);
+	int (*list_values) (struct avrcp *session, uint8_t transaction,
+					uint8_t attr, void *user_data);
+	int (*get_value_text) (struct avrcp *session, uint8_t transaction,
+					uint8_t attr, uint8_t number,
+					uint8_t *values, void *user_data);
+	int (*get_value) (struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *attrs,
+					void *user_data);
+	int (*set_value) (struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *attrs,
+					uint8_t *values, void *user_data);
+	int (*get_play_status) (struct avrcp *session, uint8_t transaction,
+					void *user_data);
+	int (*get_element_attributes) (struct avrcp *session,
+					uint8_t transaction, uint64_t uid,
+					uint8_t number, uint32_t *attrs,
+					void *user_data);
+	int (*register_notification) (struct avrcp *session,
+					uint8_t transaction, uint8_t event,
+					uint32_t interval, void *user_data);
+	int (*set_volume) (struct avrcp *session, uint8_t transaction,
+					uint8_t volume, void *user_data);
+	int (*set_addressed) (struct avrcp *session, uint8_t transaction,
+					uint16_t id, void *user_data);
+	int (*set_browsed) (struct avrcp *session, uint8_t transaction,
+					uint16_t id, void *user_data);
+	int (*get_folder_items) (struct avrcp *session, uint8_t transaction,
+					uint8_t scope, uint32_t start,
+					uint32_t end, uint16_t number,
+					uint32_t *attrs, void *user_data);
+	int (*change_path) (struct avrcp *session, uint8_t transaction,
+					uint16_t counter, uint8_t direction,
+					uint64_t uid, void *user_data);
+	int (*get_item_attributes) (struct avrcp *session, uint8_t transaction,
+					uint8_t scope, uint64_t uid,
+					uint16_t counter, uint8_t number,
+					uint32_t *attrs, void *user_data);
+	int (*play_item) (struct avrcp *session, uint8_t transaction,
+					uint8_t scope, uint64_t uid,
+					uint16_t counter, void *user_data);
+	int (*search) (struct avrcp *session, uint8_t transaction,
+					const char *string, void *user_data);
+	int (*add_to_now_playing) (struct avrcp *session, uint8_t transaction,
+					uint8_t scope, uint64_t uid,
+					uint16_t counter, void *user_data);
+};
+
+struct avrcp_control_cfm {
+	void (*get_capabilities) (struct avrcp *session, int err,
+					uint8_t number, uint8_t *params,
+					void *user_data);
+	void (*list_attributes) (struct avrcp *session, int err,
+					uint8_t number, uint8_t *attrs,
+					void *user_data);
+	void (*get_attribute_text) (struct avrcp *session, int err,
+					uint8_t number, uint8_t *attrs,
+					char **text, void *user_data);
+	void (*list_values) (struct avrcp *session, int err,
+					uint8_t number, uint8_t *values,
+					void *user_data);
+	void (*get_value_text) (struct avrcp *session, int err,
+					uint8_t number, uint8_t *values,
+					char **text, void *user_data);
+	void (*get_value) (struct avrcp *session, int err,
+					uint8_t number, uint8_t *attrs,
+					uint8_t *values, void *user_data);
+	void (*set_value) (struct avrcp *session, int err, void *user_data);
+	void (*get_play_status) (struct avrcp *session, int err,
+					uint8_t status, uint32_t position,
+					uint32_t duration, void *user_data);
+	void (*get_element_attributes) (struct avrcp *session, int err,
+					uint8_t number, uint32_t *attrs,
+					char **text, void *user_data);
+	bool (*register_notification) (struct avrcp *session, int err,
+					uint8_t code, uint8_t event,
+					void *params, void *user_data);
+	void (*set_volume) (struct avrcp *session, int err, uint8_t volume,
+					void *user_data);
+	void (*set_addressed) (struct avrcp *session, int err,
+					void *user_data);
+	void (*set_browsed) (struct avrcp *session, int err,
+					uint16_t counter, uint32_t items,
+					char *path, void *user_data);
+	void (*get_folder_items) (struct avrcp *session, int err,
+					uint16_t counter, uint16_t number,
+					uint8_t *params, void *user_data);
+	void (*change_path) (struct avrcp *session, int err,
+					uint32_t items, void *user_data);
+	void (*get_item_attributes) (struct avrcp *session, int err,
+					uint8_t number, uint32_t *attrs,
+					char **text, void *user_data);
+	void (*play_item) (struct avrcp *session, int err, void *user_data);
+	void (*search) (struct avrcp *session, int err, uint16_t counter,
+					uint32_t items, void *user_data);
+	void (*add_to_now_playing) (struct avrcp *session, int err,
+					void *user_data);
+};
+
+struct avrcp_passthrough_handler {
+	uint8_t op;
+	bool (*func) (struct avrcp *session, bool pressed, void *user_data);
+};
+
+typedef void (*avrcp_destroy_cb_t) (void *user_data);
+
+struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version);
+void avrcp_shutdown(struct avrcp *session);
+void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb,
+							void *user_data);
+int avrcp_connect_browsing(struct avrcp *session, int fd, size_t imtu,
+								size_t omtu);
+
+void avrcp_register_player(struct avrcp *session,
+				const struct avrcp_control_ind *ind,
+				const struct avrcp_control_cfm *cfm,
+				void *user_data);
+void avrcp_set_passthrough_handlers(struct avrcp *session,
+			const struct avrcp_passthrough_handler *handlers,
+			void *user_data);
+int avrcp_init_uinput(struct avrcp *session, const char *name,
+							const char *address);
+int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code,
+					uint8_t subunit, uint8_t pdu_id,
+					const struct iovec *iov, int iov_cnt);
+int avrcp_get_capabilities(struct avrcp *session, uint8_t param);
+int avrcp_register_notification(struct avrcp *session, uint8_t event,
+							uint32_t interval);
+int avrcp_list_player_attributes(struct avrcp *session);
+int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t number,
+							uint8_t *attrs);
+int avrcp_list_player_values(struct avrcp *session, uint8_t attr);
+int avrcp_get_player_value_text(struct avrcp *session, uint8_t attr,
+					uint8_t number, uint8_t *values);
+int avrcp_set_player_value(struct avrcp *session, uint8_t number,
+					uint8_t *attrs, uint8_t *values);
+int avrcp_get_current_player_value(struct avrcp *session, uint8_t number,
+							uint8_t *attrs);
+int avrcp_get_play_status(struct avrcp *session);
+int avrcp_set_volume(struct avrcp *session, uint8_t volume);
+int avrcp_get_element_attributes(struct avrcp *session);
+int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id);
+int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id);
+int avrcp_get_folder_items(struct avrcp *session, uint8_t scope,
+				uint32_t start, uint32_t end, uint8_t number,
+				uint32_t *attrs);
+int avrcp_change_path(struct avrcp *session, uint8_t direction, uint64_t uid,
+							uint16_t counter);
+int avrcp_get_item_attributes(struct avrcp *session, uint8_t scope,
+				uint64_t uid, uint16_t counter, uint8_t number,
+				uint32_t *attrs);
+int avrcp_play_item(struct avrcp *session, uint8_t scope, uint64_t uid,
+							uint16_t counter);
+int avrcp_search(struct avrcp *session, const char *string);
+int avrcp_add_to_now_playing(struct avrcp *session, uint8_t scope, uint64_t uid,
+							uint16_t counter);
+
+int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *events);
+int avrcp_list_player_attributes_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *attrs);
+int avrcp_get_player_attribute_text_rsp(struct avrcp *session,
+					uint8_t transaction, uint8_t number,
+					uint8_t *attrs, const char **text);
+int avrcp_list_player_values_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *values);
+int avrcp_get_play_status_rsp(struct avrcp *session, uint8_t transaction,
+				uint32_t position, uint32_t duration,
+				uint8_t status);
+int avrcp_get_player_values_text_rsp(struct avrcp *session,
+					uint8_t transaction, uint8_t number,
+					uint8_t *values, const char **text);
+int avrcp_get_current_player_value_rsp(struct avrcp *session,
+					uint8_t transaction, uint8_t number,
+					uint8_t *attrs, uint8_t *values);
+int avrcp_set_player_value_rsp(struct avrcp *session, uint8_t transaction);
+int avrcp_get_element_attrs_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t *params, size_t params_len);
+int avrcp_register_notification_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t code, uint8_t event,
+					void *data, size_t len);
+int avrcp_set_volume_rsp(struct avrcp *session, uint8_t transaction,
+							uint8_t volume);
+int avrcp_set_addressed_player_rsp(struct avrcp *session, uint8_t transaction,
+							uint8_t status);
+int avrcp_set_browsed_player_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t status, uint16_t counter,
+					uint32_t items, uint8_t depth,
+					const char **folders);
+int avrcp_get_folder_items_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t status, uint16_t counter,
+					uint8_t number, uint8_t *type,
+					uint16_t *len, uint8_t **params);
+int avrcp_change_path_rsp(struct avrcp *session, uint8_t transaction,
+						uint8_t status, uint32_t items);
+int avrcp_get_item_attributes_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t status, uint8_t number,
+					uint32_t *attrs, const char **text);
+int avrcp_play_item_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t status);
+int avrcp_search_rsp(struct avrcp *session, uint8_t transaction, uint8_t status,
+					uint16_t counter, uint32_t items);
+int avrcp_add_to_now_playing_rsp(struct avrcp *session, uint8_t transaction,
+								uint8_t status);
+
+int avrcp_send_passthrough(struct avrcp *session, uint32_t vendor, uint8_t op);
diff --git a/repo/android/avrcp.c b/repo/android/avrcp.c
new file mode 100644
index 0000000..8c3e25a
--- /dev/null
+++ b/repo/android/avrcp.c
@@ -0,0 +1,1169 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/shared/util.h"
+#include "src/log.h"
+
+#include "avctp.h"
+#include "avrcp-lib.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "bluetooth.h"
+#include "avrcp.h"
+#include "utils.h"
+
+#define L2CAP_PSM_AVCTP 0x17
+
+static bdaddr_t adapter_addr;
+static uint32_t record_tg_id = 0;
+static uint32_t record_ct_id = 0;
+static GSList *devices = NULL;
+static GIOChannel *server = NULL;
+static struct ipc *hal_ipc = NULL;
+
+struct avrcp_request {
+	struct avrcp_device *dev;
+	uint8_t pdu_id;
+	uint8_t event_id;
+	uint8_t transaction;
+};
+
+struct avrcp_device {
+	bdaddr_t	dst;
+	uint16_t	version;
+	uint16_t	features;
+	struct avrcp	*session;
+	GIOChannel	*io;
+	GQueue		*queue;
+};
+
+static struct avrcp_request *pop_request(uint8_t pdu_id, uint8_t event_id,
+								bool peek)
+{
+	GSList *l;
+
+	for (l = devices; l; l = g_slist_next(l)) {
+		struct avrcp_device *dev = l->data;
+		GList *reqs = g_queue_peek_head_link(dev->queue);
+		int i;
+
+		for (i = 0; reqs; reqs = g_list_next(reqs), i++) {
+			struct avrcp_request *req = reqs->data;
+
+			if (req->pdu_id != pdu_id || req->event_id != event_id)
+				continue;
+
+			if (!peek)
+				g_queue_pop_nth(dev->queue, i);
+
+			return req;
+		}
+	}
+
+	return NULL;
+}
+
+static void handle_get_play_status(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_avrcp_get_play_status *cmd = buf;
+	uint8_t status;
+	struct avrcp_request *req;
+	int ret;
+
+	DBG("");
+
+	req = pop_request(AVRCP_GET_PLAY_STATUS, 0, false);
+	if (!req) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	ret = avrcp_get_play_status_rsp(req->dev->session, req->transaction,
+					cmd->position, cmd->duration,
+					cmd->status);
+	if (ret < 0) {
+		status = HAL_STATUS_FAILED;
+		g_free(req);
+		goto done;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+	g_free(req);
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+				HAL_OP_AVRCP_GET_PLAY_STATUS, status);
+}
+
+static void handle_list_player_attrs(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_LIST_PLAYER_ATTRS, HAL_STATUS_FAILED);
+}
+
+static void handle_list_player_values(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_LIST_PLAYER_VALUES, HAL_STATUS_FAILED);
+}
+
+static void handle_get_player_attrs(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_GET_PLAYER_ATTRS, HAL_STATUS_FAILED);
+}
+
+static void handle_get_player_attrs_text(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT, HAL_STATUS_FAILED);
+}
+
+static void handle_get_player_values_text(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT, HAL_STATUS_FAILED);
+}
+
+static size_t write_element_text(uint8_t id, uint8_t text_len, uint8_t *text,
+						uint8_t *pdu)
+{
+	uint16_t charset = 106;
+	size_t len = 0;
+
+	put_be32(id, pdu);
+	pdu += 4;
+	len += 4;
+
+	put_be16(charset, pdu);
+	pdu += 2;
+	len += 2;
+
+	put_be16(text_len, pdu);
+	pdu += 2;
+	len += 2;
+
+	memcpy(pdu, text, text_len);
+	len += text_len;
+
+	return len;
+}
+
+static void write_element_attrs(uint8_t *ptr, uint8_t number, uint8_t *pdu,
+								size_t *len)
+{
+	int i;
+
+	*pdu = number;
+	pdu++;
+	*len += 1;
+
+	for (i = 0; i < number; i++) {
+		struct hal_avrcp_player_setting_text *text = (void *) ptr;
+		size_t ret;
+
+		ret = write_element_text(text->id, text->len, text->text, pdu);
+
+		ptr += sizeof(*text) + text->len;
+		pdu += ret;
+		*len += ret;
+	}
+}
+
+static void handle_get_element_attrs_text(const void *buf, uint16_t len)
+{
+	struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf;
+	uint8_t status;
+	struct avrcp_request *req;
+	uint8_t pdu[IPC_MTU];
+	uint8_t *ptr;
+	size_t pdu_len;
+	int ret;
+
+	DBG("");
+
+	req = pop_request(AVRCP_GET_ELEMENT_ATTRIBUTES, 0, false);
+	if (!req) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	ptr = (uint8_t *) &cmd->values[0];
+	pdu_len = 0;
+	write_element_attrs(ptr, cmd->number, pdu, &pdu_len);
+
+	ret = avrcp_get_element_attrs_rsp(req->dev->session, req->transaction,
+								pdu, pdu_len);
+	if (ret < 0) {
+		status = HAL_STATUS_FAILED;
+		g_free(req);
+		goto done;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+	g_free(req);
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT, status);
+}
+
+static void handle_set_player_attrs_value(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE, HAL_STATUS_FAILED);
+}
+
+static void handle_register_notification(const void *buf, uint16_t len)
+{
+	struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+	uint8_t status;
+	struct avrcp_request *req;
+	uint8_t code;
+	bool peek = false;
+	int ret;
+
+	DBG("");
+
+	switch (cmd->type) {
+	case HAL_AVRCP_EVENT_TYPE_INTERIM:
+		code = AVC_CTYPE_INTERIM;
+		peek = true;
+		break;
+	case HAL_AVRCP_EVENT_TYPE_CHANGED:
+		code = AVC_CTYPE_CHANGED;
+		break;
+	default:
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	req = pop_request(AVRCP_REGISTER_NOTIFICATION, cmd->event, peek);
+	if (!req) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	ret = avrcp_register_notification_rsp(req->dev->session,
+						req->transaction, code,
+						cmd->event, cmd->data,
+						cmd->len);
+	if (ret < 0) {
+		status = HAL_STATUS_FAILED;
+		if (!peek)
+			g_free(req);
+		goto done;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+	if (!peek)
+		g_free(req);
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_REGISTER_NOTIFICATION, status);
+}
+
+static void handle_set_volume(const void *buf, uint16_t len)
+{
+	struct hal_cmd_avrcp_set_volume *cmd = (void *) buf;
+	struct avrcp_device *dev;
+	uint8_t status;
+	int ret;
+
+	DBG("");
+
+	if (!devices) {
+		error("AVRCP: No device found to set volume");
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	/*
+	 * Peek the first device since the HAL cannot really address a specific
+	 * device it might mean there could only be one connected.
+	 */
+	dev = devices->data;
+
+	ret = avrcp_set_volume(dev->session, cmd->value & 0x7f);
+	if (ret < 0) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_VOLUME,
+								status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_AVRCP_GET_PLAY_STATUS */
+	{ handle_get_play_status, false,
+			sizeof(struct hal_cmd_avrcp_get_play_status) },
+	/* HAL_OP_AVRCP_LIST_PLAYER_ATTRS */
+	{ handle_list_player_attrs, true,
+			sizeof(struct hal_cmd_avrcp_list_player_attrs) },
+	/* HAL_OP_AVRCP_LIST_PLAYER_VALUES */
+	{ handle_list_player_values, true,
+			sizeof(struct hal_cmd_avrcp_list_player_values) },
+	/* HAL_OP_AVRCP_GET_PLAYER_ATTRS */
+	{ handle_get_player_attrs, true,
+			sizeof(struct hal_cmd_avrcp_get_player_attrs) },
+	/* HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT */
+	{ handle_get_player_attrs_text, true,
+			sizeof(struct hal_cmd_avrcp_get_player_attrs_text) },
+	/* HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT */
+	{ handle_get_player_values_text, true,
+			sizeof(struct hal_cmd_avrcp_get_player_values_text) },
+	/* HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT */
+	{ handle_get_element_attrs_text, true,
+			sizeof(struct hal_cmd_avrcp_get_element_attrs_text) },
+	/* HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE */
+	{ handle_set_player_attrs_value, true,
+			sizeof(struct hal_cmd_avrcp_set_player_attrs_value) },
+	/* HAL_OP_AVRCP_REGISTER_NOTIFICATION */
+	{ handle_register_notification, true,
+			sizeof(struct hal_cmd_avrcp_register_notification) },
+	/* HAL_OP_AVRCP_SET_VOLUME */
+	{ handle_set_volume, false, sizeof(struct hal_cmd_avrcp_set_volume) },
+};
+
+static sdp_record_t *avrcp_tg_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, l2cap, avctp, avrtg;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto_control, *proto_control[2];
+	sdp_record_t *record;
+	sdp_data_t *psm, *version, *features;
+	uint16_t lp = L2CAP_PSM_AVCTP;
+	uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0104;
+	uint16_t feat = (AVRCP_FEATURE_CATEGORY_1 |
+					AVRCP_FEATURE_CATEGORY_2 |
+					AVRCP_FEATURE_CATEGORY_3 |
+					AVRCP_FEATURE_CATEGORY_4);
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	/* Service Class ID List */
+	sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &avrtg);
+	sdp_set_service_classes(record, svclass_id);
+
+	/* Protocol Descriptor List */
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto_control[0] = sdp_list_append(NULL, &l2cap);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto_control[0] = sdp_list_append(proto_control[0], psm);
+	apseq = sdp_list_append(NULL, proto_control[0]);
+
+	sdp_uuid16_create(&avctp, AVCTP_UUID);
+	proto_control[1] = sdp_list_append(NULL, &avctp);
+	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
+	proto_control[1] = sdp_list_append(proto_control[1], version);
+	apseq = sdp_list_append(apseq, proto_control[1]);
+
+	aproto_control = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto_control);
+
+	/* Bluetooth Profile Descriptor List */
+	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+	profile[0].version = avrcp_ver;
+	pfseq = sdp_list_append(NULL, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+
+	features = sdp_data_alloc(SDP_UINT16, &feat);
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	sdp_set_info_attr(record, "AVRCP TG", NULL, NULL);
+
+	sdp_data_free(psm);
+	sdp_data_free(version);
+	sdp_list_free(proto_control[0], NULL);
+	sdp_list_free(proto_control[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(aproto_control, NULL);
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static sdp_record_t *avrcp_ct_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, l2cap, avctp, avrct, avrctr;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t *record;
+	sdp_data_t *psm, *version, *features;
+	uint16_t lp = AVCTP_CONTROL_PSM;
+	uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0104;
+	uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
+						AVRCP_FEATURE_CATEGORY_2 |
+						AVRCP_FEATURE_CATEGORY_3 |
+						AVRCP_FEATURE_CATEGORY_4);
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	/* Service Class ID List */
+	sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &avrct);
+	sdp_uuid16_create(&avrctr, AV_REMOTE_CONTROLLER_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &avrctr);
+	sdp_set_service_classes(record, svclass_id);
+
+	/* Protocol Descriptor List */
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&avctp, AVCTP_UUID);
+	proto[1] = sdp_list_append(NULL, &avctp);
+	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
+	proto[1] = sdp_list_append(proto[1], version);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	/* Bluetooth Profile Descriptor List */
+	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+	profile[0].version = avrcp_ver;
+	pfseq = sdp_list_append(NULL, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+
+	features = sdp_data_alloc(SDP_UINT16, &feat);
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	sdp_set_info_attr(record, "AVRCP CT", NULL, NULL);
+
+	free(psm);
+	free(version);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static void avrcp_device_free(void *data)
+{
+	struct avrcp_device *dev = data;
+
+	if (dev->queue) {
+		g_queue_foreach(dev->queue, (GFunc) g_free, NULL);
+		g_queue_free(dev->queue);
+	}
+
+	if (dev->session)
+		avrcp_shutdown(dev->session);
+
+	if (dev->io) {
+		g_io_channel_shutdown(dev->io, FALSE, NULL);
+		g_io_channel_unref(dev->io);
+	}
+
+	g_free(dev);
+}
+
+static void avrcp_device_remove(struct avrcp_device *dev)
+{
+	devices = g_slist_remove(devices, dev);
+	avrcp_device_free(dev);
+}
+
+static struct avrcp_device *avrcp_device_new(const bdaddr_t *dst)
+{
+	struct avrcp_device *dev;
+
+	dev = g_new0(struct avrcp_device, 1);
+	bacpy(&dev->dst, dst);
+	devices = g_slist_prepend(devices, dev);
+
+	return dev;
+}
+
+static int device_cmp(gconstpointer s, gconstpointer user_data)
+{
+	const struct avrcp_device *dev = s;
+	const bdaddr_t *dst = user_data;
+
+	return bacmp(&dev->dst, dst);
+}
+
+static struct avrcp_device *avrcp_device_find(const bdaddr_t *dst)
+{
+	GSList *l;
+
+	l = g_slist_find_custom(devices, dst, device_cmp);
+	if (!l)
+		return NULL;
+
+	return l->data;
+}
+
+static void disconnect_cb(void *data)
+{
+	struct avrcp_device *dev = data;
+
+	DBG("");
+
+	dev->session = NULL;
+
+	avrcp_device_remove(dev);
+}
+
+static bool handle_fast_forward(struct avrcp *session, bool pressed,
+							void *user_data)
+{
+	struct hal_ev_avrcp_passthrough_cmd ev;
+
+	DBG("pressed %s", pressed ? "true" : "false");
+
+	ev.id = AVC_FAST_FORWARD;
+	ev.state = pressed;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev);
+
+	return true;
+}
+
+static bool handle_rewind(struct avrcp *session, bool pressed,
+							void *user_data)
+{
+	struct hal_ev_avrcp_passthrough_cmd ev;
+
+	DBG("pressed %s", pressed ? "true" : "false");
+
+	ev.id = AVC_REWIND;
+	ev.state = pressed;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev);
+
+	return true;
+}
+
+static const struct avrcp_passthrough_handler passthrough_handlers[] = {
+		{ AVC_FAST_FORWARD, handle_fast_forward },
+		{ AVC_REWIND, handle_rewind },
+		{ },
+};
+
+static int handle_get_capabilities_cmd(struct avrcp *session,
+					uint8_t transaction, void *user_data)
+{
+	uint8_t events[] = { AVRCP_EVENT_STATUS_CHANGED,
+					AVRCP_EVENT_TRACK_CHANGED,
+					AVRCP_EVENT_PLAYBACK_POS_CHANGED  };
+
+	DBG("");
+
+	/*
+	 * Android do not provide this info via HAL so the list most
+	 * be hardcoded according to what RegisterNotification can
+	 * actually handle
+	 */
+	avrcp_get_capabilities_rsp(session, transaction, sizeof(events),
+								events);
+
+	return 0;
+}
+
+static void push_request(struct avrcp_device *dev, uint8_t pdu_id,
+					uint8_t event_id, uint8_t transaction)
+{
+	struct avrcp_request *req;
+
+	req = g_new0(struct avrcp_request, 1);
+	req->dev = dev;
+	req->pdu_id = pdu_id;
+	req->event_id = event_id;
+	req->transaction = transaction;
+
+	g_queue_push_tail(dev->queue, req);
+}
+
+static int handle_get_play_status_cmd(struct avrcp *session,
+					uint8_t transaction, void *user_data)
+{
+	struct avrcp_device *dev = user_data;
+
+	DBG("");
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+					HAL_EV_AVRCP_GET_PLAY_STATUS, 0, NULL);
+
+	push_request(dev, AVRCP_GET_PLAY_STATUS, 0, transaction);
+
+	return 0;
+}
+
+static int handle_get_element_attrs_cmd(struct avrcp *session,
+					uint8_t transaction, uint64_t uid,
+					uint8_t number, uint32_t *attrs,
+					void *user_data)
+{
+	struct avrcp_device *dev = user_data;
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_avrcp_get_element_attrs *ev = (void *) buf;
+	int i;
+
+	DBG("");
+
+	ev->number = number;
+	/* Set everything in case of empty list */
+	if (ev->number == 0) {
+		for (i = 0; i < HAL_AVRCP_MEDIA_ATTR_DURATION; i++) {
+			/* Skip 0x00 as the attributes start with 0x01 */
+			ev->attrs[i] = i + 1;
+		}
+		ev->number = i;
+		goto done;
+	}
+
+	for (i = 0; i < number; i++)
+		ev->attrs[i] = attrs[i];
+
+done:
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+					HAL_EV_AVRCP_GET_ELEMENT_ATTRS,
+					sizeof(*ev) + ev->number, ev);
+
+	push_request(dev, AVRCP_GET_ELEMENT_ATTRIBUTES, 0, transaction);
+
+	return 0;
+
+}
+
+static int handle_register_notification_cmd(struct avrcp *session,
+						uint8_t transaction,
+						uint8_t event,
+						uint32_t interval,
+						void *user_data)
+{
+	struct avrcp_device *dev = user_data;
+	struct hal_ev_avrcp_register_notification ev;
+
+	DBG("");
+
+	/* TODO: Add any missing events supported by Android */
+	switch (event) {
+	case AVRCP_EVENT_STATUS_CHANGED:
+	case AVRCP_EVENT_TRACK_CHANGED:
+	case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ev.event = event;
+	ev.param = interval;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+					HAL_EV_AVRCP_REGISTER_NOTIFICATION,
+					sizeof(ev), &ev);
+
+	push_request(dev, AVRCP_REGISTER_NOTIFICATION, event, transaction);
+
+	return 0;
+}
+
+static const struct avrcp_control_ind control_ind = {
+	.get_capabilities = handle_get_capabilities_cmd,
+	.get_play_status = handle_get_play_status_cmd,
+	.get_element_attributes = handle_get_element_attrs_cmd,
+	.register_notification = handle_register_notification_cmd,
+};
+
+static bool handle_register_notification_rsp(struct avrcp *session, int err,
+						uint8_t code, uint8_t event,
+						void *params,
+						void *user_data)
+{
+	struct avrcp_device *dev = user_data;
+	struct hal_ev_avrcp_volume_changed ev;
+	uint8_t *volume = params;
+
+	if (err < 0) {
+		error("AVRCP: %s", strerror(-err));
+		return false;
+	}
+
+	if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
+		return false;
+
+	if (event != AVRCP_EVENT_VOLUME_CHANGED)
+		return false;
+
+	ev.type = code;
+	ev.volume = volume[0];
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+					HAL_EV_AVRCP_VOLUME_CHANGED,
+					sizeof(ev), &ev);
+
+	if (code == AVC_CTYPE_INTERIM)
+		return true;
+
+	avrcp_register_notification(dev->session, event, 0);
+	return false;
+}
+
+static void handle_get_capabilities_rsp(struct avrcp *session, int err,
+					uint8_t number, uint8_t *events,
+					void *user_data)
+{
+	struct avrcp_device *dev = user_data;
+	int i;
+
+	if (err < 0) {
+		error("AVRCP: %s", strerror(-err));
+		return;
+	}
+
+	for (i = 0; i < number; i++) {
+		if (events[i] != AVRCP_EVENT_VOLUME_CHANGED)
+			continue;
+
+		avrcp_register_notification(dev->session, events[i], 0);
+		break;
+	}
+
+	return;
+}
+
+static void handle_set_volume_rsp(struct avrcp *session, int err,
+						uint8_t value, void *user_data)
+{
+	struct hal_ev_avrcp_volume_changed ev;
+
+	if (err < 0) {
+		ev.volume = 0;
+		ev.type = AVC_CTYPE_REJECTED;
+		goto done;
+	}
+
+	ev.volume = value;
+	ev.type = AVC_CTYPE_ACCEPTED;
+
+done:
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+					HAL_EV_AVRCP_VOLUME_CHANGED,
+					sizeof(ev), &ev);
+}
+
+static const struct avrcp_control_cfm control_cfm = {
+	.get_capabilities = handle_get_capabilities_rsp,
+	.register_notification = handle_register_notification_rsp,
+	.set_volume = handle_set_volume_rsp,
+};
+
+static int avrcp_device_add_session(struct avrcp_device *dev, int fd,
+						uint16_t imtu, uint16_t omtu)
+{
+	struct hal_ev_avrcp_remote_features ev;
+	char address[18];
+
+	dev->session = avrcp_new(fd, imtu, omtu, dev->version);
+	if (!dev->session)
+		return -EINVAL;
+
+	avrcp_set_destroy_cb(dev->session, disconnect_cb, dev);
+	avrcp_set_passthrough_handlers(dev->session, passthrough_handlers,
+									dev);
+	avrcp_register_player(dev->session, &control_ind, &control_cfm, dev);
+
+	dev->queue = g_queue_new();
+
+	ba2str(&dev->dst, address);
+
+	/* FIXME: get the real name of the device */
+	avrcp_init_uinput(dev->session, "bluetooth", address);
+
+	bdaddr2android(&dev->dst, ev.bdaddr);
+	ev.features = HAL_AVRCP_FEATURE_NONE;
+
+	DBG("version 0x%02x", dev->version);
+
+	if (dev->version < 0x0103)
+		goto done;
+
+	ev.features |= HAL_AVRCP_FEATURE_METADATA;
+
+	if (dev->version < 0x0104)
+		goto done;
+
+	ev.features |= HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME;
+
+	avrcp_get_capabilities(dev->session, CAP_EVENTS_SUPPORTED);
+
+done:
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+					HAL_EV_AVRCP_REMOTE_FEATURES,
+					sizeof(ev), &ev);
+
+	return 0;
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct avrcp_device *dev = user_data;
+	uint16_t imtu, omtu;
+	char address[18];
+	GError *gerr = NULL;
+	int fd;
+
+	if (err) {
+		error("%s", err->message);
+		return;
+	}
+
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_IMTU, &imtu,
+			BT_IO_OPT_OMTU, &omtu,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	fd = g_io_channel_unix_get_fd(chan);
+	if (avrcp_device_add_session(dev, fd, imtu, omtu) < 0) {
+		avrcp_device_free(dev);
+		return;
+	}
+
+	g_io_channel_set_close_on_unref(chan, FALSE);
+
+	if (dev->io) {
+		g_io_channel_unref(dev->io);
+		dev->io = NULL;
+	}
+
+	DBG("%s connected", address);
+}
+
+static bool avrcp_device_connect(struct avrcp_device *dev, BtIOConnect cb)
+{
+	GError *err = NULL;
+
+	dev->io = bt_io_connect(cb, dev, NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_DEST_BDADDR, &dev->dst,
+					BT_IO_OPT_PSM, L2CAP_PSM_AVCTP,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	return true;
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	struct avrcp_device *dev = data;
+	sdp_list_t *list;
+
+	DBG("");
+
+	if (!g_slist_find(devices, dev))
+		return;
+
+	if (err < 0) {
+		error("Unable to get AV_REMOTE_SVCLASS_ID SDP record: %s",
+							strerror(-err));
+		goto fail;
+	}
+
+	if (!recs || !recs->data) {
+		error("No AVRCP records found");
+		goto fail;
+	}
+
+	for (list = recs; list; list = list->next) {
+		sdp_record_t *rec = list->data;
+		sdp_list_t *l;
+		sdp_profile_desc_t *desc;
+		int features;
+
+		if (sdp_get_profile_descs(rec, &l) < 0)
+			continue;
+
+		desc = l->data;
+		dev->version = desc->version;
+
+		if (sdp_get_int_attr(rec, SDP_ATTR_SUPPORTED_FEATURES,
+							&features) == 0)
+			dev->features = features;
+
+		sdp_list_free(l, free);
+		break;
+	}
+
+	if (dev->io) {
+		GError *gerr = NULL;
+		if (!bt_io_accept(dev->io, connect_cb, dev, NULL, &gerr)) {
+			error("bt_io_accept: %s", gerr->message);
+			g_error_free(gerr);
+			goto fail;
+		}
+		return;
+	}
+
+	if (!avrcp_device_connect(dev, connect_cb)) {
+		error("Unable to connect to AVRCP");
+		goto fail;
+	}
+
+	return;
+
+fail:
+	avrcp_device_remove(dev);
+}
+
+static int avrcp_device_search(struct avrcp_device *dev)
+{
+	uuid_t uuid;
+
+	sdp_uuid16_create(&uuid, AV_REMOTE_SVCLASS_ID);
+
+	return bt_search_service(&adapter_addr, &dev->dst, &uuid, search_cb,
+								dev, NULL, 0);
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+	struct avrcp_device *dev;
+	char address[18];
+	bdaddr_t dst;
+	GError *err = NULL;
+
+	bt_io_get(chan, &err,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	DBG("incoming connect from %s", address);
+
+	dev = avrcp_device_find(&dst);
+	if (dev && dev->session) {
+		error("AVRCP: Refusing unexpected connect");
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	dev = avrcp_device_new(&dst);
+	if (avrcp_device_search(dev) < 0) {
+		error("AVRCP: Failed to search SDP details");
+		avrcp_device_free(dev);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+	}
+
+	dev->io = g_io_channel_ref(chan);
+}
+
+bool bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	GError *err = NULL;
+	sdp_record_t *rec;
+
+	DBG("");
+
+	bacpy(&adapter_addr, addr);
+
+	server = bt_io_listen(NULL, confirm_cb, NULL, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_PSM, L2CAP_PSM_AVCTP,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_INVALID);
+	if (!server) {
+		error("Failed to listen on AVDTP channel: %s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	rec = avrcp_tg_record();
+	if (!rec) {
+		error("Failed to allocate AVRCP TG record");
+		goto fail;
+	}
+
+	if (bt_adapter_add_record(rec, 0) < 0) {
+		error("Failed to register AVRCP TG record");
+		sdp_record_free(rec);
+		goto fail;
+	}
+	record_tg_id = rec->handle;
+
+	rec = avrcp_ct_record();
+	if (!rec) {
+		error("Failed to allocate AVRCP CT record");
+		bt_adapter_remove_record(record_tg_id);
+		goto fail;
+	}
+
+	if (bt_adapter_add_record(rec, 0) < 0) {
+		error("Failed to register AVRCP CT record");
+		bt_adapter_remove_record(record_tg_id);
+		sdp_record_free(rec);
+		goto fail;
+	}
+	record_ct_id = rec->handle;
+
+	hal_ipc = ipc;
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_AVRCP, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+fail:
+	g_io_channel_shutdown(server, TRUE, NULL);
+	g_io_channel_unref(server);
+	server = NULL;
+
+	return false;
+}
+
+void bt_avrcp_unregister(void)
+{
+	DBG("");
+
+	g_slist_free_full(devices, avrcp_device_free);
+	devices = NULL;
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_AVRCP);
+	hal_ipc = NULL;
+
+	bt_adapter_remove_record(record_tg_id);
+	record_tg_id = 0;
+
+	bt_adapter_remove_record(record_ct_id);
+	record_ct_id = 0;
+
+	if (server) {
+		g_io_channel_shutdown(server, TRUE, NULL);
+		g_io_channel_unref(server);
+		server = NULL;
+	}
+}
+
+void bt_avrcp_connect(const bdaddr_t *dst)
+{
+	struct avrcp_device *dev;
+	char addr[18];
+
+	DBG("");
+
+	if (avrcp_device_find(dst))
+		return;
+
+	dev = avrcp_device_new(dst);
+	if (avrcp_device_search(dev) < 0) {
+		error("AVRCP: Failed to search SDP details");
+		avrcp_device_free(dev);
+	}
+
+	ba2str(&dev->dst, addr);
+	DBG("connecting to %s", addr);
+}
+
+void bt_avrcp_disconnect(const bdaddr_t *dst)
+{
+	struct avrcp_device *dev;
+
+	DBG("");
+
+	dev = avrcp_device_find(dst);
+	if (!dev)
+		return;
+
+	if (dev->session) {
+		avrcp_shutdown(dev->session);
+		return;
+	}
+
+	avrcp_device_remove(dev);
+}
diff --git a/repo/android/avrcp.h b/repo/android/avrcp.h
new file mode 100644
index 0000000..11e79b7
--- /dev/null
+++ b/repo/android/avrcp.h
@@ -0,0 +1,28 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+bool bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_avrcp_unregister(void);
+
+void bt_avrcp_connect(const bdaddr_t *dst);
+void bt_avrcp_disconnect(const bdaddr_t *dst);
diff --git a/repo/android/bluetooth.c b/repo/android/bluetooth.c
new file mode 100644
index 0000000..51a31fe
--- /dev/null
+++ b/repo/android/bluetooth.c
@@ -0,0 +1,5343 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.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/mgmt.h"
+#include "lib/uuid.h"
+#include "src/shared/util.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/queue.h"
+#include "src/eir.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/sdpd.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "utils.h"
+#include "bluetooth.h"
+
+#define DUT_MODE_FILE "/sys/kernel/debug/bluetooth/hci%u/dut_mode"
+
+#define SETTINGS_FILE ANDROID_STORAGEDIR"/settings"
+#define DEVICES_FILE ANDROID_STORAGEDIR"/devices"
+#define CACHE_FILE ANDROID_STORAGEDIR"/cache"
+
+#define ADAPTER_MAJOR_CLASS 0x02 /* Phone */
+#define ADAPTER_MINOR_CLASS 0x03 /* Smartphone */
+
+/* Default to DisplayYesNo */
+#define DEFAULT_IO_CAPABILITY 0x01
+
+/* Default discoverable timeout 120sec as in Android */
+#define DEFAULT_DISCOVERABLE_TIMEOUT 120
+
+#define DEVICES_CACHE_MAX 300
+
+#define BASELEN_PROP_CHANGED (sizeof(struct hal_ev_adapter_props_changed) \
+					+ sizeof(struct hal_property))
+
+#define BASELEN_REMOTE_DEV_PROP (sizeof(struct hal_ev_remote_device_props) \
+					+ sizeof(struct hal_property))
+
+#define SCAN_TYPE_NONE 0
+#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR)
+#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
+#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)
+
+#define BDADDR_LE (BDADDR_LE_RANDOM | BDADDR_LE_PUBLIC)
+
+struct device {
+	bdaddr_t bdaddr;
+	uint8_t bdaddr_type;
+
+	bdaddr_t rpa;
+	uint8_t rpa_type;
+
+	bool le;
+	bool bredr;
+
+	bool pairing;
+
+	bool bredr_paired;
+	bool bredr_bonded;
+	bool le_paired;
+	bool le_bonded;
+
+	bool in_white_list;
+
+	bool connected;
+
+	char *name;
+	char *friendly_name;
+
+	uint32_t class;
+	int32_t rssi;
+
+	time_t bredr_seen;
+	time_t le_seen;
+
+	GSList *uuids;
+
+	bool found; /* if device is found in current discovery session */
+	unsigned int confirm_id; /* mgtm command id if command pending */
+
+	bool valid_remote_csrk;
+	bool remote_csrk_auth;
+	uint8_t remote_csrk[16];
+	uint32_t remote_sign_cnt;
+
+	bool valid_local_csrk;
+	bool local_csrk_auth;
+	uint8_t local_csrk[16];
+	uint32_t local_sign_cnt;
+	uint16_t gatt_ccc;
+};
+
+struct browse_req {
+	bdaddr_t bdaddr;
+	GSList *uuids;
+	int search_uuid;
+	int reconnect_attempt;
+};
+
+static struct {
+	uint16_t index;
+
+	bdaddr_t bdaddr;
+	uint32_t dev_class;
+
+	char *name;
+
+	uint8_t max_advert_instance;
+	uint8_t rpa_offload_supported;
+	uint8_t max_irk_list_size;
+	uint8_t max_scan_filters_supported;
+	uint16_t scan_result_storage_size;
+	uint8_t activity_energy_info_supported;
+
+	uint32_t current_settings;
+	uint32_t supported_settings;
+
+	bool le_scanning;
+	uint8_t cur_discovery_type;
+	uint8_t exp_discovery_type;
+	uint32_t discoverable_timeout;
+
+	GSList *uuids;
+} adapter = {
+	.index = MGMT_INDEX_NONE,
+	.dev_class = 0,
+	.name = NULL,
+	.max_advert_instance = 0,
+	.rpa_offload_supported = 0,
+	.max_irk_list_size = 0,
+	.max_scan_filters_supported = 0,
+	.scan_result_storage_size = 0,
+	.activity_energy_info_supported = 0,
+	.current_settings = 0,
+	.supported_settings = 0,
+	.cur_discovery_type = SCAN_TYPE_NONE,
+	.exp_discovery_type = SCAN_TYPE_NONE,
+	.discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT,
+	.uuids = NULL,
+};
+
+static const uint16_t uuid_list[] = {
+	L2CAP_UUID,
+	PNP_INFO_SVCLASS_ID,
+	PUBLIC_BROWSE_GROUP,
+	0
+};
+
+static uint16_t option_index = MGMT_INDEX_NONE;
+static struct mgmt *mgmt_if = NULL;
+
+static GSList *bonded_devices = NULL;
+static GSList *cached_devices = NULL;
+
+static bt_le_device_found gatt_device_found_cb = NULL;
+static bt_le_discovery_stopped gatt_discovery_stopped_cb = NULL;
+
+/* This list contains addresses which are asked for records */
+static GSList *browse_reqs;
+
+static struct ipc *hal_ipc = NULL;
+
+static bool kernel_conn_control = false;
+
+static struct queue *unpaired_cb_list = NULL;
+static struct queue *paired_cb_list = NULL;
+
+static void get_device_android_addr(struct device *dev, uint8_t *addr)
+{
+	/*
+	 * If RPA is set it means that IRK was received and ID address is being
+	 * used. Android Framework is still using old RPA and it needs to be
+	 * used in notifications.
+	 */
+	if (bacmp(&dev->rpa, BDADDR_ANY))
+		bdaddr2android(&dev->rpa, addr);
+	else
+		bdaddr2android(&dev->bdaddr, addr);
+}
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+	info("%s%s", prefix, str);
+}
+
+static void store_adapter_config(void)
+{
+	GKeyFile *key_file;
+	gsize length = 0;
+	char addr[18];
+	char *data;
+
+	key_file = g_key_file_new();
+
+	g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL);
+
+	ba2str(&adapter.bdaddr, addr);
+
+	g_key_file_set_string(key_file, "General", "Address", addr);
+
+	if (adapter.name)
+		g_key_file_set_string(key_file, "General", "Name",
+				adapter.name);
+
+	g_key_file_set_integer(key_file, "General", "DiscoverableTimeout",
+						adapter.discoverable_timeout);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+
+	g_file_set_contents(SETTINGS_FILE, data, length, NULL);
+
+	g_free(data);
+	g_key_file_free(key_file);
+}
+
+static void load_adapter_config(void)
+{
+	GError *gerr = NULL;
+	GKeyFile *key_file;
+	char *str;
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL);
+
+	str = g_key_file_get_string(key_file, "General", "Address", NULL);
+	if (!str) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	str2ba(str, &adapter.bdaddr);
+	g_free(str);
+
+	adapter.name = g_key_file_get_string(key_file, "General", "Name", NULL);
+
+	adapter.discoverable_timeout = g_key_file_get_integer(key_file,
+				"General", "DiscoverableTimeout", &gerr);
+	if (gerr) {
+		adapter.discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT;
+		g_clear_error(&gerr);
+	}
+
+	g_key_file_free(key_file);
+}
+
+static void store_device_info(struct device *dev, const char *path)
+{
+	GKeyFile *key_file;
+	char addr[18];
+	gsize length = 0;
+	char **uuids = NULL;
+	char *str;
+
+	ba2str(&dev->bdaddr, addr);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, path, 0, NULL);
+
+	g_key_file_set_boolean(key_file, addr, "BREDR", dev->bredr);
+
+	if (dev->le)
+		g_key_file_set_integer(key_file, addr, "AddressType",
+							dev->bdaddr_type);
+
+	g_key_file_set_string(key_file, addr, "Name", dev->name);
+
+	if (dev->friendly_name)
+		g_key_file_set_string(key_file, addr, "FriendlyName",
+							dev->friendly_name);
+	else
+		g_key_file_remove_key(key_file, addr, "FriendlyName", NULL);
+
+	if (dev->class)
+		g_key_file_set_integer(key_file, addr, "Class", dev->class);
+	else
+		g_key_file_remove_key(key_file, addr, "Class", NULL);
+
+	if (dev->bredr_seen > dev->le_seen)
+		g_key_file_set_integer(key_file, addr, "Timestamp",
+							dev->bredr_seen);
+	else
+		g_key_file_set_integer(key_file, addr, "Timestamp",
+								dev->le_seen);
+
+	if (dev->uuids) {
+		GSList *l;
+		int i;
+
+		uuids = g_new0(char *, g_slist_length(dev->uuids) + 1);
+
+		for (i = 0, l = dev->uuids; l; l = g_slist_next(l), i++) {
+			int j;
+			uint8_t *u = l->data;
+			char *uuid_str = g_malloc0(33);
+
+			for (j = 0; j < 16; j++)
+				sprintf(uuid_str + (j * 2), "%2.2X", u[j]);
+
+			uuids[i] = uuid_str;
+		}
+
+		g_key_file_set_string_list(key_file, addr, "Services",
+						(const char **)uuids, i);
+	} else {
+		g_key_file_remove_key(key_file, addr, "Services", NULL);
+	}
+
+	str = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(path, str, length, NULL);
+	g_free(str);
+
+	g_key_file_free(key_file);
+	g_strfreev(uuids);
+}
+
+static void remove_device_info(struct device *dev, const char *path)
+{
+	GKeyFile *key_file;
+	gsize length = 0;
+	char addr[18];
+	char *str;
+
+	ba2str(&dev->bdaddr, addr);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, path, 0, NULL);
+
+	g_key_file_remove_group(key_file, addr, NULL);
+
+	str = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(path, str, length, NULL);
+	g_free(str);
+
+	g_key_file_free(key_file);
+}
+
+static int device_match(gconstpointer a, gconstpointer b)
+{
+	const struct device *dev = a;
+	const bdaddr_t *bdaddr = b;
+
+	/* Android is using RPA even if IRK was received and ID addr resolved */
+	if (!bacmp(&dev->rpa, bdaddr))
+		return 0;
+
+	return bacmp(&dev->bdaddr, bdaddr);
+}
+
+static struct device *find_device(const bdaddr_t *bdaddr)
+{
+	GSList *l;
+
+	l = g_slist_find_custom(bonded_devices, bdaddr, device_match);
+	if (l)
+		return l->data;
+
+	l = g_slist_find_custom(cached_devices, bdaddr, device_match);
+	if (l)
+		return l->data;
+
+	return NULL;
+}
+
+static void free_device(struct device *dev)
+{
+	if (dev->confirm_id)
+		mgmt_cancel(mgmt_if, dev->confirm_id);
+
+	g_free(dev->name);
+	g_free(dev->friendly_name);
+	g_slist_free_full(dev->uuids, g_free);
+	g_free(dev);
+}
+
+static void cache_device(struct device *new_dev)
+{
+	struct device *dev;
+	GSList *l;
+
+	l = g_slist_find(cached_devices, new_dev);
+	if (l) {
+		cached_devices = g_slist_remove(cached_devices, new_dev);
+		goto cache;
+	}
+
+	if (g_slist_length(cached_devices) < DEVICES_CACHE_MAX)
+		goto cache;
+
+	l = g_slist_last(cached_devices);
+	dev = l->data;
+
+	cached_devices = g_slist_remove(cached_devices, dev);
+	remove_device_info(dev, CACHE_FILE);
+	free_device(dev);
+
+cache:
+	cached_devices = g_slist_prepend(cached_devices, new_dev);
+	store_device_info(new_dev, CACHE_FILE);
+}
+
+static struct device *create_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+	struct device *dev;
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+	DBG("%s", addr);
+
+	dev = g_new0(struct device, 1);
+
+	bacpy(&dev->bdaddr, bdaddr);
+
+	if (bdaddr_type == BDADDR_BREDR) {
+		dev->bredr = true;
+		dev->bredr_seen = time(NULL);
+	} else {
+		dev->le = true;
+		dev->bdaddr_type = bdaddr_type;
+		dev->le_seen = time(NULL);
+	}
+
+	/*
+	 * Use address for name, will be change if one is present
+	 * eg. in EIR or set by set_property.
+	 */
+	dev->name = g_strdup(addr);
+
+	return dev;
+}
+
+static struct device *get_device(const bdaddr_t *bdaddr, uint8_t type)
+{
+	struct device *dev;
+
+	dev = find_device(bdaddr);
+	if (dev)
+		return dev;
+
+	dev = create_device(bdaddr, type);
+
+	cache_device(dev);
+
+	return dev;
+}
+
+static struct device *find_device_android(const uint8_t *addr)
+{
+	bdaddr_t bdaddr;
+
+	android2bdaddr(addr, &bdaddr);
+
+	return find_device(&bdaddr);
+}
+
+static struct device *get_device_android(const uint8_t *addr)
+{
+	bdaddr_t bdaddr;
+
+	android2bdaddr(addr, &bdaddr);
+
+	return get_device(&bdaddr, BDADDR_BREDR);
+}
+
+static  void send_adapter_property(uint8_t type, uint16_t len, const void *val)
+{
+	uint8_t buf[BASELEN_PROP_CHANGED + len];
+	struct hal_ev_adapter_props_changed *ev = (void *) buf;
+
+	ev->status = HAL_STATUS_SUCCESS;
+	ev->num_props = 1;
+	ev->props[0].type = type;
+	ev->props[0].len = len;
+	memcpy(ev->props[0].val, val, len);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf);
+}
+
+static void adapter_name_changed(const uint8_t *name)
+{
+	/* Android expects string value without NULL terminator */
+	send_adapter_property(HAL_PROP_ADAPTER_NAME,
+					strlen((const char *) name), name);
+}
+
+static void adapter_set_name(const uint8_t *name)
+{
+	if (!g_strcmp0(adapter.name, (const char *) name))
+		return;
+
+	DBG("%s", name);
+
+	g_free(adapter.name);
+	adapter.name = g_strdup((const char *) name);
+
+	store_adapter_config();
+
+	adapter_name_changed(name);
+}
+
+static void mgmt_local_name_changed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_cp_set_local_name *rp = param;
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size of local name changed parameters");
+		return;
+	}
+
+	adapter_set_name(rp->name);
+
+	/* TODO Update services if needed */
+}
+
+static void powered_changed(void)
+{
+	struct hal_ev_adapter_state_changed ev;
+
+	ev.state = (adapter.current_settings & MGMT_SETTING_POWERED) ?
+						HAL_POWER_ON : HAL_POWER_OFF;
+
+	DBG("%u", ev.state);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_EV_ADAPTER_STATE_CHANGED, sizeof(ev), &ev);
+}
+
+static uint8_t settings2scan_mode(void)
+{
+	bool connectable, discoverable;
+
+	connectable = adapter.current_settings & MGMT_SETTING_CONNECTABLE;
+	discoverable = adapter.current_settings & MGMT_SETTING_DISCOVERABLE;
+
+	if (connectable && discoverable)
+		return HAL_ADAPTER_SCAN_MODE_CONN_DISC;
+
+	if (connectable)
+		return HAL_ADAPTER_SCAN_MODE_CONN;
+
+	return HAL_ADAPTER_SCAN_MODE_NONE;
+}
+
+static void scan_mode_changed(void)
+{
+	uint8_t mode;
+
+	mode = settings2scan_mode();
+
+	DBG("mode %u", mode);
+
+	send_adapter_property(HAL_PROP_ADAPTER_SCAN_MODE, sizeof(mode), &mode);
+}
+
+static void adapter_class_changed(void)
+{
+	send_adapter_property(HAL_PROP_ADAPTER_CLASS, sizeof(adapter.dev_class),
+							&adapter.dev_class);
+}
+
+static void settings_changed(uint32_t settings)
+{
+	uint32_t changed_mask;
+	uint32_t scan_mode_mask;
+
+	changed_mask = adapter.current_settings ^ settings;
+
+	adapter.current_settings = settings;
+
+	DBG("0x%08x", changed_mask);
+
+	if (changed_mask & MGMT_SETTING_POWERED)
+		powered_changed();
+
+	scan_mode_mask = MGMT_SETTING_CONNECTABLE |
+					MGMT_SETTING_DISCOVERABLE;
+
+	/*
+	 * Only when powered, the connectable and discoverable
+	 * state changes should be communicated.
+	 */
+	if (adapter.current_settings & MGMT_SETTING_POWERED)
+		if (changed_mask & scan_mode_mask)
+			scan_mode_changed();
+}
+
+static void new_settings_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	uint32_t settings;
+
+	if (length < sizeof(settings)) {
+		error("Wrong size of new settings parameters");
+		return;
+	}
+
+	settings = get_le32(param);
+
+	DBG("settings: 0x%8.8x -> 0x%8.8x", adapter.current_settings,
+								settings);
+
+	if (settings == adapter.current_settings)
+		return;
+
+	settings_changed(settings);
+}
+
+static void mgmt_dev_class_changed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_cod *rp = param;
+	uint32_t dev_class;
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size of class of device changed parameters");
+		return;
+	}
+
+	dev_class = rp->val[0] | (rp->val[1] << 8) | (rp->val[2] << 16);
+
+	if (dev_class == adapter.dev_class)
+		return;
+
+	DBG("Class: 0x%06x", dev_class);
+
+	adapter.dev_class = dev_class;
+
+	adapter_class_changed();
+
+	/* TODO: Gatt attrib set*/
+}
+
+void bt_store_gatt_ccc(const bdaddr_t *dst, uint16_t value)
+{
+	struct device *dev;
+	GKeyFile *key_file;
+	gsize length = 0;
+	char addr[18];
+	char *data;
+
+	dev = find_device(dst);
+	if (!dev)
+		return;
+
+	key_file = g_key_file_new();
+
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	ba2str(&dev->bdaddr, addr);
+
+	DBG("%s Gatt CCC %d", addr, value);
+
+	g_key_file_set_integer(key_file, addr, "GattCCC", value);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+
+	dev->gatt_ccc = value;
+}
+
+uint16_t bt_get_gatt_ccc(const bdaddr_t *addr)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return 0;
+
+	return dev->gatt_ccc;
+}
+
+static void store_link_key(const bdaddr_t *dst, const uint8_t *key,
+					uint8_t type, uint8_t pin_length)
+{
+	GKeyFile *key_file;
+	char key_str[33];
+	gsize length = 0;
+	char addr[18];
+	char *data;
+	int i;
+
+	key_file = g_key_file_new();
+
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	ba2str(dst, addr);
+
+	DBG("%s type %u pin_len %u", addr, type, pin_length);
+
+	for (i = 0; i < 16; i++)
+		sprintf(key_str + (i * 2), "%2.2X", key[i]);
+
+	g_key_file_set_string(key_file, addr, "LinkKey", key_str);
+	g_key_file_set_integer(key_file, addr, "LinkKeyType", type);
+	g_key_file_set_integer(key_file, addr, "LinkKeyPinLength", pin_length);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+static void send_bond_state_change(struct device *dev, uint8_t status,
+								uint8_t state)
+{
+	struct hal_ev_bond_state_changed ev;
+
+	ev.status = status;
+	ev.state = state;
+	get_device_android_addr(dev, ev.bdaddr);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_EV_BOND_STATE_CHANGED, sizeof(ev), &ev);
+}
+
+static void update_bredr_state(struct device *dev, bool pairing, bool paired,
+								bool bonded)
+{
+	if (pairing == dev->pairing && paired == dev->bredr_paired &&
+						bonded == dev->bredr_bonded)
+		return;
+
+	/* avoid unpairing device on incoming pairing request */
+	if (pairing && dev->bredr_paired)
+		goto done;
+
+	/* avoid unpairing device if pairing failed */
+	if (!pairing && !paired && dev->pairing && dev->bredr_paired)
+		goto done;
+
+	if (paired && !dev->le_paired && !dev->bredr_paired) {
+		cached_devices = g_slist_remove(cached_devices, dev);
+		bonded_devices = g_slist_prepend(bonded_devices, dev);
+		remove_device_info(dev, CACHE_FILE);
+		store_device_info(dev, DEVICES_FILE);
+	} else if (!paired && !dev->le_paired) {
+		bonded_devices = g_slist_remove(bonded_devices, dev);
+		remove_device_info(dev, DEVICES_FILE);
+		cache_device(dev);
+	}
+
+	dev->bredr_paired = paired;
+
+	if (dev->bredr_paired)
+		dev->bredr_bonded = dev->bredr_bonded || bonded;
+	else
+		dev->bredr_bonded = false;
+
+done:
+	dev->pairing = pairing;
+}
+
+static void update_le_state(struct device *dev, bool pairing, bool paired,
+								bool bonded)
+{
+	if (pairing == dev->pairing && paired == dev->le_paired &&
+						bonded == dev->le_bonded)
+		return;
+
+	/* avoid unpairing device on incoming pairing request */
+	if (pairing && dev->le_paired)
+		goto done;
+
+	/* avoid unpairing device if pairing failed */
+	if (!pairing && !paired && dev->pairing && dev->le_paired)
+		goto done;
+
+	if (paired && !dev->bredr_paired && !dev->le_paired) {
+		cached_devices = g_slist_remove(cached_devices, dev);
+		bonded_devices = g_slist_prepend(bonded_devices, dev);
+		remove_device_info(dev, CACHE_FILE);
+		store_device_info(dev, DEVICES_FILE);
+	} else if (!paired && !dev->bredr_paired) {
+		bonded_devices = g_slist_remove(bonded_devices, dev);
+		remove_device_info(dev, DEVICES_FILE);
+		dev->valid_local_csrk = false;
+		dev->valid_remote_csrk = false;
+		dev->local_sign_cnt = 0;
+		dev->remote_sign_cnt = 0;
+		memset(dev->local_csrk, 0, sizeof(dev->local_csrk));
+		memset(dev->remote_csrk, 0, sizeof(dev->remote_csrk));
+		cache_device(dev);
+	}
+
+	dev->le_paired = paired;
+
+	if (dev->le_paired)
+		dev->le_bonded = dev->le_bonded || bonded;
+	else
+		dev->le_bonded = false;
+
+done:
+	dev->pairing = pairing;
+}
+
+static uint8_t device_bond_state(struct device *dev)
+{
+	if (dev->pairing)
+		return HAL_BOND_STATE_BONDING;
+
+	/*
+	 * We are checking for paired here instead of bonded as HAL API is
+	 * using BOND state also if there was no bonding pairing.
+	 */
+	if (dev->bredr_paired || dev->le_paired)
+		return HAL_BOND_STATE_BONDED;
+
+	return HAL_BOND_STATE_NONE;
+}
+
+static void update_bond_state(struct device *dev, uint8_t status,
+					uint8_t old_bond, uint8_t new_bond)
+{
+	if (old_bond == new_bond)
+		return;
+
+	/*
+	 * When internal bond state changes from bond to non-bond or other way,
+	 * BfA needs to send bonding state to Android in the middle. Otherwise
+	 * Android will not handle it correctly
+	 */
+	if ((old_bond == HAL_BOND_STATE_NONE &&
+				new_bond == HAL_BOND_STATE_BONDED) ||
+				(old_bond == HAL_BOND_STATE_BONDED &&
+				new_bond == HAL_BOND_STATE_NONE))
+		send_bond_state_change(dev, HAL_STATUS_SUCCESS,
+						HAL_BOND_STATE_BONDING);
+
+	send_bond_state_change(dev, status, new_bond);
+}
+
+static void send_paired_notification(void *data, void *user_data)
+{
+	bt_paired_device_cb cb = data;
+	struct device *dev = user_data;
+
+	cb(&dev->bdaddr);
+}
+
+static void update_device_state(struct device *dev, uint8_t addr_type,
+				uint8_t status, bool pairing, bool paired,
+				bool bonded)
+{
+	uint8_t old_bond, new_bond;
+
+	old_bond = device_bond_state(dev);
+
+	if (addr_type == BDADDR_BREDR)
+		update_bredr_state(dev, pairing, paired, bonded);
+	else
+		update_le_state(dev, pairing, paired, bonded);
+
+	new_bond = device_bond_state(dev);
+
+	update_bond_state(dev, status, old_bond, new_bond);
+}
+
+static void send_device_property(struct device *dev, uint8_t type,
+						uint16_t len, const void *val)
+{
+	uint8_t buf[BASELEN_REMOTE_DEV_PROP + len];
+	struct hal_ev_remote_device_props *ev = (void *) buf;
+
+	ev->status = HAL_STATUS_SUCCESS;
+	get_device_android_addr(dev, ev->bdaddr);
+	ev->num_props = 1;
+	ev->props[0].type = type;
+	ev->props[0].len = len;
+	memcpy(ev->props[0].val, val, len);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_EV_REMOTE_DEVICE_PROPS, sizeof(buf), buf);
+}
+
+static void send_device_uuids_notif(struct device *dev)
+{
+	uint8_t buf[sizeof(uint128_t) * g_slist_length(dev->uuids)];
+	uint8_t *ptr = buf;
+	GSList *l;
+
+	for (l = dev->uuids; l; l = g_slist_next(l)) {
+		memcpy(ptr, l->data, sizeof(uint128_t));
+		ptr += sizeof(uint128_t);
+	}
+
+	send_device_property(dev, HAL_PROP_DEVICE_UUIDS, sizeof(buf), buf);
+}
+
+static void set_device_uuids(struct device *dev, GSList *uuids)
+{
+	g_slist_free_full(dev->uuids, g_free);
+	dev->uuids = uuids;
+
+	if (dev->le_paired || dev->bredr_paired)
+		store_device_info(dev, DEVICES_FILE);
+	else
+		store_device_info(dev, CACHE_FILE);
+
+	send_device_uuids_notif(dev);
+}
+
+static void browse_req_free(struct browse_req *req)
+{
+	g_slist_free_full(req->uuids, g_free);
+	g_free(req);
+}
+
+static int uuid_128_cmp(gconstpointer a, gconstpointer b)
+{
+	return memcmp(a, b, sizeof(uint128_t));
+}
+
+static void update_records(struct browse_req *req, sdp_list_t *recs)
+{
+	for (; recs; recs = recs->next) {
+		sdp_record_t *rec = (sdp_record_t *) recs->data;
+		sdp_list_t *svcclass = NULL;
+		uuid_t uuid128;
+		uuid_t *tmp;
+		uint8_t *new_uuid;
+
+		if (!rec)
+			break;
+
+		if (sdp_get_service_classes(rec, &svcclass) < 0)
+			continue;
+
+		if (!svcclass)
+			continue;
+
+		tmp = svcclass->data;
+
+		switch (tmp->type) {
+		case SDP_UUID16:
+			sdp_uuid16_to_uuid128(&uuid128, tmp);
+			break;
+		case SDP_UUID32:
+			sdp_uuid32_to_uuid128(&uuid128, tmp);
+			break;
+		case SDP_UUID128:
+			memcpy(&uuid128, tmp, sizeof(uuid_t));
+			break;
+		default:
+			sdp_list_free(svcclass, free);
+			continue;
+		}
+
+		new_uuid = g_malloc(16);/* size of 128 bit uuid */
+		memcpy(new_uuid, &uuid128.value.uuid128,
+				sizeof(uuid128.value.uuid128));
+
+		/* Check if uuid is already added */
+		if (g_slist_find_custom(req->uuids, new_uuid, uuid_128_cmp))
+			g_free(new_uuid);
+		else
+			req->uuids = g_slist_append(req->uuids, new_uuid);
+
+		sdp_list_free(svcclass, free);
+	}
+}
+
+static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+	struct browse_req *req = user_data;
+	struct device *dev;
+	uuid_t uuid;
+
+	/*
+	 * If we have a valid response and req->search_uuid == 2, then L2CAP
+	 * UUID & PNP searching was successful -- we are done
+	 */
+	if (err < 0 || req->search_uuid == 2) {
+		if (err == -ECONNRESET && req->reconnect_attempt < 1) {
+			req->search_uuid--;
+			req->reconnect_attempt++;
+		} else {
+			goto done;
+		}
+	}
+
+	update_records(req, recs);
+
+	/* Search for mandatory uuids */
+	if (uuid_list[req->search_uuid]) {
+		sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
+		bt_search_service(&adapter.bdaddr, &req->bdaddr, &uuid,
+						browse_cb, user_data, NULL, 0);
+		return;
+	}
+
+done:
+	dev = find_device(&req->bdaddr);
+	if (dev) {
+		set_device_uuids(dev, req->uuids);
+		req->uuids = NULL;
+	}
+
+	browse_reqs = g_slist_remove(browse_reqs, req);
+	browse_req_free(req);
+}
+
+static int req_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct browse_req *req = a;
+	const bdaddr_t *bdaddr = b;
+
+	return bacmp(&req->bdaddr, bdaddr);
+}
+
+static uint8_t browse_remote_sdp(const bdaddr_t *addr)
+{
+	struct browse_req *req;
+	uuid_t uuid;
+
+	if (g_slist_find_custom(browse_reqs, addr, req_cmp))
+		return HAL_STATUS_SUCCESS;
+
+	req = g_new0(struct browse_req, 1);
+	bacpy(&req->bdaddr, addr);
+	sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
+
+	if (bt_search_service(&adapter.bdaddr,
+			&req->bdaddr, &uuid, browse_cb, req, NULL , 0) < 0) {
+		browse_req_free(req);
+		return HAL_STATUS_FAILED;
+	}
+
+	browse_reqs = g_slist_append(browse_reqs, req);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void send_remote_sdp_rec_notify(bt_uuid_t *uuid, int channel,
+					char *name, uint8_t name_len,
+					uint8_t status, bdaddr_t *bdaddr)
+{
+	struct hal_prop_device_service_rec *prop;
+	uint8_t buf[BASELEN_REMOTE_DEV_PROP + name_len + sizeof(*prop)];
+	struct hal_ev_remote_device_props *ev = (void *) buf;
+	size_t prop_len = sizeof(*prop) + name_len;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (uuid && status == HAL_STATUS_SUCCESS) {
+		prop = (void *) &ev->props[0].val;
+		prop->name_len = name_len;
+		prop->channel = (uint16_t)channel;
+		memcpy(prop->name, name, name_len);
+		memcpy(prop->uuid, &uuid->value.u128, sizeof(prop->uuid));
+	}
+
+	ev->num_props = 1;
+	ev->status = status;
+	ev->props[0].len = prop_len;
+	bdaddr2android(bdaddr, ev->bdaddr);
+	ev->props[0].type = HAL_PROP_DEVICE_SERVICE_REC;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+						HAL_EV_REMOTE_DEVICE_PROPS,
+						sizeof(buf), buf);
+}
+
+static void find_remote_sdp_rec_cb(sdp_list_t *recs, int err,
+							gpointer user_data)
+{
+	bdaddr_t *addr = user_data;
+	uint8_t name_len;
+	uint8_t status;
+	char name_buf[256];
+	int channel;
+	bt_uuid_t uuid;
+	uuid_t uuid128_sdp;
+	sdp_list_t *protos;
+	sdp_record_t *sdp_rec;
+
+	if (err < 0) {
+		error("error while search remote sdp records");
+		status = HAL_STATUS_FAILED;
+		send_remote_sdp_rec_notify(NULL, 0, NULL, 0, status, addr);
+		goto done;
+	}
+
+	if (!recs) {
+		info("No service records found on remote");
+		status = HAL_STATUS_SUCCESS;
+		send_remote_sdp_rec_notify(NULL, 0, NULL, 0, status, addr);
+		goto done;
+	}
+
+	for ( ; recs; recs = recs->next) {
+		sdp_rec = recs->data;
+
+		switch (sdp_rec->svclass.type) {
+		case SDP_UUID16:
+			sdp_uuid16_to_uuid128(&uuid128_sdp,
+							&sdp_rec->svclass);
+			break;
+		case SDP_UUID32:
+			sdp_uuid32_to_uuid128(&uuid128_sdp,
+							&sdp_rec->svclass);
+			break;
+		case SDP_UUID128:
+			break;
+		default:
+			error("wrong sdp uuid type");
+			goto done;
+		}
+
+		if (!sdp_get_access_protos(sdp_rec, &protos)) {
+			channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+
+			sdp_list_foreach(protos,
+						(sdp_list_func_t) sdp_list_free,
+						NULL);
+			sdp_list_free(protos, NULL);
+		} else
+			channel = -1;
+
+		if (channel < 0) {
+			error("can't get channel for sdp record");
+			channel = 0;
+		}
+
+		if (!sdp_get_service_name(sdp_rec, name_buf, sizeof(name_buf)))
+			name_len = strlen(name_buf);
+		else
+			name_len = 0;
+
+		uuid.type = BT_UUID128;
+		memcpy(&uuid.value.u128, uuid128_sdp.value.uuid128.data,
+						sizeof(uuid.value.u128));
+		status = HAL_STATUS_SUCCESS;
+
+		send_remote_sdp_rec_notify(&uuid, channel, name_buf, name_len,
+								status, addr);
+	}
+
+done:
+	g_free(addr);
+}
+
+static uint8_t find_remote_sdp_rec(const bdaddr_t *addr,
+						const uint8_t *find_uuid)
+{
+	bdaddr_t *bdaddr;
+	uuid_t uuid;
+
+	/* from android we always get full 128bit length uuid */
+	sdp_uuid128_create(&uuid, find_uuid);
+
+	bdaddr = g_new(bdaddr_t, 1);
+	if (!bdaddr)
+		return HAL_STATUS_NOMEM;
+
+	memcpy(bdaddr, addr, sizeof(*bdaddr));
+
+	if (bt_search_service(&adapter.bdaddr, addr, &uuid,
+				find_remote_sdp_rec_cb, bdaddr, NULL, 0) < 0) {
+		g_free(bdaddr);
+		return HAL_STATUS_FAILED;
+	}
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void new_link_key_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_link_key *ev = param;
+	const struct mgmt_addr_info *addr = &ev->key.addr;
+	struct device *dev;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small new link key event");
+		return;
+	}
+
+	ba2str(&addr->bdaddr, dst);
+
+	DBG("new key for %s type %u pin_len %u",
+					dst, ev->key.type, ev->key.pin_len);
+
+	if (ev->key.pin_len > 16) {
+		error("Invalid PIN length (%u) in new_key event",
+							ev->key.pin_len);
+		return;
+	}
+
+	dev = get_device(&ev->key.addr.bdaddr, ev->key.addr.type);
+	if (!dev)
+		return;
+
+	update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+							true, !!ev->store_hint);
+
+	if (ev->store_hint) {
+		const struct mgmt_link_key_info *key = &ev->key;
+
+		store_link_key(&addr->bdaddr, key->val, key->type,
+								key->pin_len);
+	}
+
+	browse_remote_sdp(&addr->bdaddr);
+}
+
+static uint8_t get_device_name(struct device *dev)
+{
+	send_device_property(dev, HAL_PROP_DEVICE_NAME,
+						strlen(dev->name), dev->name);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void pin_code_request_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_pin_code_request *ev = param;
+	struct hal_ev_pin_request hal_ev;
+	struct device *dev;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small PIN code request event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, dst);
+
+	dev = get_device(&ev->addr.bdaddr, BDADDR_BREDR);
+
+	/*
+	 * Workaround for Android Bluetooth.apk issue: send remote
+	 * device property
+	 */
+	get_device_name(dev);
+
+	update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+								false, false);
+
+	DBG("%s type %u secure %u", dst, ev->addr.type, ev->secure);
+
+	/* Name already sent in remote device prop */
+	memset(&hal_ev, 0, sizeof(hal_ev));
+	get_device_android_addr(dev, hal_ev.bdaddr);
+	hal_ev.class_of_dev = dev->class;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_PIN_REQUEST,
+						sizeof(hal_ev), &hal_ev);
+}
+
+static void send_ssp_request(struct device *dev, uint8_t variant,
+							uint32_t passkey)
+{
+	struct hal_ev_ssp_request ev;
+
+	memset(&ev, 0, sizeof(ev));
+
+	get_device_android_addr(dev, ev.bdaddr);
+	memcpy(ev.name, dev->name, strlen(dev->name));
+	ev.class_of_dev = dev->class;
+
+	ev.pairing_variant = variant;
+	ev.passkey = passkey;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST,
+							sizeof(ev), &ev);
+}
+
+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 device *dev;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small user confirm request event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, dst);
+	DBG("%s confirm_hint %u", dst, ev->confirm_hint);
+
+	dev = get_device(&ev->addr.bdaddr, ev->addr.type);
+	if (!dev)
+		return;
+
+	update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+								false, false);
+
+	if (ev->confirm_hint)
+		send_ssp_request(dev, HAL_SSP_VARIANT_CONSENT, 0);
+	else
+		send_ssp_request(dev, HAL_SSP_VARIANT_CONFIRM, ev->value);
+}
+
+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 device *dev;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small passkey request event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, dst);
+	DBG("%s", dst);
+
+	dev = get_device(&ev->addr.bdaddr, ev->addr.type);
+	if (!dev)
+		return;
+
+	update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+								false, false);
+
+	send_ssp_request(dev, HAL_SSP_VARIANT_ENTRY, 0);
+}
+
+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 device *dev;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small passkey notify event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, dst);
+	DBG("%s entered %u", dst, ev->entered);
+
+	/* HAL seems to not support entered characters */
+	if (ev->entered)
+		return;
+
+	dev = find_device(&ev->addr.bdaddr);
+	if (!dev)
+		return;
+
+	update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+								false, false);
+
+	send_ssp_request(dev, HAL_SSP_VARIANT_NOTIF, ev->passkey);
+}
+
+static void clear_device_found(gpointer data, gpointer user_data)
+{
+	struct device *dev = data;
+
+	dev->found = false;
+}
+
+static uint8_t get_supported_discovery_type(void)
+{
+	uint8_t type = SCAN_TYPE_NONE;
+
+	if (adapter.current_settings & MGMT_SETTING_BREDR)
+		type |= SCAN_TYPE_BREDR;
+
+	if (adapter.current_settings & MGMT_SETTING_LE)
+		type |= SCAN_TYPE_LE;
+
+	return type;
+}
+
+static bool start_discovery(uint8_t type)
+{
+	struct mgmt_cp_start_discovery cp;
+
+	cp.type = get_supported_discovery_type() & type;
+
+	DBG("type=0x%x", cp.type);
+
+	if (cp.type == SCAN_TYPE_NONE)
+		return false;
+
+	if (mgmt_send(mgmt_if, MGMT_OP_START_DISCOVERY, adapter.index,
+				sizeof(cp), &cp, NULL, NULL, NULL) > 0)
+		return true;
+
+	error("Failed to start discovery");
+
+	return false;
+}
+
+/*
+ * Send discovery state change event only if it is related to dual type
+ * discovery session (triggered by start/cancel discovery commands)
+ */
+static void check_discovery_state(uint8_t new_type, uint8_t old_type)
+{
+	struct hal_ev_discovery_state_changed ev;
+
+	DBG("%u %u", new_type, old_type);
+
+	if (new_type == get_supported_discovery_type()) {
+		g_slist_foreach(bonded_devices, clear_device_found, NULL);
+		g_slist_foreach(cached_devices, clear_device_found, NULL);
+		ev.state = HAL_DISCOVERY_STATE_STARTED;
+		goto done;
+	}
+
+	if (old_type != get_supported_discovery_type())
+		return;
+
+	ev.state = HAL_DISCOVERY_STATE_STOPPED;
+
+done:
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+			HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(ev), &ev);
+}
+
+static void mgmt_discovering_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_discovering *ev = param;
+	uint8_t type;
+
+	if (length < sizeof(*ev)) {
+		error("Too small discovering event");
+		return;
+	}
+
+	DBG("type %u discovering %u", ev->type, ev->discovering);
+
+	if (!!adapter.cur_discovery_type == !!ev->discovering)
+		return;
+
+	type = ev->discovering ? ev->type : SCAN_TYPE_NONE;
+
+	check_discovery_state(type, adapter.cur_discovery_type);
+
+	adapter.cur_discovery_type = type;
+
+	if (ev->discovering) {
+		adapter.exp_discovery_type = adapter.le_scanning ?
+						SCAN_TYPE_LE : SCAN_TYPE_NONE;
+		return;
+	}
+
+	/* One shot notification about discovery stopped */
+	if (gatt_discovery_stopped_cb) {
+		gatt_discovery_stopped_cb();
+		gatt_discovery_stopped_cb = NULL;
+	}
+
+	type = adapter.exp_discovery_type;
+	adapter.exp_discovery_type = adapter.le_scanning ? SCAN_TYPE_LE :
+								SCAN_TYPE_NONE;
+
+	if (type != SCAN_TYPE_NONE)
+		start_discovery(type);
+}
+
+static void confirm_device_name_cb(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_confirm_name *rp = param;
+	struct device *dev;
+
+	DBG("Confirm name status: %s (0x%02x)", mgmt_errstr(status), status);
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size of confirm name response");
+		return;
+	}
+
+	dev = find_device(&rp->addr.bdaddr);
+	if (!dev)
+		return;
+
+	dev->confirm_id = 0;
+}
+
+static unsigned int confirm_device_name(const bdaddr_t *addr, uint8_t addr_type,
+							bool resolve_name)
+{
+	struct mgmt_cp_confirm_name cp;
+	unsigned int res;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, addr);
+	cp.addr.type = addr_type;
+
+	if (!resolve_name)
+		cp.name_known = 1;
+
+	res = mgmt_send(mgmt_if, MGMT_OP_CONFIRM_NAME, adapter.index,
+				sizeof(cp), &cp, confirm_device_name_cb,
+				NULL, NULL);
+	if (!res)
+		error("Failed to send confirm name request");
+
+	return res;
+}
+
+static int fill_hal_prop(void *buf, uint8_t type, uint16_t len,
+							const void *val)
+{
+	struct hal_property *prop = buf;
+
+	prop->type = type;
+	prop->len = len;
+
+	if (len)
+		memcpy(prop->val, val, len);
+
+	return sizeof(*prop) + len;
+}
+
+static uint8_t get_device_android_type(struct device *dev)
+{
+	if (dev->bredr && dev->le)
+		return HAL_TYPE_DUAL;
+
+	if (dev->le)
+		return HAL_TYPE_LE;
+
+	return HAL_TYPE_BREDR;
+}
+
+uint8_t bt_get_device_android_type(const bdaddr_t *addr)
+{
+	struct device *dev;
+
+	dev = get_device(addr, BDADDR_BREDR);
+
+	return get_device_android_type(dev);
+}
+
+bool bt_is_device_le(const bdaddr_t *addr)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return false;
+
+	return dev->le;
+}
+
+const bdaddr_t *bt_get_id_addr(const bdaddr_t *addr, uint8_t *type)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return NULL;
+
+	if (type)
+		*type = dev->bdaddr_type;
+
+	return &dev->bdaddr;
+}
+
+const char *bt_get_adapter_name(void)
+{
+	return adapter.name;
+}
+
+bool bt_device_is_bonded(const bdaddr_t *bdaddr)
+{
+	if (g_slist_find_custom(bonded_devices, bdaddr, device_match))
+		return true;
+
+	return false;
+}
+
+bool bt_device_set_uuids(const bdaddr_t *addr, GSList *uuids)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return false;
+
+	set_device_uuids(dev, uuids);
+
+	return true;
+}
+
+bool bt_kernel_conn_control(void)
+{
+	return kernel_conn_control;
+}
+
+bool bt_auto_connect_add(const bdaddr_t *addr)
+{
+	struct mgmt_cp_add_device cp;
+	struct device *dev;
+
+	if (!kernel_conn_control)
+		return false;
+
+	dev = find_device(addr);
+	if (!dev)
+		return false;
+
+	if (dev->bdaddr_type == BDADDR_BREDR) {
+		DBG("auto-connection feature is not available for BR/EDR");
+		return false;
+	}
+
+	if (dev->in_white_list) {
+		DBG("Device already in white list");
+		return true;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, addr);
+	cp.addr.type = dev->bdaddr_type;
+	cp.action = 0x02;
+
+	if (mgmt_send(mgmt_if, MGMT_OP_ADD_DEVICE, adapter.index, sizeof(cp),
+						&cp, NULL, NULL, NULL) > 0) {
+		dev->in_white_list = true;
+		return true;
+	}
+
+	error("Failed to add device");
+
+	return false;
+}
+
+void bt_auto_connect_remove(const bdaddr_t *addr)
+{
+	struct mgmt_cp_remove_device cp;
+	struct device *dev;
+
+	if (!kernel_conn_control)
+		return;
+
+	dev = find_device(addr);
+	if (!dev)
+		return;
+
+	if (dev->bdaddr_type == BDADDR_BREDR) {
+		DBG("auto-connection feature is not available for BR/EDR");
+		return;
+	}
+
+	if (!dev->in_white_list) {
+		DBG("Device already removed from white list");
+		return;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, addr);
+	cp.addr.type = dev->bdaddr_type;
+
+	if (mgmt_send(mgmt_if, MGMT_OP_REMOVE_DEVICE, adapter.index,
+				sizeof(cp), &cp, NULL, NULL, NULL) > 0) {
+		dev->in_white_list = false;
+		return;
+	}
+
+	error("Failed to remove device");
+}
+
+static bool match_by_value(const void *data, const void *user_data)
+{
+	return data == user_data;
+}
+
+bool bt_unpaired_register(bt_unpaired_device_cb cb)
+{
+	if (queue_find(unpaired_cb_list, match_by_value, cb))
+		return false;
+
+	return queue_push_head(unpaired_cb_list, cb);
+}
+
+void bt_unpaired_unregister(bt_unpaired_device_cb cb)
+{
+	queue_remove(unpaired_cb_list, cb);
+}
+
+bool bt_paired_register(bt_paired_device_cb cb)
+{
+	if (queue_find(paired_cb_list, match_by_value, cb))
+		return false;
+
+	return queue_push_head(paired_cb_list, cb);
+}
+
+void bt_paired_unregister(bt_paired_device_cb cb)
+{
+	queue_remove(paired_cb_list, cb);
+}
+
+bool bt_is_pairing(const bdaddr_t *addr)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return false;
+
+	return dev->pairing;
+}
+
+static bool rssi_above_threshold(int old, int new)
+{
+	/* only 8 dBm or more */
+	return abs(old - new) >= 8;
+}
+
+static void update_new_device(struct device *dev, int8_t rssi,
+						const struct eir_data *eir)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_device_found *ev = (void *) buf;
+	uint8_t android_bdaddr[6];
+	uint8_t android_type;
+	size_t size;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (adapter.cur_discovery_type)
+		dev->found = true;
+
+	size = sizeof(*ev);
+
+	get_device_android_addr(dev, android_bdaddr);
+	size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_ADDR,
+				sizeof(android_bdaddr), android_bdaddr);
+	ev->num_props++;
+
+	android_type = get_device_android_type(dev);
+	size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE,
+				sizeof(android_type), &android_type);
+	ev->num_props++;
+
+	if (eir->class)
+		dev->class = eir->class;
+
+	if (dev->class) {
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS,
+					sizeof(dev->class), &dev->class);
+		ev->num_props++;
+	}
+
+	if (rssi && rssi_above_threshold(dev->rssi, rssi))
+		dev->rssi = rssi;
+
+	if (dev->rssi) {
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI,
+						sizeof(dev->rssi), &dev->rssi);
+		ev->num_props++;
+	}
+
+	if (eir->name && strlen(eir->name)) {
+		g_free(dev->name);
+		dev->name = g_strdup(eir->name);
+	}
+
+	if (dev->name) {
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME,
+						strlen(dev->name), dev->name);
+		ev->num_props++;
+
+		/* when updating name also send stored friendly name */
+		if (dev->friendly_name) {
+			size += fill_hal_prop(buf + size,
+						HAL_PROP_DEVICE_FRIENDLY_NAME,
+						strlen(dev->friendly_name),
+						dev->friendly_name);
+			ev->num_props++;
+		}
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_DEVICE_FOUND,
+								size, buf);
+}
+
+static void update_device(struct device *dev, int8_t rssi,
+						const struct eir_data *eir)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_remote_device_props *ev = (void *) buf;
+	uint8_t old_type, new_type;
+	size_t size;
+
+	memset(buf, 0, sizeof(buf));
+
+	size = sizeof(*ev);
+
+	ev->status = HAL_STATUS_SUCCESS;
+	get_device_android_addr(dev, ev->bdaddr);
+
+	old_type = get_device_android_type(dev);
+
+	new_type = get_device_android_type(dev);
+
+	if (old_type != new_type) {
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE,
+						sizeof(new_type), &new_type);
+		ev->num_props++;
+	}
+
+	if (eir->class && dev->class != eir->class) {
+		dev->class = eir->class;
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS,
+					sizeof(dev->class), &dev->class);
+		ev->num_props++;
+	}
+
+	if (rssi && rssi_above_threshold(dev->rssi, rssi)) {
+		dev->rssi = rssi;
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI,
+						sizeof(dev->rssi), &dev->rssi);
+		ev->num_props++;
+	}
+
+	if (eir->name && strlen(eir->name) && strcmp(dev->name, eir->name)) {
+		g_free(dev->name);
+		dev->name = g_strdup(eir->name);
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME,
+						strlen(dev->name), dev->name);
+		ev->num_props++;
+
+		/* when updating name also send stored friendly name */
+		if (dev->friendly_name) {
+			size += fill_hal_prop(buf + size,
+						HAL_PROP_DEVICE_FRIENDLY_NAME,
+						strlen(dev->friendly_name),
+						dev->friendly_name);
+			ev->num_props++;
+		}
+	}
+
+	if (ev->num_props)
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+					HAL_EV_REMOTE_DEVICE_PROPS, size, buf);
+}
+
+static bool is_new_device(const struct device *dev, unsigned int flags,
+							uint8_t bdaddr_type)
+{
+	if (dev->found)
+		return false;
+
+	if (dev->bredr_paired || dev->le_paired)
+		return false;
+
+	if (bdaddr_type != BDADDR_BREDR &&
+				!(flags & (EIR_LIM_DISC | EIR_GEN_DISC)))
+		return false;
+
+	return true;
+}
+
+static void update_found_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+					int8_t rssi, bool confirm,
+					bool connectable,
+					const uint8_t *data, uint8_t data_len)
+{
+	struct eir_data eir;
+	struct device *dev;
+
+	memset(&eir, 0, sizeof(eir));
+
+	eir_parse(&eir, data, data_len);
+
+	dev = get_device(bdaddr, bdaddr_type);
+
+	if (bdaddr_type == BDADDR_BREDR) {
+		dev->bredr = true;
+		dev->bredr_seen = time(NULL);
+	} else {
+		dev->le = true;
+		dev->bdaddr_type = bdaddr_type;
+		dev->le_seen = time(NULL);
+	}
+
+	/*
+	 * Device found event needs to be send also for known device if this is
+	 * new discovery session. Otherwise framework will ignore it.
+	 */
+	if (is_new_device(dev, eir.flags, bdaddr_type))
+		update_new_device(dev, rssi, &eir);
+	else
+		update_device(dev, rssi, &eir);
+
+	eir_data_free(&eir);
+
+	/* Notify Gatt if its registered for LE events */
+	if (bdaddr_type != BDADDR_BREDR && gatt_device_found_cb) {
+		const bdaddr_t *addr;
+
+		/*
+		 * If RPA is set it means that IRK was received and ID address
+		 * is being used. Android Framework is still using old RPA and
+		 * it needs to be used also in GATT notifications. Also GATT
+		 * HAL implementation is using RPA for devices matching.
+		 */
+		if (bacmp(&dev->rpa, BDADDR_ANY))
+			addr = &dev->rpa;
+		else
+			addr = &dev->bdaddr;
+
+		gatt_device_found_cb(addr, rssi, data_len, data, connectable,
+								dev->le_bonded);
+	}
+
+	if (!dev->bredr_paired && !dev->le_paired)
+		cache_device(dev);
+
+	if (confirm) {
+		char addr[18];
+		bool resolve_name = true;
+
+		ba2str(bdaddr, addr);
+
+		/*
+		 * Don't need to confirm name if we have it already in cache
+		 * Just check if device name is different than bdaddr
+		 */
+		if (g_strcmp0(dev->name, addr)) {
+			get_device_name(dev);
+			resolve_name = false;
+		}
+
+		info("Device %s needs name confirmation (resolve_name=%d)",
+							addr, resolve_name);
+		dev->confirm_id = confirm_device_name(bdaddr, bdaddr_type,
+								resolve_name);
+	}
+}
+
+static void mgmt_device_found_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_device_found *ev = param;
+	const uint8_t *eir;
+	uint16_t eir_len;
+	uint32_t flags;
+	bool confirm_name;
+	bool connectable;
+	char addr[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too short device found event (%u bytes)", length);
+		return;
+	}
+
+	eir_len = le16_to_cpu(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 = le32_to_cpu(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;
+	connectable = !(flags & MGMT_DEV_FOUND_NOT_CONNECTABLE);
+
+	update_found_device(&ev->addr.bdaddr, ev->addr.type, ev->rssi,
+				confirm_name, connectable, eir, eir_len);
+}
+
+static void mgmt_device_connected_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_device_connected *ev = param;
+	struct hal_ev_acl_state_changed hal_ev;
+	struct device *dev;
+	char addr[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too short device connected event (%u bytes)", length);
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+	DBG("%s type %u", addr, ev->addr.type);
+
+	update_found_device(&ev->addr.bdaddr, ev->addr.type, 0, false, false,
+					&ev->eir[0], le16_to_cpu(ev->eir_len));
+
+	hal_ev.status = HAL_STATUS_SUCCESS;
+	hal_ev.state = HAL_ACL_STATE_CONNECTED;
+
+	dev = find_device(&ev->addr.bdaddr);
+	if (!dev)
+		return;
+
+	dev->connected = true;
+
+	get_device_android_addr(dev, hal_ev.bdaddr);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+			HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev);
+}
+
+static bool device_is_paired(struct device *dev, uint8_t addr_type)
+{
+	if (addr_type == BDADDR_BREDR)
+		return dev->bredr_paired;
+
+	return dev->le_paired;
+}
+
+static bool device_is_bonded(struct device *dev)
+{
+	return dev->bredr_bonded || dev->le_bonded;
+}
+
+static void mgmt_device_disconnected_event(uint16_t index, uint16_t length,
+							const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_device_disconnected *ev = param;
+	struct hal_ev_acl_state_changed hal_ev;
+	struct device *dev;
+	uint8_t type = ev->addr.type;
+
+	if (length < sizeof(*ev)) {
+		error("Too short device disconnected event (%u bytes)", length);
+		return;
+	}
+
+	dev = find_device(&ev->addr.bdaddr);
+	if (!dev)
+		return;
+
+	hal_ev.status = HAL_STATUS_SUCCESS;
+	hal_ev.state = HAL_ACL_STATE_DISCONNECTED;
+	get_device_android_addr(dev, hal_ev.bdaddr);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+			HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev);
+
+	if (device_is_paired(dev, type) && !device_is_bonded(dev))
+		update_device_state(dev, type, HAL_STATUS_SUCCESS, false,
+								false, false);
+
+	dev->connected = false;
+}
+
+static uint8_t status_mgmt2hal(uint8_t mgmt)
+{
+	switch (mgmt) {
+	case MGMT_STATUS_SUCCESS:
+		return HAL_STATUS_SUCCESS;
+	case MGMT_STATUS_NO_RESOURCES:
+		return HAL_STATUS_NOMEM;
+	case MGMT_STATUS_BUSY:
+		return HAL_STATUS_BUSY;
+	case MGMT_STATUS_NOT_SUPPORTED:
+		return HAL_STATUS_UNSUPPORTED;
+	case MGMT_STATUS_INVALID_PARAMS:
+		return HAL_STATUS_INVALID;
+	case MGMT_STATUS_AUTH_FAILED:
+		return HAL_STATUS_AUTH_FAILURE;
+	case MGMT_STATUS_NOT_CONNECTED:
+		return HAL_STATUS_REMOTE_DEVICE_DOWN;
+	default:
+		return HAL_STATUS_FAILED;
+	}
+}
+
+static void mgmt_connect_failed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_connect_failed *ev = param;
+	struct device *dev;
+
+	if (length < sizeof(*ev)) {
+		error("Too short connect failed event (%u bytes)", length);
+		return;
+	}
+
+	DBG("");
+
+	dev = find_device(&ev->addr.bdaddr);
+	if (!dev)
+		return;
+
+	/*
+	 * In case security mode 3 pairing we will get connect failed event
+	 * in case e.g wrong PIN code entered. Let's check if device is
+	 * bonding, if so update bond state
+	 */
+
+	if (!dev->pairing)
+		return;
+
+	update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status),
+							false, false, false);
+}
+
+static void mgmt_auth_failed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_auth_failed *ev = param;
+	struct device *dev;
+
+	if (length < sizeof(*ev)) {
+		error("Too small auth failed mgmt event (%u bytes)", length);
+		return;
+	}
+
+	DBG("");
+
+	dev = find_device(&ev->addr.bdaddr);
+	if (!dev)
+		return;
+
+	if (!dev->pairing)
+		return;
+
+	update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status),
+							false, false, false);
+}
+
+static void mgmt_device_unpaired_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_device_unpaired *ev = param;
+	struct device *dev;
+
+	if (length < sizeof(*ev)) {
+		error("Too small device unpaired event (%u bytes)", length);
+		return;
+	}
+
+	DBG("");
+
+	/* TODO should device be disconnected ? */
+
+	dev = find_device(&ev->addr.bdaddr);
+	if (!dev)
+		return;
+
+	update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, false,
+								false, false);
+
+	/* Unpaired device is removed from the white list */
+	dev->in_white_list = false;
+}
+
+static void store_ltk(const bdaddr_t *dst, uint8_t bdaddr_type, bool master,
+			const uint8_t *key, uint8_t key_type, uint8_t enc_size,
+			uint16_t ediv, uint64_t rand)
+{
+	const char *key_s, *keytype_s, *encsize_s, *ediv_s, *rand_s;
+	GKeyFile *key_file;
+	char key_str[33];
+	gsize length = 0;
+	char addr[18];
+	char *data;
+	int i;
+
+	key_file = g_key_file_new();
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	ba2str(dst, addr);
+
+	key_s = master ? "LongTermKey" : "SlaveLongTermKey";
+	keytype_s = master ? "LongTermKeyType" : "SlaveLongTermKeyType";
+	encsize_s = master ? "LongTermKeyEncSize" : "SlaveLongTermKeyEncSize";
+	ediv_s = master ? "LongTermKeyEDiv" : "SlaveLongTermKeyEDiv";
+	rand_s = master ? "LongTermKeyRand" : "SlaveLongTermKeyRand";
+
+	for (i = 0; i < 16; i++)
+		sprintf(key_str + (i * 2), "%2.2X", key[i]);
+
+	g_key_file_set_string(key_file, addr, key_s, key_str);
+
+	g_key_file_set_integer(key_file, addr, keytype_s, key_type);
+
+	g_key_file_set_integer(key_file, addr, encsize_s, enc_size);
+
+	g_key_file_set_integer(key_file, addr, ediv_s, ediv);
+
+	g_key_file_set_uint64(key_file, addr, rand_s, rand);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+static void new_long_term_key_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_long_term_key *ev = param;
+	struct device *dev;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small long term key event (%u bytes)", length);
+		return;
+	}
+
+	ba2str(&ev->key.addr.bdaddr, dst);
+
+	DBG("new LTK for %s type %u enc_size %u store_hint %u",
+			dst, ev->key.type, ev->key.enc_size, ev->store_hint);
+
+	dev = find_device(&ev->key.addr.bdaddr);
+	if (!dev)
+		return;
+
+	update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+							true, !!ev->store_hint);
+
+	if (ev->store_hint) {
+		const struct mgmt_ltk_info *key = &ev->key;
+		uint16_t ediv;
+		uint64_t rand;
+
+		ediv = le16_to_cpu(key->ediv);
+		rand = le64_to_cpu(key->rand);
+
+		store_ltk(&key->addr.bdaddr, key->addr.type, key->master,
+				key->val, key->type, key->enc_size, ediv, rand);
+	}
+
+	/* TODO browse services here? */
+}
+
+static void store_csrk(struct device *dev)
+{
+	GKeyFile *key_file;
+	char key_str[33];
+	char addr[18];
+	int i;
+	gsize length = 0;
+	char *data;
+
+	ba2str(&dev->bdaddr, addr);
+
+	key_file = g_key_file_new();
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	if (dev->valid_local_csrk) {
+		for (i = 0; i < 16; i++)
+			sprintf(key_str + (i * 2), "%2.2X",
+							dev->local_csrk[i]);
+
+		g_key_file_set_string(key_file, addr, "LocalCSRK", key_str);
+
+		g_key_file_set_boolean(key_file, addr, "LocalCSRKAuthenticated",
+							dev->local_csrk_auth);
+	}
+
+	if (dev->valid_remote_csrk) {
+		for (i = 0; i < 16; i++)
+			sprintf(key_str + (i * 2), "%2.2X",
+							dev->remote_csrk[i]);
+
+		g_key_file_set_string(key_file, addr, "RemoteCSRK", key_str);
+
+		g_key_file_set_boolean(key_file, addr,
+						"RemoteCSRKAuthenticated",
+						dev->remote_csrk_auth);
+	}
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+static void new_csrk_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_csrk *ev = param;
+	struct device *dev;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small csrk event (%u bytes)", length);
+		return;
+	}
+
+	ba2str(&ev->key.addr.bdaddr, dst);
+	dev = get_device(&ev->key.addr.bdaddr, ev->key.addr.type);
+	if (!dev)
+		return;
+
+	switch (ev->key.type) {
+	case 0x00:
+	case 0x02:
+		memcpy(dev->local_csrk, ev->key.val, 16);
+		dev->local_sign_cnt = 0;
+		dev->valid_local_csrk = true;
+		dev->local_csrk_auth = ev->key.type == 0x02;
+		break;
+	case 0x01:
+	case 0x03:
+		memcpy(dev->remote_csrk, ev->key.val, 16);
+		dev->remote_sign_cnt = 0;
+		dev->valid_remote_csrk = true;
+		dev->remote_csrk_auth = ev->key.type == 0x03;
+		break;
+	default:
+		error("Unknown CSRK key type 02%02x", ev->key.type);
+		return;
+	}
+
+	update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+							true, !!ev->store_hint);
+
+	if (ev->store_hint)
+		store_csrk(dev);
+}
+
+static void store_irk(struct device *dev, const uint8_t *val)
+{
+	GKeyFile *key_file;
+	char key_str[33];
+	char addr[18];
+	int i;
+	gsize length = 0;
+	char *data;
+
+	ba2str(&dev->bdaddr, addr);
+
+	key_file = g_key_file_new();
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	for (i = 0; i < 16; i++)
+		sprintf(key_str + (i * 2), "%2.2X", val[i]);
+
+	g_key_file_set_string(key_file, addr, "IdentityResolvingKey", key_str);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+static void new_irk_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_irk *ev = param;
+	const struct mgmt_addr_info *addr = &ev->key.addr;
+	struct device *dev;
+	char dst[18], rpa[18];
+
+	if (length < sizeof(*ev)) {
+		error("To small New Irk Event (%u bytes)", length);
+		return;
+	}
+
+	ba2str(&ev->key.addr.bdaddr, dst);
+	ba2str(&ev->rpa, rpa);
+
+	DBG("new IRK for %s, RPA %s", dst, rpa);
+
+	if (!bacmp(&ev->rpa, BDADDR_ANY)) {
+		dev = get_device(&addr->bdaddr, addr->type);
+		if (!dev)
+			return;
+	} else {
+		dev = find_device(&addr->bdaddr);
+
+		if (dev && dev->bredr_paired) {
+			bacpy(&dev->rpa, &addr->bdaddr);
+			dev->rpa_type = addr->type;
+
+			/* TODO merge properties ie. UUIDs */
+		} else {
+			dev = find_device(&ev->rpa);
+			if (!dev)
+				return;
+
+			/* don't leave garbage in cache file */
+			remove_device_info(dev, CACHE_FILE);
+
+			/*
+			 * RPA resolution is transparent for Android Framework
+			 * ie. device is still access by RPA so it need to be
+			 * keep. After bluetoothd restart device is advertised
+			 * to Android with IDA and RPA is not set.
+			 */
+			bacpy(&dev->rpa, &dev->bdaddr);
+			dev->rpa_type = dev->bdaddr_type;
+
+			bacpy(&dev->bdaddr, &addr->bdaddr);
+			dev->bdaddr_type = addr->type;
+		}
+	}
+
+	update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+							true, !!ev->store_hint);
+
+	if (ev->store_hint)
+		store_irk(dev, ev->key.val);
+}
+
+static void register_mgmt_handlers(void)
+{
+	mgmt_register(mgmt_if, MGMT_EV_NEW_SETTINGS, adapter.index,
+					new_settings_callback, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_CLASS_OF_DEV_CHANGED, adapter.index,
+				mgmt_dev_class_changed_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_LOCAL_NAME_CHANGED, adapter.index,
+				mgmt_local_name_changed_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_NEW_LINK_KEY, adapter.index,
+					new_link_key_callback, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_PIN_CODE_REQUEST, adapter.index,
+					pin_code_request_callback, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_USER_CONFIRM_REQUEST, adapter.index,
+				user_confirm_request_callback, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_USER_PASSKEY_REQUEST, adapter.index,
+				user_passkey_request_callback, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_PASSKEY_NOTIFY, adapter.index,
+				user_passkey_notify_callback, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_DISCOVERING, adapter.index,
+					mgmt_discovering_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_DEVICE_FOUND, adapter.index,
+					mgmt_device_found_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_DEVICE_CONNECTED, adapter.index,
+				mgmt_device_connected_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_DEVICE_DISCONNECTED, adapter.index,
+				mgmt_device_disconnected_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_CONNECT_FAILED, adapter.index,
+					mgmt_connect_failed_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_AUTH_FAILED, adapter.index,
+					mgmt_auth_failed_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_DEVICE_UNPAIRED, adapter.index,
+				mgmt_device_unpaired_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_NEW_LONG_TERM_KEY, adapter.index,
+					new_long_term_key_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_NEW_CSRK, adapter.index,
+						new_csrk_callback, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_NEW_IRK, adapter.index, new_irk_callback,
+								NULL, NULL);
+}
+
+static void load_link_keys_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	bt_bluetooth_ready cb = user_data;
+	int err;
+
+	if (status) {
+		error("Failed to load link keys for index %u: %s (0x%02x)",
+				adapter.index, mgmt_errstr(status), status);
+		err = -EIO;
+		goto failed;
+	}
+
+	DBG("status %u", status);
+
+	cb(0, &adapter.bdaddr);
+	return;
+
+failed:
+	cb(err, NULL);
+}
+
+static void load_link_keys(GSList *keys, bt_bluetooth_ready cb)
+{
+	struct mgmt_cp_load_link_keys *cp;
+	struct mgmt_link_key_info *key;
+	size_t key_count, cp_size;
+	unsigned int id;
+
+	key_count = g_slist_length(keys);
+
+	DBG("keys %zu ", key_count);
+
+	cp_size = sizeof(*cp) + (key_count * sizeof(*key));
+
+	cp = g_malloc0(cp_size);
+
+	/*
+	 * Even if the list of stored keys is empty, it is important to
+	 * load an empty list into the kernel. That way it is ensured
+	 * that no old keys from a previous daemon are present.
+	 */
+	cp->key_count = cpu_to_le16(key_count);
+
+	for (key = cp->keys; keys != NULL; keys = g_slist_next(keys), key++)
+		memcpy(key, keys->data, sizeof(*key));
+
+	id = mgmt_send(mgmt_if, MGMT_OP_LOAD_LINK_KEYS, adapter.index,
+			cp_size, cp, load_link_keys_complete, cb, NULL);
+
+	g_free(cp);
+
+	if (id == 0) {
+		error("Failed to load link keys");
+		cb(-EIO, NULL);
+	}
+}
+
+static void load_ltk_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status == MGMT_STATUS_SUCCESS)
+		return;
+
+	info("Failed to load LTKs: %s (0x%02x)", mgmt_errstr(status), status);
+}
+
+static void load_ltks(GSList *ltks)
+{
+	struct mgmt_cp_load_long_term_keys *cp;
+	struct mgmt_ltk_info *ltk;
+	size_t ltk_count, cp_size;
+	GSList *l;
+
+	ltk_count = g_slist_length(ltks);
+
+	DBG("ltks %zu", ltk_count);
+
+	cp_size = sizeof(*cp) + (ltk_count * sizeof(*ltk));
+
+	cp = g_malloc0(cp_size);
+
+	/*
+	 * Even if the list of stored keys is empty, it is important to load
+	 * an empty list into the kernel. That way it is ensured that no old
+	 * keys from a previous daemon are present.
+	 */
+	cp->key_count = cpu_to_le16(ltk_count);
+
+	for (l = ltks, ltk = cp->keys; l != NULL; l = g_slist_next(l), ltk++)
+		memcpy(ltk, l->data, sizeof(*ltk));
+
+	if (mgmt_send(mgmt_if, MGMT_OP_LOAD_LONG_TERM_KEYS, adapter.index,
+			cp_size, cp, load_ltk_complete, NULL, NULL) == 0)
+		error("Failed to load LTKs");
+
+	g_free(cp);
+}
+
+static void load_irk_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status == MGMT_STATUS_SUCCESS)
+		return;
+
+	info("Failed to load IRKs: %s (0x%02x)", mgmt_errstr(status), status);
+}
+
+static void load_irks(GSList *irks)
+{
+	struct mgmt_cp_load_irks *cp;
+	struct mgmt_irk_info *irk;
+	size_t irk_count, cp_size;
+	GSList *l;
+
+	irk_count = g_slist_length(irks);
+
+	DBG("irks %zu", irk_count);
+
+	cp_size = sizeof(*cp) + (irk_count * sizeof(*irk));
+
+	cp = g_malloc0(cp_size);
+
+	cp->irk_count = cpu_to_le16(irk_count);
+
+	for (l = irks, irk = cp->irks; l != NULL; l = g_slist_next(l), irk++)
+		memcpy(irk, irks->data, sizeof(*irk));
+
+	if (mgmt_send(mgmt_if, MGMT_OP_LOAD_IRKS, adapter.index, cp_size, cp,
+					load_irk_complete, NULL, NULL) == 0)
+		error("Failed to load IRKs");
+
+	g_free(cp);
+}
+
+static uint8_t get_adapter_uuids(void)
+{
+	struct hal_ev_adapter_props_changed *ev;
+	GSList *list = adapter.uuids;
+	size_t uuid_count = g_slist_length(list);
+	size_t len = uuid_count * sizeof(uint128_t);
+	uint8_t buf[BASELEN_PROP_CHANGED + len];
+	uint8_t *p;
+
+	memset(buf, 0, sizeof(buf));
+	ev = (void *) buf;
+
+	ev->num_props = 1;
+	ev->status = HAL_STATUS_SUCCESS;
+
+	ev->props[0].type = HAL_PROP_ADAPTER_UUIDS;
+	ev->props[0].len = len;
+	p = ev->props->val;
+
+	for (; list; list = g_slist_next(list)) {
+		uuid_t *uuid = list->data;
+
+		memcpy(p, &uuid->value.uuid128, sizeof(uint128_t));
+
+		p += sizeof(uint128_t);
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void remove_uuid_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to remove UUID: %s (0x%02x)", mgmt_errstr(status),
+									status);
+		return;
+	}
+
+	mgmt_dev_class_changed_event(adapter.index, length, param, NULL);
+
+	get_adapter_uuids();
+}
+
+static void remove_uuid(uuid_t *uuid)
+{
+	uint128_t uint128;
+	struct mgmt_cp_remove_uuid cp;
+
+	ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128);
+	htob128(&uint128, (uint128_t *) cp.uuid);
+
+	mgmt_send(mgmt_if, MGMT_OP_REMOVE_UUID, adapter.index, sizeof(cp), &cp,
+					remove_uuid_complete, NULL, NULL);
+}
+
+static void add_uuid_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to add UUID: %s (0x%02x)", mgmt_errstr(status),
+									status);
+		return;
+	}
+
+	mgmt_dev_class_changed_event(adapter.index, length, param, NULL);
+
+	get_adapter_uuids();
+}
+
+static void add_uuid(uint8_t svc_hint, uuid_t *uuid)
+{
+	uint128_t uint128;
+	struct mgmt_cp_add_uuid cp;
+
+	ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128);
+	htob128(&uint128, (uint128_t *) cp.uuid);
+
+	cp.svc_hint = svc_hint;
+
+	mgmt_send(mgmt_if, MGMT_OP_ADD_UUID, adapter.index, sizeof(cp), &cp,
+						add_uuid_complete, NULL, NULL);
+}
+
+int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint)
+{
+	uuid_t *uuid;
+
+	uuid = sdp_uuid_to_uuid128(&rec->svclass);
+
+	if (g_slist_find_custom(adapter.uuids, uuid, sdp_uuid_cmp)) {
+		char uuid_str[32];
+
+		sdp_uuid2strn(uuid, uuid_str, sizeof(uuid_str));
+		DBG("UUID %s already added", uuid_str);
+
+		bt_free(uuid);
+		return -EALREADY;
+	}
+
+	adapter.uuids = g_slist_prepend(adapter.uuids, uuid);
+
+	add_uuid(svc_hint, uuid);
+
+	return add_record_to_server(&adapter.bdaddr, rec);
+}
+
+void bt_adapter_remove_record(uint32_t handle)
+{
+	sdp_record_t *rec;
+	GSList *uuid_found;
+
+	rec = sdp_record_find(handle);
+	if (!rec)
+		return;
+
+	uuid_found = g_slist_find_custom(adapter.uuids, &rec->svclass,
+								sdp_uuid_cmp);
+	if (uuid_found) {
+		uuid_t *uuid = uuid_found->data;
+
+		remove_uuid(uuid);
+
+		adapter.uuids = g_slist_remove(adapter.uuids, uuid);
+
+		free(uuid);
+	}
+
+	remove_record_from_server(handle);
+}
+
+static void set_mode_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to set mode: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	/*
+	 * The parameters are identical and also the task that is
+	 * required in both cases. So it is safe to just call the
+	 * event handling functions here.
+	 */
+	new_settings_callback(adapter.index, length, param, NULL);
+}
+
+static bool set_mode(uint16_t opcode, uint8_t mode)
+{
+	struct mgmt_mode cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.val = mode;
+
+	DBG("opcode=0x%x mode=0x%x", opcode, mode);
+
+	if (mgmt_send(mgmt_if, opcode, adapter.index, sizeof(cp), &cp,
+					set_mode_complete, NULL, NULL) > 0)
+		return true;
+
+	error("Failed to set mode");
+
+	return false;
+}
+
+static void set_io_capability(void)
+{
+	struct mgmt_cp_set_io_capability cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.io_capability = DEFAULT_IO_CAPABILITY;
+
+	if (mgmt_send(mgmt_if, MGMT_OP_SET_IO_CAPABILITY, adapter.index,
+				sizeof(cp), &cp, NULL, NULL, NULL) == 0)
+		error("Failed to set IO capability");
+}
+
+static void set_device_id(void)
+{
+	struct mgmt_cp_set_device_id cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.source = cpu_to_le16(bt_config_get_pnp_source());
+	cp.vendor = cpu_to_le16(bt_config_get_pnp_vendor());
+	cp.product = cpu_to_le16(bt_config_get_pnp_product());
+	cp.version = cpu_to_le16(bt_config_get_pnp_version());
+
+	if (mgmt_send(mgmt_if, MGMT_OP_SET_DEVICE_ID, adapter.index,
+				sizeof(cp), &cp, NULL, NULL, NULL) == 0)
+		error("Failed to set device id");
+
+	register_device_id(bt_config_get_pnp_source(),
+						bt_config_get_pnp_vendor(),
+						bt_config_get_pnp_product(),
+						bt_config_get_pnp_version());
+
+	bt_adapter_add_record(sdp_record_find(0x10000), 0x00);
+}
+
+static void set_adapter_name_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_cp_set_local_name *rp = param;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to set name: %s (0x%02x)", mgmt_errstr(status),
+									status);
+		return;
+	}
+
+	adapter_set_name(rp->name);
+}
+
+static uint8_t set_adapter_name(const uint8_t *name, uint16_t len)
+{
+	struct mgmt_cp_set_local_name cp;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(cp.name, name, len);
+
+	if (mgmt_send(mgmt_if, MGMT_OP_SET_LOCAL_NAME, adapter.index,
+				sizeof(cp), &cp, set_adapter_name_complete,
+				NULL, NULL) > 0)
+		return HAL_STATUS_SUCCESS;
+
+	error("Failed to set name");
+
+	return HAL_STATUS_FAILED;
+}
+
+static uint8_t set_adapter_discoverable_timeout(const void *buf, uint16_t len)
+{
+	const uint32_t *timeout = buf;
+
+	if (len != sizeof(*timeout)) {
+		error("Invalid set disc timeout size (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return HAL_STATUS_FAILED;
+	}
+
+	/*
+	 * Android handles discoverable timeout in Settings app.
+	 * There is no need to use kernel feature for that.
+	 * Just need to store this value here
+	 */
+
+	memcpy(&adapter.discoverable_timeout, timeout, sizeof(uint32_t));
+
+	store_adapter_config();
+
+	send_adapter_property(HAL_PROP_ADAPTER_DISC_TIMEOUT,
+					sizeof(adapter.discoverable_timeout),
+					&adapter.discoverable_timeout);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void clear_uuids(void)
+{
+	struct mgmt_cp_remove_uuid cp;
+
+	memset(&cp, 0, sizeof(cp));
+
+	mgmt_send(mgmt_if, MGMT_OP_REMOVE_UUID, adapter.index, sizeof(cp),
+							&cp, NULL, NULL, NULL);
+}
+
+static struct device *create_device_from_info(GKeyFile *key_file,
+							const char *peer)
+{
+	struct device *dev;
+	uint8_t type;
+	bdaddr_t bdaddr;
+	char **uuids;
+	char *str;
+
+	/* BREDR if not present */
+	type = g_key_file_get_integer(key_file, peer, "AddressType", NULL);
+
+	str2ba(peer, &bdaddr);
+	dev = create_device(&bdaddr, type);
+
+	if (type != BDADDR_BREDR)
+		dev->bredr = g_key_file_get_boolean(key_file, peer, "BREDR",
+									NULL);
+
+	str = g_key_file_get_string(key_file, peer, "LocalCSRK", NULL);
+	if (str) {
+		int i;
+
+		dev->valid_local_csrk = true;
+		for (i = 0; i < 16; i++)
+			sscanf(str + (i * 2), "%02hhX", &dev->local_csrk[i]);
+
+		g_free(str);
+
+		dev->local_sign_cnt = g_key_file_get_integer(key_file, peer,
+						"LocalCSRKSignCounter", NULL);
+
+		dev->local_csrk_auth = g_key_file_get_boolean(key_file, peer,
+						"LocalCSRKAuthenticated", NULL);
+	}
+
+	str = g_key_file_get_string(key_file, peer, "RemoteCSRK", NULL);
+	if (str) {
+		int i;
+
+		dev->valid_remote_csrk = true;
+		for (i = 0; i < 16; i++)
+			sscanf(str + (i * 2), "%02hhX", &dev->remote_csrk[i]);
+
+		g_free(str);
+
+		dev->remote_sign_cnt = g_key_file_get_integer(key_file, peer,
+						"RemoteCSRKSignCounter", NULL);
+
+		dev->remote_csrk_auth = g_key_file_get_boolean(key_file, peer,
+						"RemoteCSRKAuthenticated",
+						NULL);
+	}
+
+	str = g_key_file_get_string(key_file, peer, "GattCCC", NULL);
+	if (str) {
+		dev->gatt_ccc = atoi(str);
+		g_free(str);
+	}
+
+	str = g_key_file_get_string(key_file, peer, "Name", NULL);
+	if (str) {
+		g_free(dev->name);
+		dev->name = str;
+	}
+
+	str = g_key_file_get_string(key_file, peer, "FriendlyName", NULL);
+	if (str) {
+		g_free(dev->friendly_name);
+		dev->friendly_name = str;
+	}
+
+	dev->class = g_key_file_get_integer(key_file, peer, "Class", NULL);
+
+	if (dev->bredr)
+		dev->bredr_seen = g_key_file_get_integer(key_file, peer,
+								"Timestamp",
+								NULL);
+	else
+		dev->le_seen = g_key_file_get_integer(key_file, peer,
+							"Timestamp", NULL);
+
+	uuids = g_key_file_get_string_list(key_file, peer, "Services", NULL,
+									NULL);
+	if (uuids) {
+		char **uuid;
+
+		for (uuid = uuids; *uuid; uuid++) {
+			uint8_t *u = g_malloc0(16);
+			int i;
+
+			for (i = 0; i < 16; i++)
+				sscanf((*uuid) + (i * 2), "%02hhX", &u[i]);
+
+			dev->uuids = g_slist_append(dev->uuids, u);
+		}
+
+		g_strfreev(uuids);
+	}
+
+	return dev;
+}
+
+static struct mgmt_link_key_info *get_key_info(GKeyFile *key_file,
+							const char *peer)
+{
+	struct mgmt_link_key_info *info = NULL;
+	char *str;
+	unsigned int i;
+
+	str = g_key_file_get_string(key_file, peer, "LinkKey", NULL);
+	if (!str || strlen(str) != 32)
+		goto failed;
+
+	info = g_new0(struct mgmt_link_key_info, 1);
+
+	str2ba(peer, &info->addr.bdaddr);
+
+	for (i = 0; i < sizeof(info->val); i++)
+		sscanf(str + (i * 2), "%02hhX", &info->val[i]);
+
+	info->type = g_key_file_get_integer(key_file, peer, "LinkKeyType",
+									NULL);
+	info->pin_len = g_key_file_get_integer(key_file, peer,
+						"LinkKeyPinLength", NULL);
+
+failed:
+	g_free(str);
+
+	return info;
+}
+
+static struct mgmt_ltk_info *get_ltk_info(GKeyFile *key_file, const char *peer,
+								bool master)
+{
+	const char *key_s, *keytype_s, *encsize_s, *ediv_s, *rand_s;
+	struct mgmt_ltk_info *info = NULL;
+	char *key;
+	unsigned int i;
+
+	key_s = master ? "LongTermKey" : "SlaveLongTermKey";
+	keytype_s = master ? "LongTermKeyType" : "SlaveLongTermKeyType";
+	encsize_s = master ? "LongTermKeyEncSize" : "SlaveLongTermKeyEncSize";
+	ediv_s = master ? "LongTermKeyEDiv" : "SlaveLongTermKeyEDiv";
+	rand_s = master ? "LongTermKeyRand" : "SlaveLongTermKeyRand";
+
+	key = g_key_file_get_string(key_file, peer, key_s, NULL);
+	if (!key || strlen(key) != 32)
+		goto failed;
+
+	info = g_new0(struct mgmt_ltk_info, 1);
+
+	str2ba(peer, &info->addr.bdaddr);
+
+	info->addr.type = g_key_file_get_integer(key_file, peer, "AddressType",
+									NULL);
+
+	for (i = 0; i < sizeof(info->val); i++)
+		sscanf(key + (i * 2), "%02hhX", &info->val[i]);
+
+	info->type = g_key_file_get_integer(key_file, peer, keytype_s, NULL);
+
+	info->enc_size = g_key_file_get_integer(key_file, peer, encsize_s,
+									NULL);
+
+	info->rand = g_key_file_get_uint64(key_file, peer, rand_s, NULL);
+	info->rand = cpu_to_le64(info->rand);
+
+	info->ediv = g_key_file_get_integer(key_file, peer, ediv_s, NULL);
+	info->ediv = cpu_to_le16(info->ediv);
+
+	info->master = master;
+
+failed:
+	g_free(key);
+
+	return info;
+}
+
+static struct mgmt_irk_info *get_irk_info(GKeyFile *key_file, const char *peer)
+{
+	struct mgmt_irk_info *info = NULL;
+	unsigned int i;
+	char *str;
+
+	str = g_key_file_get_string(key_file, peer, "IdentityResolvingKey",
+									NULL);
+	if (!str || strlen(str) != 32)
+		goto failed;
+
+	info = g_new0(struct mgmt_irk_info, 1);
+
+	str2ba(peer, &info->addr.bdaddr);
+
+	info->addr.type = g_key_file_get_integer(key_file, peer, "AddressType",
+									NULL);
+
+	for (i = 0; i < sizeof(info->val); i++)
+		sscanf(str + (i * 2), "%02hhX", &info->val[i]);
+
+failed:
+	g_free(str);
+
+	return info;
+}
+
+static time_t device_timestamp(const struct device *dev)
+{
+	if (dev->bredr && dev->le) {
+		if (dev->le_seen > dev->bredr_seen)
+			return dev->le_seen;
+
+		return dev->bredr_seen;
+	}
+
+	if (dev->bredr)
+		return dev->bredr_seen;
+
+	return dev->le_seen;
+}
+
+static int device_timestamp_cmp(gconstpointer  a, gconstpointer  b)
+{
+	const struct device *deva = a;
+	const struct device *devb = b;
+
+	return device_timestamp(deva) < device_timestamp(devb);
+}
+
+static void load_devices_cache(void)
+{
+	GKeyFile *key_file;
+	gchar **devs;
+	gsize len = 0;
+	unsigned int i;
+
+	key_file = g_key_file_new();
+
+	g_key_file_load_from_file(key_file, CACHE_FILE, 0, NULL);
+
+	devs = g_key_file_get_groups(key_file, &len);
+
+	for (i = 0; i < len; i++) {
+		struct device *dev;
+
+		dev = create_device_from_info(key_file, devs[i]);
+		cached_devices = g_slist_prepend(cached_devices, dev);
+	}
+
+	cached_devices = g_slist_sort(cached_devices, device_timestamp_cmp);
+
+	g_strfreev(devs);
+	g_key_file_free(key_file);
+}
+
+static void load_devices_info(bt_bluetooth_ready cb)
+{
+	GKeyFile *key_file;
+	gchar **devs;
+	gsize len = 0;
+	unsigned int i;
+	GSList *keys = NULL;
+	GSList *ltks = NULL;
+	GSList *irks = NULL;
+
+	key_file = g_key_file_new();
+
+	g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL);
+
+	devs = g_key_file_get_groups(key_file, &len);
+
+	for (i = 0; i < len; i++) {
+		struct mgmt_link_key_info *key_info;
+		struct mgmt_ltk_info *ltk_info;
+		struct mgmt_irk_info *irk_info;
+		struct mgmt_ltk_info *slave_ltk_info;
+		struct device *dev;
+
+		dev = create_device_from_info(key_file, devs[i]);
+
+		key_info = get_key_info(key_file, devs[i]);
+		irk_info = get_irk_info(key_file, devs[i]);
+		ltk_info = get_ltk_info(key_file, devs[i], true);
+		slave_ltk_info = get_ltk_info(key_file, devs[i], false);
+
+		/*
+		 * Skip devices that have no permanent keys
+		 * (CSRKs are loaded by create_device_from_info())
+		 */
+		if (!dev->valid_local_csrk && !dev->valid_remote_csrk &&
+						!key_info && !ltk_info &&
+						!slave_ltk_info && !irk_info) {
+			error("Failed to load keys for %s, skipping", devs[i]);
+			free_device(dev);
+			continue;
+		}
+
+		if (key_info) {
+			keys = g_slist_prepend(keys, key_info);
+			dev->bredr_paired = true;
+			dev->bredr_bonded = true;
+		}
+
+		if (irk_info)
+			irks = g_slist_prepend(irks, irk_info);
+
+		if (ltk_info)
+			ltks = g_slist_prepend(ltks, ltk_info);
+
+		if (slave_ltk_info)
+			ltks = g_slist_prepend(ltks, slave_ltk_info);
+
+		if (dev->valid_local_csrk || dev->valid_remote_csrk ||
+				irk_info || ltk_info || slave_ltk_info) {
+			dev->le_paired = true;
+			dev->le_bonded = true;
+		}
+
+		bonded_devices = g_slist_prepend(bonded_devices, dev);
+	}
+
+	load_ltks(ltks);
+	g_slist_free_full(ltks, g_free);
+
+	load_irks(irks);
+	g_slist_free_full(irks, g_free);
+
+	load_link_keys(keys, cb);
+	g_slist_free_full(keys, g_free);
+
+	g_strfreev(devs);
+	g_key_file_free(key_file);
+}
+
+static void set_adapter_class(void)
+{
+	struct mgmt_cp_set_dev_class cp;
+
+	memset(&cp, 0, sizeof(cp));
+
+	/*
+	 * kernel assign the major and minor numbers straight to dev_class[0]
+	 * and dev_class[1] without considering the proper bit shifting.
+	 */
+	cp.major = ADAPTER_MAJOR_CLASS & 0x1f;
+	cp.minor = ADAPTER_MINOR_CLASS << 2;
+
+	if (mgmt_send(mgmt_if, MGMT_OP_SET_DEV_CLASS, adapter.index, sizeof(cp),
+						&cp, NULL, NULL, NULL) > 0)
+		return;
+
+	error("Failed to set class of device");
+}
+
+static void enable_mps(void)
+{
+	uuid_t uuid, *uuid128;
+
+	sdp_uuid16_create(&uuid, MPS_SVCLASS_ID);
+	uuid128 = sdp_uuid_to_uuid128(&uuid);
+	if (!uuid128)
+		return;
+
+	register_mps(true);
+	adapter.uuids = g_slist_prepend(adapter.uuids, uuid128);
+	add_uuid(0, uuid128);
+}
+
+static void clear_auto_connect_list_complete(uint8_t status,
+							uint16_t length,
+							const void *param,
+							void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS)
+		error("Failed to clear auto connect list: %s (0x%02x)",
+						mgmt_errstr(status), status);
+}
+
+static void clear_auto_connect_list(void)
+{
+	struct mgmt_cp_remove_device cp;
+
+	if (!kernel_conn_control)
+		return;
+
+	memset(&cp, 0, sizeof(cp));
+
+	if (mgmt_send(mgmt_if, MGMT_OP_REMOVE_DEVICE, adapter.index, sizeof(cp),
+			&cp, clear_auto_connect_list_complete, NULL, NULL) > 0)
+		return;
+
+	error("Could not clear auto connect list");
+}
+
+static void read_info_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_info *rp = param;
+	bt_bluetooth_ready cb = user_data;
+	uint32_t missing_settings;
+	int err;
+
+	DBG("");
+
+	if (status) {
+		error("Failed to read info for index %u: %s (0x%02x)",
+				adapter.index, mgmt_errstr(status), status);
+		err = -EIO;
+		goto failed;
+	}
+
+	if (length < sizeof(*rp)) {
+		error("Too small read info complete response");
+		err = -EIO;
+		goto failed;
+	}
+
+	if (!bacmp(&rp->bdaddr, BDADDR_ANY)) {
+		error("No Bluetooth address");
+		err = -ENODEV;
+		goto failed;
+	}
+
+	load_adapter_config();
+
+	if (!bacmp(&adapter.bdaddr, BDADDR_ANY)) {
+		bacpy(&adapter.bdaddr, &rp->bdaddr);
+		store_adapter_config();
+	} else if (bacmp(&adapter.bdaddr, &rp->bdaddr)) {
+		error("Bluetooth address mismatch");
+		err = -ENODEV;
+		goto failed;
+	}
+
+	if (adapter.name && g_strcmp0(adapter.name, (const char *) rp->name))
+		set_adapter_name((uint8_t *)adapter.name, strlen(adapter.name));
+
+	set_adapter_class();
+
+	/* Store adapter information */
+	adapter.dev_class = rp->dev_class[0] | (rp->dev_class[1] << 8) |
+						(rp->dev_class[2] << 16);
+
+	adapter.supported_settings = le32_to_cpu(rp->supported_settings);
+	adapter.current_settings = le32_to_cpu(rp->current_settings);
+
+	/* TODO: Register all event notification handlers */
+	register_mgmt_handlers();
+
+	clear_uuids();
+	clear_auto_connect_list();
+
+	set_io_capability();
+	set_device_id();
+	enable_mps();
+
+	missing_settings = adapter.current_settings ^
+						adapter.supported_settings;
+
+	if (missing_settings & MGMT_SETTING_SSP)
+		set_mode(MGMT_OP_SET_SSP, 0x01);
+
+	if (missing_settings & MGMT_SETTING_BONDABLE)
+		set_mode(MGMT_OP_SET_BONDABLE, 0x01);
+
+	load_devices_info(cb);
+	load_devices_cache();
+
+	return;
+
+failed:
+	cb(err, NULL);
+}
+
+static void mgmt_index_added_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	bt_bluetooth_ready cb = user_data;
+
+	DBG("index %u", index);
+
+	if (adapter.index != MGMT_INDEX_NONE) {
+		DBG("skip event for index %u", index);
+		return;
+	}
+
+	if (option_index != MGMT_INDEX_NONE && option_index != index) {
+		DBG("skip event for index %u (option %u)", index, option_index);
+		return;
+	}
+
+	adapter.index = index;
+
+	if (mgmt_send(mgmt_if, MGMT_OP_READ_INFO, index, 0, NULL,
+				read_info_complete, cb, NULL) == 0) {
+		cb(-EIO, NULL);
+		return;
+	}
+}
+
+static void mgmt_index_removed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	DBG("index %u", index);
+
+	if (index != adapter.index)
+		return;
+
+	error("Adapter was removed. Exiting.");
+	raise(SIGTERM);
+}
+
+static void read_index_list_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_index_list *rp = param;
+	bt_bluetooth_ready cb = user_data;
+	uint16_t num;
+	int i;
+
+	DBG("");
+
+	if (status) {
+		error("%s: Failed to read index list: %s (0x%02x)", __func__,
+						mgmt_errstr(status), status);
+		goto failed;
+	}
+
+	if (length < sizeof(*rp)) {
+		error("%s: Wrong size of read index list response", __func__);
+		goto failed;
+	}
+
+	num = le16_to_cpu(rp->num_controllers);
+
+	DBG("Number of controllers: %u", num);
+
+	if (num * sizeof(uint16_t) + sizeof(*rp) != length) {
+		error("%s: Incorrect pkt size for index list rsp", __func__);
+		goto failed;
+	}
+
+	if (adapter.index != MGMT_INDEX_NONE)
+		return;
+
+	for (i = 0; i < num; i++) {
+		uint16_t index = le16_to_cpu(rp->index[i]);
+
+		if (option_index != MGMT_INDEX_NONE && option_index != index)
+			continue;
+
+		if (mgmt_send(mgmt_if, MGMT_OP_READ_INFO, index, 0, NULL,
+					read_info_complete, cb, NULL) == 0)
+			goto failed;
+
+		adapter.index = index;
+		return;
+	}
+
+	return;
+
+failed:
+	cb(-EIO, NULL);
+}
+
+static void read_version_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_version *rp = param;
+	uint8_t mgmt_version, mgmt_revision;
+	bt_bluetooth_ready cb = user_data;
+
+	DBG("");
+
+	if (status) {
+		error("Failed to read version information: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		goto failed;
+	}
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size response");
+		goto failed;
+	}
+
+	mgmt_version = rp->version;
+	mgmt_revision = le16_to_cpu(rp->revision);
+
+	info("Bluetooth management interface %u.%u initialized",
+						mgmt_version, mgmt_revision);
+
+	if (MGMT_VERSION(mgmt_version, mgmt_revision) < MGMT_VERSION(1, 3)) {
+		error("Version 1.3 or later of management interface required");
+		goto failed;
+	}
+
+	/* Starting from mgmt 1.7, kernel can handle connection control */
+	if (MGMT_VERSION(mgmt_version, mgmt_revision) >= MGMT_VERSION(1, 7)) {
+		info("Kernel connection control will be used");
+		kernel_conn_control = true;
+	}
+
+	mgmt_register(mgmt_if, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+					mgmt_index_added_event, cb, NULL);
+	mgmt_register(mgmt_if, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+					mgmt_index_removed_event, NULL, NULL);
+
+	if (mgmt_send(mgmt_if, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0,
+				NULL, read_index_list_complete, cb, NULL) > 0)
+		return;
+
+	error("Failed to read controller index list");
+
+failed:
+	cb(-EIO, NULL);
+}
+
+bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb)
+{
+	DBG("index %d", index);
+
+	mgmt_if = mgmt_new_default();
+	if (!mgmt_if) {
+		error("Failed to access management interface");
+		return false;
+	}
+
+	if (mgmt_dbg)
+		mgmt_set_debug(mgmt_if, mgmt_debug, "mgmt_if: ", NULL);
+
+	if (mgmt_send(mgmt_if, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE, 0, NULL,
+				read_version_complete, cb, NULL) == 0) {
+		error("Error sending READ_VERSION mgmt command");
+
+		mgmt_unref(mgmt_if);
+		mgmt_if = NULL;
+
+		return false;
+	}
+
+	if (index >= 0)
+		option_index = index;
+
+	return true;
+}
+
+static void shutdown_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	bt_bluetooth_stopped cb = user_data;
+
+	if (status != MGMT_STATUS_SUCCESS)
+		error("Clean controller shutdown failed");
+
+	cb();
+}
+
+bool bt_bluetooth_stop(bt_bluetooth_stopped cb)
+{
+	struct mgmt_mode cp;
+
+	if (adapter.index == MGMT_INDEX_NONE)
+		return false;
+
+	info("Switching controller off");
+
+	memset(&cp, 0, sizeof(cp));
+
+	return mgmt_send(mgmt_if, MGMT_OP_SET_POWERED, adapter.index,
+				sizeof(cp), &cp, shutdown_complete, (void *)cb,
+				NULL) > 0;
+}
+
+void bt_bluetooth_cleanup(void)
+{
+	g_free(adapter.name);
+	adapter.name = NULL;
+
+	mgmt_unref(mgmt_if);
+	mgmt_if = NULL;
+}
+
+static bool set_discoverable(uint8_t mode, uint16_t timeout)
+{
+	struct mgmt_cp_set_discoverable cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.val = mode;
+	cp.timeout = cpu_to_le16(timeout);
+
+	DBG("mode %u timeout %u", mode, timeout);
+
+	if (mgmt_send(mgmt_if, MGMT_OP_SET_DISCOVERABLE, adapter.index,
+			sizeof(cp), &cp, set_mode_complete, NULL, NULL) > 0)
+		return true;
+
+	error("Failed to set mode discoverable");
+
+	return false;
+}
+
+static uint8_t get_adapter_address(void)
+{
+	uint8_t buf[6];
+
+	bdaddr2android(&adapter.bdaddr, buf);
+
+	send_adapter_property(HAL_PROP_ADAPTER_ADDR, sizeof(buf), buf);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_name(void)
+{
+	if (!adapter.name)
+		return HAL_STATUS_FAILED;
+
+	adapter_name_changed((uint8_t *) adapter.name);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_class(void)
+{
+	DBG("");
+
+	adapter_class_changed();
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t settings2type(void)
+{
+	bool bredr, le;
+
+	bredr = adapter.current_settings & MGMT_SETTING_BREDR;
+	le = adapter.current_settings & MGMT_SETTING_LE;
+
+	if (bredr && le)
+		return HAL_TYPE_DUAL;
+
+	if (bredr && !le)
+		return HAL_TYPE_BREDR;
+
+	if (!bredr && le)
+		return HAL_TYPE_LE;
+
+	return 0;
+}
+
+static uint8_t get_adapter_type(void)
+{
+	uint8_t type;
+
+	DBG("");
+
+	type = settings2type();
+
+	if (!type)
+		return HAL_STATUS_FAILED;
+
+	send_adapter_property(HAL_PROP_ADAPTER_TYPE, sizeof(type), &type);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_service_rec(void)
+{
+	DBG("Not implemented");
+
+	/* TODO: Add implementation */
+
+	return HAL_STATUS_FAILED;
+}
+
+static uint8_t get_adapter_scan_mode(void)
+{
+	DBG("");
+
+	scan_mode_changed();
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_bonded_devices(void)
+{
+	uint8_t buf[sizeof(bdaddr_t) * g_slist_length(bonded_devices)];
+	int i = 0;
+	GSList *l;
+
+	DBG("");
+
+	for (l = bonded_devices; l; l = g_slist_next(l)) {
+		struct device *dev = l->data;
+
+		get_device_android_addr(dev, buf + (i * sizeof(bdaddr_t)));
+		i++;
+	}
+
+	send_adapter_property(HAL_PROP_ADAPTER_BONDED_DEVICES,
+						i * sizeof(bdaddr_t), buf);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_discoverable_timeout(void)
+{
+	send_adapter_property(HAL_PROP_ADAPTER_DISC_TIMEOUT,
+					sizeof(adapter.discoverable_timeout),
+					&adapter.discoverable_timeout);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void prepare_le_features(uint8_t *le_features)
+{
+	le_features[0] = !!(adapter.current_settings & MGMT_SETTING_PRIVACY);
+	le_features[1] = adapter.max_advert_instance;
+	le_features[2] = adapter.rpa_offload_supported;
+	le_features[3] = adapter.max_irk_list_size;
+	le_features[4] = adapter.max_scan_filters_supported;
+	/* lo byte */
+	le_features[5] = adapter.scan_result_storage_size;
+	/* hi byte */
+	le_features[6] = adapter.scan_result_storage_size >> 8;
+	le_features[7] = adapter.activity_energy_info_supported;
+}
+
+static uint8_t get_adapter_le_features(void)
+{
+	uint8_t le_features[8];
+
+	prepare_le_features(le_features);
+
+	send_adapter_property(HAL_PROP_ADAPTER_LOCAL_LE_FEAT,
+					sizeof(le_features), le_features);
+	return HAL_STATUS_SUCCESS;
+}
+
+static void handle_get_adapter_prop_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_get_adapter_prop *cmd = buf;
+	uint8_t status;
+
+	switch (cmd->type) {
+	case HAL_PROP_ADAPTER_ADDR:
+		status = get_adapter_address();
+		break;
+	case HAL_PROP_ADAPTER_NAME:
+		status = get_adapter_name();
+		break;
+	case HAL_PROP_ADAPTER_UUIDS:
+		status = get_adapter_uuids();
+		break;
+	case HAL_PROP_ADAPTER_CLASS:
+		status = get_adapter_class();
+		break;
+	case HAL_PROP_ADAPTER_TYPE:
+		status = get_adapter_type();
+		break;
+	case HAL_PROP_ADAPTER_SERVICE_REC:
+		status = get_adapter_service_rec();
+		break;
+	case HAL_PROP_ADAPTER_SCAN_MODE:
+		status = get_adapter_scan_mode();
+		break;
+	case HAL_PROP_ADAPTER_BONDED_DEVICES:
+		status = get_adapter_bonded_devices();
+		break;
+	case HAL_PROP_ADAPTER_DISC_TIMEOUT:
+		status = get_adapter_discoverable_timeout();
+		break;
+	case HAL_PROP_ADAPTER_LOCAL_LE_FEAT:
+		status = get_adapter_le_features();
+		break;
+	default:
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
+	if (status != HAL_STATUS_SUCCESS)
+		error("Failed to get adapter property (type %u status %u)",
+							cmd->type, status);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP,
+									status);
+}
+
+static void get_adapter_properties(void)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_adapter_props_changed *ev = (void *) buf;
+	uint8_t bonded[g_slist_length(bonded_devices) * sizeof(bdaddr_t)];
+	uint128_t uuids[g_slist_length(adapter.uuids)];
+	uint8_t android_bdaddr[6];
+	uint8_t le_features[8];
+	uint8_t type, mode;
+	size_t size, i;
+	GSList *l;
+
+	size = sizeof(*ev);
+
+	ev->status = HAL_STATUS_SUCCESS;
+	ev->num_props = 0;
+
+	bdaddr2android(&adapter.bdaddr, &android_bdaddr);
+	size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_ADDR,
+					sizeof(android_bdaddr), android_bdaddr);
+	ev->num_props++;
+
+	if (adapter.name) {
+		size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_NAME,
+					strlen(adapter.name), adapter.name);
+		ev->num_props++;
+	}
+
+	size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_CLASS,
+				sizeof(adapter.dev_class), &adapter.dev_class);
+	ev->num_props++;
+
+	type = settings2type();
+	if (type) {
+		size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_TYPE,
+							sizeof(type), &type);
+		ev->num_props++;
+	}
+
+	mode = settings2scan_mode();
+	size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_SCAN_MODE,
+							sizeof(mode), &mode);
+	ev->num_props++;
+
+	size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_DISC_TIMEOUT,
+					sizeof(adapter.discoverable_timeout),
+					&adapter.discoverable_timeout);
+	ev->num_props++;
+
+	for (i = 0, l = bonded_devices; l; l = g_slist_next(l), i++) {
+		struct device *dev = l->data;
+
+		get_device_android_addr(dev, bonded + (i * sizeof(bdaddr_t)));
+	}
+
+	size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_BONDED_DEVICES,
+						sizeof(bonded), bonded);
+	ev->num_props++;
+
+	for (i = 0, l = adapter.uuids; l; l = g_slist_next(l), i++) {
+		uuid_t *uuid = l->data;
+
+		memcpy(&uuids[i], &uuid->value.uuid128, sizeof(uint128_t));
+	}
+
+	size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_UUIDS, sizeof(uuids),
+									uuids);
+	ev->num_props++;
+
+	prepare_le_features(le_features);
+	size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_LOCAL_LE_FEAT,
+					sizeof(le_features), le_features);
+
+	ev->num_props++;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_EV_ADAPTER_PROPS_CHANGED, size, buf);
+}
+
+static void cancel_pending_confirm_name(gpointer data, gpointer user_data)
+{
+	struct device *dev = data;
+
+	mgmt_cancel(mgmt_if, dev->confirm_id);
+	dev->confirm_id = 0;
+}
+
+static bool stop_discovery(uint8_t type)
+{
+	struct mgmt_cp_stop_discovery cp;
+
+	cp.type = get_supported_discovery_type() & type;
+
+	DBG("type=0x%x", cp.type);
+
+	if (cp.type == SCAN_TYPE_NONE)
+		return false;
+
+	/* Lets drop all confirm name request as we don't need it anymore */
+	g_slist_foreach(cached_devices, cancel_pending_confirm_name, NULL);
+
+	if (mgmt_send(mgmt_if, MGMT_OP_STOP_DISCOVERY, adapter.index,
+					sizeof(cp), &cp, NULL, NULL, NULL) > 0)
+		return true;
+
+	error("Failed to stop discovery");
+	return false;
+}
+
+struct adv_user_data {
+	bt_le_set_advertising_done cb;
+	void *user_data;
+};
+
+static void set_advertising_cb(uint8_t status, uint16_t length,
+			const void *param, void *user_data)
+{
+	struct adv_user_data *data = user_data;
+
+	DBG("");
+
+	if (status)
+		error("Failed to set adverising %s (0x%02x))",
+						mgmt_errstr(status), status);
+
+	data->cb(status, data->user_data);
+}
+
+bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb,
+							 void *user_data)
+{
+	struct adv_user_data *data;
+	uint8_t adv = advertising ? 0x01 : 0x00;
+
+	data = new0(struct adv_user_data, 1);
+	data->cb = cb;
+	data->user_data = user_data;
+
+	if (mgmt_send(mgmt_if, MGMT_OP_SET_ADVERTISING, adapter.index,
+			sizeof(adv), &adv, set_advertising_cb, data, free) > 0)
+		return true;
+
+	error("Failed to set advertising");
+	free(data);
+	return false;
+}
+
+bool bt_le_register(bt_le_device_found cb)
+{
+	if (gatt_device_found_cb)
+		return false;
+
+	gatt_device_found_cb = cb;
+
+	return true;
+}
+
+void bt_le_unregister(void)
+{
+	gatt_device_found_cb = NULL;
+}
+
+bool bt_le_discovery_stop(bt_le_discovery_stopped cb)
+{
+	if (!(adapter.current_settings & MGMT_SETTING_POWERED))
+		return false;
+
+	adapter.le_scanning = false;
+
+	if (adapter.cur_discovery_type != SCAN_TYPE_LE) {
+		if (cb)
+			cb();
+
+		return true;
+	}
+
+	if (!stop_discovery(SCAN_TYPE_LE))
+		return false;
+
+	gatt_discovery_stopped_cb = cb;
+	adapter.exp_discovery_type = SCAN_TYPE_NONE;
+
+	return true;
+}
+
+bool bt_le_discovery_start(void)
+{
+	if (!(adapter.current_settings & MGMT_SETTING_POWERED))
+		return false;
+
+	adapter.le_scanning = true;
+
+	/*
+	 * If core is discovering - just set expected next scan type.
+	 * It will be triggered in case current scan session is almost done
+	 * i.e. we missed LE phase in interleaved scan, or we're trying to
+	 * connect to device that was already discovered.
+	 */
+	if (adapter.cur_discovery_type != SCAN_TYPE_NONE) {
+		adapter.exp_discovery_type = SCAN_TYPE_LE;
+		return true;
+	}
+
+	if (start_discovery(SCAN_TYPE_LE))
+		return true;
+
+	return false;
+}
+
+struct read_rssi_user_data {
+	bt_read_device_rssi_done cb;
+	void *user_data;
+};
+
+static void read_device_rssi_cb(uint8_t status, uint16_t length,
+			const void *param, void *user_data)
+{
+	const struct mgmt_rp_get_conn_info *rp = param;
+	struct read_rssi_user_data *data = user_data;
+
+	DBG("");
+
+	if (status)
+		error("Failed to get conn info: %s (0x%02x))",
+						mgmt_errstr(status), status);
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size of get conn info response");
+		return;
+	}
+
+	data->cb(status, &rp->addr.bdaddr, rp->rssi, data->user_data);
+}
+
+bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb,
+							void *user_data)
+{
+	struct device *dev;
+	struct read_rssi_user_data *data;
+	struct mgmt_cp_get_conn_info cp;
+
+	dev = find_device(addr);
+	if (!dev)
+		return false;
+
+	memcpy(&cp.addr.bdaddr, addr, sizeof(cp.addr.bdaddr));
+	cp.addr.type = dev->bredr ? BDADDR_BREDR : dev->bdaddr_type;
+
+	data = new0(struct read_rssi_user_data, 1);
+	data->cb = cb;
+	data->user_data = user_data;
+
+	if (!mgmt_send(mgmt_if, MGMT_OP_GET_CONN_INFO, adapter.index,
+			sizeof(cp), &cp, read_device_rssi_cb, data, free)) {
+		free(data);
+		error("Failed to get conn info");
+		return false;
+	}
+
+	return true;
+}
+
+bool bt_get_csrk(const bdaddr_t *addr, bool local, uint8_t key[16],
+					uint32_t *sign_cnt, bool *authenticated)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return false;
+
+	if (local && dev->valid_local_csrk) {
+		if (key)
+			memcpy(key, dev->local_csrk, 16);
+
+		if (sign_cnt)
+			*sign_cnt = dev->local_sign_cnt;
+
+		if (authenticated)
+			*authenticated = dev->local_csrk_auth;
+	} else if (!local && dev->valid_remote_csrk) {
+		if (key)
+			memcpy(key, dev->remote_csrk, 16);
+
+		if (sign_cnt)
+			*sign_cnt = dev->remote_sign_cnt;
+
+		if (authenticated)
+			*authenticated = dev->remote_csrk_auth;
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+static void store_sign_counter(struct device *dev, bool local)
+{
+	const char *sign_cnt_s;
+	uint32_t sign_cnt;
+	GKeyFile *key_file;
+
+	gsize length = 0;
+	char addr[18];
+	char *data;
+
+	key_file = g_key_file_new();
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	ba2str(&dev->bdaddr, addr);
+
+	sign_cnt_s = local ? "LocalCSRKSignCounter" : "RemoteCSRKSignCounter";
+	sign_cnt = local ? dev->local_sign_cnt : dev->remote_sign_cnt;
+
+	g_key_file_set_integer(key_file, addr, sign_cnt_s, sign_cnt);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+void bt_update_sign_counter(const bdaddr_t *addr, bool local, uint32_t val)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return;
+
+	if (local)
+		dev->local_sign_cnt = val;
+	else
+		dev->remote_sign_cnt = val;
+
+	store_sign_counter(dev, local);
+}
+
+static uint8_t set_adapter_scan_mode(const void *buf, uint16_t len)
+{
+	const uint8_t *mode = buf;
+	bool conn, disc, cur_conn, cur_disc;
+
+	if (len != sizeof(*mode)) {
+		error("Invalid set scan mode size (%u bytes), terminating",
+								len);
+		raise(SIGTERM);
+		return HAL_STATUS_FAILED;
+	}
+
+	cur_conn = adapter.current_settings & MGMT_SETTING_CONNECTABLE;
+	cur_disc = adapter.current_settings & MGMT_SETTING_DISCOVERABLE;
+
+	DBG("connectable %u discoverable %d mode %u", cur_conn, cur_disc,
+								*mode);
+
+	switch (*mode) {
+	case HAL_ADAPTER_SCAN_MODE_NONE:
+		if (!cur_conn && !cur_disc)
+			goto done;
+
+		conn = false;
+		disc = false;
+		break;
+	case HAL_ADAPTER_SCAN_MODE_CONN:
+		if (cur_conn && !cur_disc)
+			goto done;
+
+		conn = true;
+		disc = false;
+		break;
+	case HAL_ADAPTER_SCAN_MODE_CONN_DISC:
+		if (cur_conn && cur_disc)
+			goto done;
+
+		conn = true;
+		disc = true;
+		break;
+	default:
+		return HAL_STATUS_FAILED;
+	}
+
+	if (cur_conn != conn) {
+		if (!set_mode(MGMT_OP_SET_CONNECTABLE, conn ? 0x01 : 0x00))
+			return HAL_STATUS_FAILED;
+	}
+
+	if (cur_disc != disc && conn) {
+		if (!set_discoverable(disc ? 0x01 : 0x00, 0))
+			return HAL_STATUS_FAILED;
+	}
+
+	return HAL_STATUS_SUCCESS;
+
+done:
+	/* Android expects property changed callback */
+	scan_mode_changed();
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void handle_set_adapter_prop_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_set_adapter_prop *cmd = buf;
+	uint8_t status;
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid set adapter prop cmd (0x%x), terminating",
+								cmd->type);
+		raise(SIGTERM);
+		return;
+	}
+
+	switch (cmd->type) {
+	case HAL_PROP_ADAPTER_SCAN_MODE:
+		status = set_adapter_scan_mode(cmd->val, cmd->len);
+		break;
+	case HAL_PROP_ADAPTER_NAME:
+		status = set_adapter_name(cmd->val, cmd->len);
+		break;
+	case HAL_PROP_ADAPTER_DISC_TIMEOUT:
+		status = set_adapter_discoverable_timeout(cmd->val, cmd->len);
+		break;
+	default:
+		DBG("Unhandled property type 0x%x", cmd->type);
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
+	if (status != HAL_STATUS_SUCCESS)
+		error("Failed to set adapter property (type %u status %u)",
+							cmd->type, status);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP,
+									status);
+}
+
+static void pair_device_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_pair_device *rp = param;
+	struct device *dev;
+
+	DBG("status %u", status);
+
+	dev = find_device(&rp->addr.bdaddr);
+	if (!dev)
+		return;
+
+	/*
+	 * Update pairing and paired status. Bonded status will be updated once
+	 * any link key come
+	 */
+	update_device_state(dev, rp->addr.type, status_mgmt2hal(status), false,
+								!status, false);
+
+	if (status == MGMT_STATUS_SUCCESS)
+		queue_foreach(paired_cb_list, send_paired_notification, dev);
+}
+
+static uint8_t select_device_bearer(struct device *dev)
+{
+	uint8_t res;
+
+	if (dev->bredr && dev->le) {
+		if (dev->le_seen > dev->bredr_seen)
+			res = dev->bdaddr_type;
+		else
+			res = BDADDR_BREDR;
+	} else {
+		res = dev->bredr ? BDADDR_BREDR : dev->bdaddr_type;
+	}
+
+	DBG("Selected bearer %d", res);
+
+	return res;
+}
+
+uint8_t bt_device_last_seen_bearer(const bdaddr_t *bdaddr)
+{
+	 struct device *dev;
+
+	dev = find_device(bdaddr);
+	if (!dev)
+		return BDADDR_BREDR;
+
+	return select_device_bearer(dev);
+}
+
+static void handle_create_bond_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_create_bond *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+	struct mgmt_cp_pair_device cp;
+
+	dev = get_device_android(cmd->bdaddr);
+
+	cp.io_cap = DEFAULT_IO_CAPABILITY;
+	cp.addr.type = select_device_bearer(dev);
+	bacpy(&cp.addr.bdaddr, &dev->bdaddr);
+
+	/* TODO: Handle transport parameter */
+	if (cmd->transport > BT_TRANSPORT_LE) {
+		status = HAL_STATUS_INVALID;
+		goto fail;
+	}
+
+	if (device_is_paired(dev, cp.addr.type)) {
+		status = HAL_STATUS_FAILED;
+		goto fail;
+	}
+
+	if (mgmt_send(mgmt_if, MGMT_OP_PAIR_DEVICE, adapter.index, sizeof(cp),
+				&cp, pair_device_complete, NULL, NULL) == 0) {
+		status = HAL_STATUS_FAILED;
+		goto fail;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+	update_device_state(dev, cp.addr.type, HAL_STATUS_SUCCESS, true, false,
+									false);
+
+fail:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND,
+									status);
+}
+
+static void handle_cancel_bond_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_cancel_bond *cmd = buf;
+	struct mgmt_addr_info cp;
+	struct device *dev;
+	uint8_t status;
+
+	dev = find_device_android(cmd->bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	cp.type = select_device_bearer(dev);
+	bacpy(&cp.bdaddr, &dev->bdaddr);
+
+	if (mgmt_reply(mgmt_if, MGMT_OP_CANCEL_PAIR_DEVICE, adapter.index,
+				sizeof(cp), &cp, NULL, NULL, NULL) == 0) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND,
+									status);
+}
+
+static void send_unpaired_notification(void *data, void *user_data)
+{
+	bt_unpaired_device_cb cb = data;
+	struct mgmt_addr_info *addr = user_data;
+
+	cb(&addr->bdaddr);
+}
+
+static void unpair_device_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_unpair_device *rp = param;
+	struct device *dev;
+
+	DBG("status %u", status);
+
+	if (status != MGMT_STATUS_SUCCESS && status != MGMT_STATUS_NOT_PAIRED)
+		return;
+
+	dev = find_device(&rp->addr.bdaddr);
+	if (!dev)
+		return;
+
+	update_device_state(dev, rp->addr.type, HAL_STATUS_SUCCESS, false,
+								false, false);
+
+	/* Cast rp->addr to (void *) since queue_foreach don't take const */
+
+	if (!dev->le_paired && !dev->bredr_paired)
+		queue_foreach(unpaired_cb_list, send_unpaired_notification,
+							(void *)&rp->addr);
+}
+
+static void handle_remove_bond_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_remove_bond *cmd = buf;
+	struct mgmt_cp_unpair_device cp;
+	struct device *dev;
+	uint8_t status;
+
+	dev = find_device_android(cmd->bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	cp.disconnect = 1;
+	bacpy(&cp.addr.bdaddr, &dev->bdaddr);
+
+	if (dev->le_paired) {
+		cp.addr.type = dev->bdaddr_type;
+
+		if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index,
+					sizeof(cp), &cp, unpair_device_complete,
+					NULL, NULL) == 0) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+	}
+
+	if (dev->bredr_paired) {
+		cp.addr.type = BDADDR_BREDR;
+
+		if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index,
+					sizeof(cp), &cp, unpair_device_complete,
+					NULL, NULL) == 0) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND,
+									status);
+}
+
+static void handle_pin_reply_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_pin_reply *cmd = buf;
+	uint8_t status;
+	bdaddr_t bdaddr;
+	char addr[18];
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+	ba2str(&bdaddr, addr);
+
+	DBG("%s accept %u pin_len %u", addr, cmd->accept, cmd->pin_len);
+
+	if (!cmd->accept && cmd->pin_len) {
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	if (cmd->accept) {
+		struct mgmt_cp_pin_code_reply rp;
+
+		memset(&rp, 0, sizeof(rp));
+
+		bacpy(&rp.addr.bdaddr, &bdaddr);
+		rp.addr.type = BDADDR_BREDR;
+		rp.pin_len = cmd->pin_len;
+		memcpy(rp.pin_code, cmd->pin_code, rp.pin_len);
+
+		if (mgmt_reply(mgmt_if, MGMT_OP_PIN_CODE_REPLY, adapter.index,
+				sizeof(rp), &rp, NULL, NULL, NULL) == 0) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+	} else {
+		struct mgmt_cp_pin_code_neg_reply rp;
+
+		bacpy(&rp.addr.bdaddr, &bdaddr);
+		rp.addr.type = BDADDR_BREDR;
+
+		if (mgmt_reply(mgmt_if, MGMT_OP_PIN_CODE_NEG_REPLY,
+						adapter.index, sizeof(rp), &rp,
+						NULL, NULL, NULL) == 0) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+	}
+
+	status = HAL_STATUS_SUCCESS;
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY,
+									status);
+}
+
+static uint8_t user_confirm_reply(const bdaddr_t *bdaddr, uint8_t type,
+								bool accept)
+{
+	struct mgmt_addr_info cp;
+	uint16_t opcode;
+
+	if (accept)
+		opcode = MGMT_OP_USER_CONFIRM_REPLY;
+	else
+		opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY;
+
+	bacpy(&cp.bdaddr, bdaddr);
+	cp.type = type;
+
+	if (mgmt_reply(mgmt_if, opcode, adapter.index, sizeof(cp), &cp,
+							NULL, NULL, NULL) > 0)
+		return HAL_STATUS_SUCCESS;
+
+	return HAL_STATUS_FAILED;
+}
+
+static uint8_t user_passkey_reply(const bdaddr_t *bdaddr, uint8_t type,
+						bool accept, uint32_t passkey)
+{
+	unsigned int id;
+
+	if (accept) {
+		struct mgmt_cp_user_passkey_reply cp;
+
+		memset(&cp, 0, sizeof(cp));
+		bacpy(&cp.addr.bdaddr, bdaddr);
+		cp.addr.type = type;
+		cp.passkey = cpu_to_le32(passkey);
+
+		id = mgmt_reply(mgmt_if, MGMT_OP_USER_PASSKEY_REPLY,
+						adapter.index, sizeof(cp), &cp,
+						NULL, NULL, NULL);
+	} else {
+		struct mgmt_cp_user_passkey_neg_reply cp;
+
+		memset(&cp, 0, sizeof(cp));
+		bacpy(&cp.addr.bdaddr, bdaddr);
+		cp.addr.type = type;
+
+		id = mgmt_reply(mgmt_if, MGMT_OP_USER_PASSKEY_NEG_REPLY,
+						adapter.index, sizeof(cp), &cp,
+						NULL, NULL, NULL);
+	}
+
+	if (id == 0)
+		return HAL_STATUS_FAILED;
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void handle_ssp_reply_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_ssp_reply *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+	char addr[18];
+
+	/* TODO should parameters sanity be verified here? */
+
+	dev = find_device_android(cmd->bdaddr);
+	if (!dev)
+		return;
+
+	ba2str(&dev->bdaddr, addr);
+
+	DBG("%s variant %u accept %u", addr, cmd->ssp_variant, cmd->accept);
+
+	switch (cmd->ssp_variant) {
+	case HAL_SSP_VARIANT_CONFIRM:
+	case HAL_SSP_VARIANT_CONSENT:
+		status = user_confirm_reply(&dev->bdaddr,
+						select_device_bearer(dev),
+						cmd->accept);
+		break;
+	case HAL_SSP_VARIANT_ENTRY:
+		status = user_passkey_reply(&dev->bdaddr,
+						select_device_bearer(dev),
+						cmd->accept, cmd->passkey);
+		break;
+	case HAL_SSP_VARIANT_NOTIF:
+		status = HAL_STATUS_SUCCESS;
+		break;
+	default:
+		status = HAL_STATUS_INVALID;
+		break;
+	}
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY,
+									status);
+}
+
+static void handle_get_remote_services_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_get_remote_services *cmd = buf;
+	uint8_t status;
+	bdaddr_t addr;
+
+	android2bdaddr(&cmd->bdaddr, &addr);
+
+	status = browse_remote_sdp(&addr);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_GET_REMOTE_SERVICES, status);
+}
+
+static uint8_t get_device_uuids(struct device *dev)
+{
+	send_device_uuids_notif(dev);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_class(struct device *dev)
+{
+	send_device_property(dev, HAL_PROP_DEVICE_CLASS, sizeof(dev->class),
+								&dev->class);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_type(struct device *dev)
+{
+	uint8_t type = get_device_android_type(dev);
+
+	send_device_property(dev, HAL_PROP_DEVICE_TYPE, sizeof(type), &type);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_service_rec(struct device *dev)
+{
+	DBG("Not implemented");
+
+	/* TODO */
+
+	return HAL_STATUS_FAILED;
+}
+
+static uint8_t get_device_friendly_name(struct device *dev)
+{
+	if (!dev->friendly_name)
+		return HAL_STATUS_FAILED;
+
+	send_device_property(dev, HAL_PROP_DEVICE_FRIENDLY_NAME,
+				strlen(dev->friendly_name), dev->friendly_name);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_rssi(struct device *dev)
+{
+	if (!dev->rssi)
+		return HAL_STATUS_FAILED;
+
+	send_device_property(dev, HAL_PROP_DEVICE_RSSI, sizeof(dev->rssi),
+								&dev->rssi);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_version_info(struct device *dev)
+{
+	DBG("Not implemented");
+
+	/* TODO */
+
+	return HAL_STATUS_FAILED;
+}
+
+static uint8_t get_device_timestamp(struct device *dev)
+{
+	uint32_t timestamp;
+
+	timestamp = device_timestamp(dev);
+
+	send_device_property(dev, HAL_PROP_DEVICE_TIMESTAMP, sizeof(timestamp),
+								&timestamp);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void get_remote_device_props(struct device *dev)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_remote_device_props *ev = (void *) buf;
+	uint128_t uuids[g_slist_length(dev->uuids)];
+	uint8_t android_type;
+	uint32_t timestamp;
+	size_t size, i;
+	GSList *l;
+
+	memset(buf, 0, sizeof(buf));
+
+	size = sizeof(*ev);
+
+	ev->status = HAL_STATUS_SUCCESS;
+	get_device_android_addr(dev, ev->bdaddr);
+
+	android_type = get_device_android_type(dev);
+	size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE,
+					sizeof(android_type), &android_type);
+	ev->num_props++;
+
+	size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS,
+					sizeof(dev->class), &dev->class);
+	ev->num_props++;
+
+	if (dev->rssi) {
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI,
+						sizeof(dev->rssi), &dev->rssi);
+		ev->num_props++;
+	}
+
+	size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME,
+						strlen(dev->name), dev->name);
+	ev->num_props++;
+
+	if (dev->friendly_name) {
+		size += fill_hal_prop(buf + size,
+					HAL_PROP_DEVICE_FRIENDLY_NAME,
+					strlen(dev->friendly_name),
+					dev->friendly_name);
+		ev->num_props++;
+	}
+
+	for (i = 0, l = dev->uuids; l; l = g_slist_next(l), i++)
+		memcpy(&uuids[i], l->data, sizeof(uint128_t));
+
+	size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_UUIDS, sizeof(uuids),
+									uuids);
+	ev->num_props++;
+
+	timestamp = get_device_timestamp(dev);
+
+	size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TIMESTAMP,
+						sizeof(timestamp), &timestamp);
+	ev->num_props++;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+					HAL_EV_REMOTE_DEVICE_PROPS, size, buf);
+}
+
+static void send_bonded_devices_props(void)
+{
+	GSList *l;
+
+	for (l = bonded_devices; l; l = g_slist_next(l)) {
+		struct device *dev = l->data;
+
+		get_remote_device_props(dev);
+	}
+}
+
+static void handle_enable_cmd(const void *buf, uint16_t len)
+{
+	uint8_t status;
+
+	/*
+	 * Framework expects all properties to be emitted while enabling
+	 * adapter
+	 */
+	get_adapter_properties();
+
+	/* Sent also properties of bonded devices */
+	send_bonded_devices_props();
+
+	if (adapter.current_settings & MGMT_SETTING_POWERED) {
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	if (!set_mode(MGMT_OP_SET_POWERED, 0x01)) {
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, status);
+}
+
+static void handle_disable_cmd(const void *buf, uint16_t len)
+{
+	uint8_t status;
+
+	if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	/* Cancel all pending requests. Need it in case of ongoing paring */
+	mgmt_cancel_index(mgmt_if, adapter.index);
+
+	if (!set_mode(MGMT_OP_SET_POWERED, 0x00)) {
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, status);
+}
+
+static void handle_get_adapter_props_cmd(const void *buf, uint16_t len)
+{
+	get_adapter_properties();
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_OP_GET_ADAPTER_PROPS, HAL_STATUS_SUCCESS);
+}
+
+static void handle_get_remote_device_props_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_get_remote_device_props *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+
+	dev = find_device_android(cmd->bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	get_remote_device_props(dev);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_GET_REMOTE_DEVICE_PROPS, status);
+}
+
+static void handle_get_remote_device_prop_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_get_remote_device_prop *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+
+	dev = find_device_android(cmd->bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	switch (cmd->type) {
+	case HAL_PROP_DEVICE_NAME:
+		status = get_device_name(dev);
+		break;
+	case HAL_PROP_DEVICE_UUIDS:
+		status = get_device_uuids(dev);
+		break;
+	case HAL_PROP_DEVICE_CLASS:
+		status = get_device_class(dev);
+		break;
+	case HAL_PROP_DEVICE_TYPE:
+		status = get_device_type(dev);
+		break;
+	case HAL_PROP_DEVICE_SERVICE_REC:
+		status = get_device_service_rec(dev);
+		break;
+	case HAL_PROP_DEVICE_FRIENDLY_NAME:
+		status = get_device_friendly_name(dev);
+		break;
+	case HAL_PROP_DEVICE_RSSI:
+		status = get_device_rssi(dev);
+		break;
+	case HAL_PROP_DEVICE_VERSION_INFO:
+		status = get_device_version_info(dev);
+		break;
+	case HAL_PROP_DEVICE_TIMESTAMP:
+		status = get_device_timestamp(dev);
+		break;
+	default:
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
+	if (status != HAL_STATUS_SUCCESS)
+		error("Failed to get device property (type %u status %u)",
+							cmd->type, status);
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_GET_REMOTE_DEVICE_PROP, status);
+}
+
+static uint8_t set_device_friendly_name(struct device *dev, const uint8_t *val,
+								uint16_t len)
+{
+	DBG("");
+
+	g_free(dev->friendly_name);
+	dev->friendly_name = g_strndup((const char *) val, len);
+
+	if (dev->bredr_paired || dev->le_paired)
+		store_device_info(dev, DEVICES_FILE);
+	else
+		store_device_info(dev, CACHE_FILE);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t set_device_version_info(struct device *dev)
+{
+	DBG("Not implemented");
+
+	/* TODO */
+
+	return HAL_STATUS_FAILED;
+}
+
+static void handle_set_remote_device_prop_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_set_remote_device_prop *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid set remote device prop cmd (0x%x), terminating",
+								cmd->type);
+		raise(SIGTERM);
+		return;
+	}
+
+	dev = find_device_android(cmd->bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	switch (cmd->type) {
+	case HAL_PROP_DEVICE_FRIENDLY_NAME:
+		status = set_device_friendly_name(dev, cmd->val, cmd->len);
+		break;
+	case HAL_PROP_DEVICE_VERSION_INFO:
+		status = set_device_version_info(dev);
+		break;
+	default:
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
+	if (status != HAL_STATUS_SUCCESS)
+		error("Failed to set device property (type %u status %u)",
+							cmd->type, status);
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_SET_REMOTE_DEVICE_PROP, status);
+}
+
+static void handle_get_remote_service_rec_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_get_remote_service_rec *cmd = buf;
+	uint8_t status;
+	bdaddr_t addr;
+
+	android2bdaddr(&cmd->bdaddr, &addr);
+
+	status = find_remote_sdp_rec(&addr, cmd->uuid);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_GET_REMOTE_SERVICE_REC, status);
+}
+
+static void handle_start_discovery_cmd(const void *buf, uint16_t len)
+{
+	uint8_t status;
+
+	if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
+		status = HAL_STATUS_NOT_READY;
+		goto failed;
+	}
+
+	switch (adapter.cur_discovery_type) {
+	case SCAN_TYPE_DUAL:
+	case SCAN_TYPE_BREDR:
+		break;
+	case SCAN_TYPE_NONE:
+		if (!start_discovery(SCAN_TYPE_DUAL)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case SCAN_TYPE_LE:
+		if (get_supported_discovery_type() == SCAN_TYPE_LE)
+			break;
+
+		if (!stop_discovery(SCAN_TYPE_LE)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		adapter.exp_discovery_type = SCAN_TYPE_DUAL;
+		break;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY,
+									status);
+}
+
+static void handle_cancel_discovery_cmd(const void *buf, uint16_t len)
+{
+	uint8_t status;
+
+	if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
+		status = HAL_STATUS_NOT_READY;
+		goto failed;
+	}
+
+	switch (adapter.cur_discovery_type) {
+	case SCAN_TYPE_NONE:
+		break;
+	case SCAN_TYPE_LE:
+		if (get_supported_discovery_type() != SCAN_TYPE_LE)
+			break;
+
+		if (adapter.exp_discovery_type == SCAN_TYPE_LE) {
+			status = HAL_STATUS_BUSY;
+			goto failed;
+		}
+
+		if (!stop_discovery(SCAN_TYPE_LE)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case SCAN_TYPE_DUAL:
+	case SCAN_TYPE_BREDR:
+		if (!stop_discovery(SCAN_TYPE_DUAL)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		if (adapter.exp_discovery_type != SCAN_TYPE_LE)
+			adapter.exp_discovery_type = SCAN_TYPE_NONE;
+
+		break;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY,
+									status);
+}
+
+static void handle_dut_mode_conf_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_dut_mode_conf *cmd = buf;
+	char path[FILENAME_MAX];
+	uint8_t status;
+	int fd, ret;
+
+	DBG("enable %u", cmd->enable);
+
+	snprintf(path, sizeof(path), DUT_MODE_FILE, adapter.index);
+
+	fd = open(path, O_WRONLY);
+	if (fd < 0) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (cmd->enable)
+		ret = write(fd, "1", sizeof("1"));
+	else
+		ret = write(fd, "0", sizeof("0"));
+
+	if (ret < 0)
+		status = HAL_STATUS_FAILED;
+	else
+		status = HAL_STATUS_SUCCESS;
+
+	close(fd);
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF,
+									status);
+}
+
+static void handle_dut_mode_send_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_dut_mode_send *cmd = buf;
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid dut mode send cmd, terminating");
+		raise(SIGTERM);
+		return;
+	}
+
+	error("dut_mode_send not supported (cmd opcode %u)", cmd->opcode);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND,
+							HAL_STATUS_FAILED);
+}
+
+static void handle_le_test_mode_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_le_test_mode *cmd = buf;
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid le test mode cmd, terminating");
+		raise(SIGTERM);
+		return;
+	}
+
+	error("le_test_mode not supported (cmd opcode %u)", cmd->opcode);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE,
+							HAL_STATUS_FAILED);
+}
+
+static void handle_get_connection_state(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_get_connection_state *cmd = buf;
+	struct hal_rsp_get_connection_state rsp;
+	struct device *dev;
+	char address[18];
+	bdaddr_t bdaddr;
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+	ba2str(&bdaddr, address);
+
+	dev = find_device_android(cmd->bdaddr);
+	if (dev && dev->connected)
+		rsp.connection_state = 1;
+	else
+		rsp.connection_state = 0;
+
+	DBG("%s %u", address, rsp.connection_state);
+
+	ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_OP_GET_CONNECTION_STATE, sizeof(rsp), &rsp,
+				-1);
+}
+
+static void handle_read_energy_info(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_READ_ENERGY_INFO,
+							HAL_STATUS_UNSUPPORTED);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_ENABLE */
+	{ handle_enable_cmd, false, 0 },
+	/* HAL_OP_DISABLE */
+	{ handle_disable_cmd, false, 0 },
+	/* HAL_OP_GET_ADAPTER_PROPS */
+	{ handle_get_adapter_props_cmd, false, 0 },
+	/* HAL_OP_GET_ADAPTER_PROP */
+	{ handle_get_adapter_prop_cmd, false,
+				sizeof(struct hal_cmd_get_adapter_prop) },
+	/* HAL_OP_SET_ADAPTER_PROP */
+	{ handle_set_adapter_prop_cmd, true,
+				sizeof(struct hal_cmd_set_adapter_prop) },
+	/* HAL_OP_GET_REMOTE_DEVICE_PROPS */
+	{ handle_get_remote_device_props_cmd, false,
+			sizeof(struct hal_cmd_get_remote_device_props) },
+	/* HAL_OP_GET_REMOTE_DEVICE_PROP */
+	{ handle_get_remote_device_prop_cmd, false,
+				sizeof(struct hal_cmd_get_remote_device_prop) },
+	/* HAL_OP_SET_REMOTE_DEVICE_PROP */
+	{ handle_set_remote_device_prop_cmd, true,
+				sizeof(struct hal_cmd_set_remote_device_prop) },
+	/* HAL_OP_GET_REMOTE_SERVICE_REC */
+	{ handle_get_remote_service_rec_cmd, false,
+				sizeof(struct hal_cmd_get_remote_service_rec) },
+	/* HAL_OP_GET_REMOTE_SERVICES */
+	{ handle_get_remote_services_cmd, false,
+				sizeof(struct hal_cmd_get_remote_services) },
+	/* HAL_OP_START_DISCOVERY */
+	{ handle_start_discovery_cmd, false, 0 },
+	/* HAL_OP_CANCEL_DISCOVERY */
+	{ handle_cancel_discovery_cmd, false, 0 },
+	/* HAL_OP_CREATE_BOND */
+	{ handle_create_bond_cmd, false, sizeof(struct hal_cmd_create_bond) },
+	/* HAL_OP_REMOVE_BOND */
+	{ handle_remove_bond_cmd, false, sizeof(struct hal_cmd_remove_bond) },
+	/* HAL_OP_CANCEL_BOND */
+	{handle_cancel_bond_cmd, false, sizeof(struct hal_cmd_cancel_bond) },
+	/* HAL_OP_PIN_REPLY */
+	{ handle_pin_reply_cmd, false, sizeof(struct hal_cmd_pin_reply) },
+	/* HAL_OP_SSP_REPLY */
+	{ handle_ssp_reply_cmd, false, sizeof(struct hal_cmd_ssp_reply) },
+	/* HAL_OP_DUT_MODE_CONF */
+	{ handle_dut_mode_conf_cmd, false,
+					sizeof(struct hal_cmd_dut_mode_conf) },
+	/* HAL_OP_DUT_MODE_SEND */
+	{ handle_dut_mode_send_cmd, true,
+					sizeof(struct hal_cmd_dut_mode_send) },
+	/* HAL_OP_LE_TEST_MODE */
+	{ handle_le_test_mode_cmd, true, sizeof(struct hal_cmd_le_test_mode) },
+	/* HAL_OP_GET_CONNECTION_STATE */
+	{ handle_get_connection_state, false,
+				sizeof(struct hal_cmd_get_connection_state) },
+	/* HAL_OP_READ_ENERGY_INFO */
+	{ handle_read_energy_info, false, 0 },
+};
+
+bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode)
+{
+	uint32_t missing_settings;
+
+	DBG("mode 0x%x", mode);
+
+	unpaired_cb_list = queue_new();
+	paired_cb_list = queue_new();
+
+	missing_settings = adapter.current_settings ^
+						adapter.supported_settings;
+
+	switch (mode) {
+	case HAL_MODE_DEFAULT:
+		if (missing_settings & MGMT_SETTING_BREDR)
+			set_mode(MGMT_OP_SET_BREDR, 0x01);
+
+		if (missing_settings & MGMT_SETTING_LE)
+			set_mode(MGMT_OP_SET_LE, 0x01);
+		break;
+	case HAL_MODE_LE:
+		/* Fail if controller does not support LE */
+		if (!(adapter.supported_settings & MGMT_SETTING_LE)) {
+			error("LE Mode not supported by controller");
+			goto failed;
+		}
+
+		/* If LE it is not yet enabled then enable it */
+		if (!(adapter.current_settings & MGMT_SETTING_LE))
+			set_mode(MGMT_OP_SET_LE, 0x01);
+
+		/* Disable BR/EDR if it is enabled */
+		if (adapter.current_settings & MGMT_SETTING_BREDR)
+			set_mode(MGMT_OP_SET_BREDR, 0x00);
+		break;
+	case HAL_MODE_BREDR:
+		/* Fail if controller does not support BR/EDR */
+		if (!(adapter.supported_settings & MGMT_SETTING_BREDR)) {
+			error("BR/EDR Mode not supported");
+			goto failed;
+		}
+
+		/* Enable BR/EDR if it is not enabled */
+		if (missing_settings & MGMT_SETTING_BREDR)
+			set_mode(MGMT_OP_SET_BREDR, 0x01);
+
+		/*
+		 * According to Core Spec 4.0 host should not disable LE in
+		 * controller if it was enabled (Vol 2. Part E. 7.3.79).
+		 * Core Spec 4.1 removed this limitation and chips seem to be
+		 * handling this just fine anyway.
+		 */
+		if (adapter.current_settings & MGMT_SETTING_LE)
+			set_mode(MGMT_OP_SET_LE, 0x00);
+		break;
+	default:
+		error("Unknown mode 0x%x", mode);
+		goto failed;
+	}
+
+	/* Requested mode is set now, let's enable secure connection */
+	if (missing_settings & MGMT_SETTING_SECURE_CONN)
+		set_mode(MGMT_OP_SET_SECURE_CONN, 0x01);
+
+	/* Set initial default name */
+	if (!adapter.name) {
+		adapter.name = g_strdup(bt_config_get_model());
+		set_adapter_name((uint8_t *)adapter.name, strlen(adapter.name));
+	}
+
+	hal_ipc = ipc;
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+
+failed:
+	queue_destroy(unpaired_cb_list, NULL);
+	unpaired_cb_list = NULL;
+	queue_destroy(paired_cb_list, NULL);
+	paired_cb_list = NULL;
+
+	return false;
+}
+
+void bt_bluetooth_unregister(void)
+{
+	DBG("");
+
+	g_slist_free_full(bonded_devices, (GDestroyNotify) free_device);
+	bonded_devices = NULL;
+
+	g_slist_free_full(cached_devices, (GDestroyNotify) free_device);
+	cached_devices = NULL;
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_CORE);
+	hal_ipc = NULL;
+
+	queue_destroy(unpaired_cb_list, NULL);
+	unpaired_cb_list = NULL;
+
+	queue_destroy(paired_cb_list, NULL);
+	paired_cb_list = NULL;
+}
diff --git a/repo/android/bluetooth.h b/repo/android/bluetooth.h
new file mode 100644
index 0000000..4b17209
--- /dev/null
+++ b/repo/android/bluetooth.h
@@ -0,0 +1,90 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+typedef void (*bt_bluetooth_ready)(int err, const bdaddr_t *addr);
+bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb);
+
+typedef void (*bt_bluetooth_stopped)(void);
+bool bt_bluetooth_stop(bt_bluetooth_stopped cb);
+
+void bt_bluetooth_cleanup(void);
+
+bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode);
+void bt_bluetooth_unregister(void);
+
+int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint);
+void bt_adapter_remove_record(uint32_t handle);
+
+typedef void (*bt_le_device_found)(const bdaddr_t *addr, int rssi,
+					uint16_t eir_len, const void *eir,
+					bool connectable, bool bonded);
+bool bt_le_register(bt_le_device_found cb);
+void bt_le_unregister(void);
+
+bool bt_le_discovery_start(void);
+
+typedef void (*bt_le_discovery_stopped)(void);
+bool bt_le_discovery_stop(bt_le_discovery_stopped cb);
+
+typedef void (*bt_le_set_advertising_done)(uint8_t status, void *user_data);
+bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb,
+							void *user_data);
+
+uint8_t bt_get_device_android_type(const bdaddr_t *addr);
+bool bt_is_device_le(const bdaddr_t *addr);
+uint8_t bt_device_last_seen_bearer(const bdaddr_t *bdaddr);
+
+const char *bt_get_adapter_name(void);
+bool bt_device_is_bonded(const bdaddr_t *bdaddr);
+bool bt_device_set_uuids(const bdaddr_t *bdaddr, GSList *uuids);
+
+typedef void (*bt_read_device_rssi_done)(uint8_t status, const bdaddr_t *addr,
+						int8_t rssi, void *user_data);
+bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb,
+							void *user_data);
+
+bool bt_get_csrk(const bdaddr_t *addr, bool local, uint8_t key[16],
+				uint32_t *sign_cnt, bool *authenticated);
+
+void bt_update_sign_counter(const bdaddr_t *addr, bool local, uint32_t val);
+
+void bt_store_gatt_ccc(const bdaddr_t *addr, uint16_t value);
+
+uint16_t bt_get_gatt_ccc(const bdaddr_t *addr);
+
+const bdaddr_t *bt_get_id_addr(const bdaddr_t *addr, uint8_t *type);
+
+bool bt_kernel_conn_control(void);
+
+bool bt_auto_connect_add(const bdaddr_t *addr);
+
+void bt_auto_connect_remove(const bdaddr_t *addr);
+
+typedef void (*bt_unpaired_device_cb)(const bdaddr_t *addr);
+bool bt_unpaired_register(bt_unpaired_device_cb cb);
+void bt_unpaired_unregister(bt_unpaired_device_cb cb);
+
+typedef void (*bt_paired_device_cb)(const bdaddr_t *addr);
+bool bt_paired_register(bt_paired_device_cb cb);
+void bt_paired_unregister(bt_paired_device_cb cb);
+bool bt_is_pairing(const bdaddr_t *addr);
diff --git a/repo/android/bluetoothd-snoop.c b/repo/android/bluetoothd-snoop.c
new file mode 100644
index 0000000..7526782
--- /dev/null
+++ b/repo/android/bluetoothd-snoop.c
@@ -0,0 +1,262 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#if defined(ANDROID)
+#include <sys/capability.h>
+#endif
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/mgmt.h"
+
+#include "src/shared/mainloop.h"
+#include "src/shared/btsnoop.h"
+#include "src/log.h"
+
+#define DEFAULT_SNOOP_FILE "/sdcard/btsnoop_hci.log"
+
+static struct btsnoop *snoop = NULL;
+static uint8_t monitor_buf[BTSNOOP_MAX_PACKET_SIZE];
+static int monitor_fd = -1;
+
+static void signal_callback(int signum, void *user_data)
+{
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		mainloop_quit();
+		break;
+	}
+}
+
+static uint32_t get_flags_from_opcode(uint16_t opcode)
+{
+	switch (opcode) {
+	case BTSNOOP_OPCODE_NEW_INDEX:
+	case BTSNOOP_OPCODE_DEL_INDEX:
+		break;
+	case BTSNOOP_OPCODE_COMMAND_PKT:
+		return 0x02;
+	case BTSNOOP_OPCODE_EVENT_PKT:
+		return 0x03;
+	case BTSNOOP_OPCODE_ACL_TX_PKT:
+		return 0x00;
+	case BTSNOOP_OPCODE_ACL_RX_PKT:
+		return 0x01;
+	case BTSNOOP_OPCODE_SCO_TX_PKT:
+	case BTSNOOP_OPCODE_SCO_RX_PKT:
+		break;
+	case BTSNOOP_OPCODE_OPEN_INDEX:
+	case BTSNOOP_OPCODE_CLOSE_INDEX:
+		break;
+	}
+
+	return 0xff;
+}
+
+static void data_callback(int fd, uint32_t events, void *user_data)
+{
+	unsigned char control[32];
+	struct mgmt_hdr hdr;
+	struct msghdr msg;
+	struct iovec iov[2];
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(monitor_fd);
+		return;
+	}
+
+	iov[0].iov_base = &hdr;
+	iov[0].iov_len = MGMT_HDR_SIZE;
+	iov[1].iov_base = monitor_buf;
+	iov[1].iov_len = sizeof(monitor_buf);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 2;
+	msg.msg_control = control;
+	msg.msg_controllen = sizeof(control);
+
+	while (true) {
+		struct cmsghdr *cmsg;
+		struct timeval *tv = NULL;
+		struct timeval ctv;
+		uint16_t opcode, index, pktlen;
+		uint32_t flags;
+		ssize_t len;
+
+		len = recvmsg(monitor_fd, &msg, MSG_DONTWAIT);
+		if (len < 0)
+			break;
+
+		if (len < MGMT_HDR_SIZE)
+			break;
+
+		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+					cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+			if (cmsg->cmsg_level != SOL_SOCKET)
+				continue;
+
+			if (cmsg->cmsg_type == SCM_TIMESTAMP) {
+				memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv));
+				tv = &ctv;
+			}
+		}
+
+		opcode = btohs(hdr.opcode);
+		index  = btohs(hdr.index);
+		pktlen = btohs(hdr.len);
+
+		if (index)
+			continue;
+
+		flags = get_flags_from_opcode(opcode);
+		if (flags != 0xff)
+			btsnoop_write(snoop, tv, flags, monitor_buf, pktlen);
+	}
+}
+
+static int open_monitor(const char *path)
+{
+	struct sockaddr_hci addr;
+	int opt = 1;
+
+	snoop = btsnoop_create(path, BTSNOOP_FORMAT_HCI);
+	if (!snoop)
+		return -1;
+
+	monitor_fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+	if (monitor_fd < 0)
+		goto failed;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.hci_family = AF_BLUETOOTH;
+	addr.hci_dev = HCI_DEV_NONE;
+	addr.hci_channel = HCI_CHANNEL_MONITOR;
+
+	if (bind(monitor_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+		goto failed_close;
+
+	if (setsockopt(monitor_fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))
+									< 0)
+		goto failed_close;
+
+	mainloop_add_fd(monitor_fd, EPOLLIN, data_callback, NULL, NULL);
+
+	return 0;
+
+failed_close:
+	close(monitor_fd);
+	monitor_fd = -1;
+
+failed:
+	btsnoop_unref(snoop);
+	snoop = NULL;
+
+	return -1;
+}
+
+static void close_monitor(void)
+{
+	btsnoop_unref(snoop);
+	snoop = NULL;
+
+	close(monitor_fd);
+	monitor_fd = -1;
+}
+
+static void set_capabilities(void)
+{
+#if defined(ANDROID)
+	struct __user_cap_header_struct header;
+	struct __user_cap_data_struct cap;
+
+	header.version = _LINUX_CAPABILITY_VERSION;
+	header.pid = 0;
+
+	/*
+	 * CAP_NET_RAW: for snooping
+	 * CAP_DAC_READ_SEARCH: override path search permissions
+	 */
+	cap.effective = cap.permitted =
+		CAP_TO_MASK(CAP_NET_RAW) |
+		CAP_TO_MASK(CAP_DAC_READ_SEARCH);
+	cap.inheritable = 0;
+
+	/* TODO: Move to cap_set_proc once bionic support it */
+	if (capset(&header, &cap) < 0)
+		exit(EXIT_FAILURE);
+#endif
+}
+
+int main(int argc, char *argv[])
+{
+	const char *path;
+	sigset_t mask;
+
+	__btd_log_init(NULL, 0);
+
+	DBG("");
+
+	set_capabilities();
+
+	if (argc > 1)
+		path = argv[1];
+	else
+		path = DEFAULT_SNOOP_FILE;
+
+	mainloop_init();
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+	if (!strcmp(DEFAULT_SNOOP_FILE, path))
+		rename(DEFAULT_SNOOP_FILE, DEFAULT_SNOOP_FILE ".old");
+
+	if (open_monitor(path) < 0) {
+		error("bluetoothd_snoop: start failed");
+		return EXIT_FAILURE;
+	}
+
+	info("bluetoothd_snoop: started");
+
+	mainloop_run();
+
+	close_monitor();
+
+	info("bluetoothd_snoop: stopped");
+
+	__btd_log_cleanup();
+
+	return EXIT_SUCCESS;
+}
diff --git a/repo/android/bluetoothd-wrapper.c b/repo/android/bluetoothd-wrapper.c
new file mode 100644
index 0000000..7f668da
--- /dev/null
+++ b/repo/android/bluetoothd-wrapper.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#include <cutils/properties.h>
+
+#include "hal-utils.h"
+
+#define VALGRIND_BIN "/system/bin/valgrind"
+
+#define BLUETOOTHD_BIN "/system/bin/bluetoothd-main"
+
+static void run_valgrind(int debug, int mgmt_dbg)
+{
+	char *prg_argv[7];
+	char *prg_envp[3];
+
+	prg_argv[0] = VALGRIND_BIN;
+	prg_argv[1] = "--leak-check=full";
+	prg_argv[2] = "--track-origins=yes";
+	prg_argv[3] = BLUETOOTHD_BIN;
+	prg_argv[4] = debug ? "-d" : NULL;
+	prg_argv[5] = mgmt_dbg ? "--mgmt-debug" : NULL;
+	prg_argv[6] = NULL;
+
+	prg_envp[0] = "G_SLICE=always-malloc";
+	prg_envp[1] = "G_DEBUG=gc-friendly";
+	prg_envp[2] = NULL;
+
+	execve(prg_argv[0], prg_argv, prg_envp);
+}
+
+static void run_bluetoothd(int debug, int mgmt_dbg)
+{
+	char *prg_argv[4];
+	char *prg_envp[1];
+
+	prg_argv[0] = BLUETOOTHD_BIN;
+	prg_argv[1] = debug ? "-d" : NULL;
+	prg_argv[2] = mgmt_dbg ? "--mgmt-debug" : NULL;
+	prg_argv[3] = NULL;
+
+	prg_envp[0] = NULL;
+
+	execve(prg_argv[0], prg_argv, prg_envp);
+}
+
+int main(int argc, char *argv[])
+{
+	char value[PROPERTY_VALUE_MAX];
+	int debug = 0;
+	int mgmt_dbg = 0;
+
+	if (get_config("debug", value, NULL) > 0 &&
+			(!strcasecmp(value, "true") || atoi(value) > 0))
+		debug = 1;
+
+	if (get_config("mgmtdbg", value, NULL) > 0 &&
+			(!strcasecmp(value, "true") || atoi(value) > 0)) {
+		debug = 1;
+		mgmt_dbg = 1;
+	}
+
+	if (get_config("valgrind", value, NULL) > 0 &&
+			(!strcasecmp(value, "true") || atoi(value) > 0))
+		run_valgrind(debug, mgmt_dbg);
+
+	/*
+	 * In case we failed to execute Valgrind, try to run bluetoothd
+	 * without it
+	 */
+	run_bluetoothd(debug, mgmt_dbg);
+
+	return 0;
+}
diff --git a/repo/android/bluetoothd.te b/repo/android/bluetoothd.te
new file mode 100644
index 0000000..532bfbb
--- /dev/null
+++ b/repo/android/bluetoothd.te
@@ -0,0 +1,47 @@
+type bluetoothd, domain;
+type bluetoothd_exec, exec_type, file_type;
+type bluetoothd_main_exec, exec_type, file_type;
+
+# Start bluetoothd from init
+init_daemon_domain(bluetoothd)
+
+# Data file accesses
+allow bluetoothd bluetooth_data_file:dir w_dir_perms;
+allow bluetoothd bluetooth_data_file:notdevfile_class_set create_file_perms;
+
+allow bluetoothd self:capability { setuid net_admin net_bind_service net_raw };
+allow bluetoothd kernel:system module_request;
+
+# TODO: this may be romoved for userbuild where we don't use bluetoothd_wrapper
+allow bluetoothd bluetoothd_main_exec:file { execute execute_no_trans read open };
+
+# IPC socket communication
+allow bluetoothd self:socket { create_socket_perms accept listen setopt getopt };
+
+# Allow clients to use a socket provided by the bluetooth app.
+allow bluetoothd { bluetooth mediaserver }:unix_stream_socket connectto;
+
+# Allow system app to use sockets and fds
+allow bluetooth bluetoothd:fd use;
+allow bluetooth bluetoothd:unix_stream_socket rw_socket_perms;
+
+# Allow user bluetooth apps to use sockets and fds
+allow bluetoothdomain bluetoothd:fd use;
+allow bluetoothdomain bluetoothd:unix_stream_socket { getopt setopt getattr read write ioctl shutdown };
+
+# Other domains that can create and use bluetooth sockets.
+allow bluetoothdomain self:socket create_socket_perms;
+
+#This we might should put to mediaserver.te ?
+allow mediaserver bluetoothd:fd use;
+allow mediaserver bluetoothd:socket rw_socket_perms;
+
+# needs /system/bin/log access
+allow bluetoothd devpts:chr_file rw_file_perms;
+
+# access to uhid device
+allow bluetoothd uhid_device:chr_file rw_file_perms;
+
+# tethering
+allow bluetoothd self:udp_socket create_socket_perms;
+allow bluetoothd self:tcp_socket { create ioctl };
diff --git a/repo/android/bluetoothd_snoop.te b/repo/android/bluetoothd_snoop.te
new file mode 100644
index 0000000..ef817b5
--- /dev/null
+++ b/repo/android/bluetoothd_snoop.te
@@ -0,0 +1,17 @@
+type bluetoothd_snoop, domain;
+type bluetoothd_snoop_exec, exec_type, file_type;
+
+# Start bluetoothd_snoop from init
+init_daemon_domain(bluetoothd_snoop)
+
+# directory search and read caps
+allow bluetoothd_snoop self:capability dac_read_search;
+# use raw and packet sockets caps
+allow bluetoothd_snoop self:capability net_raw;
+
+# monitor socket access
+allow bluetoothd_snoop self:socket { create bind setopt read };
+
+# sdcard access
+allow bluetoothd_snoop fuse:dir w_dir_perms;
+allow bluetoothd_snoop fuse:file create_file_perms;
diff --git a/repo/android/client/haltest.c b/repo/android/client/haltest.c
new file mode 100644
index 0000000..92cd811
--- /dev/null
+++ b/repo/android/client/haltest.c
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <poll.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "if-main.h"
+#include "terminal.h"
+#include "pollhandler.h"
+#include "history.h"
+
+static void process_line(char *line_buffer);
+
+const struct interface *interfaces[] = {
+	&audio_if,
+	&sco_if,
+	&bluetooth_if,
+	&av_if,
+	&rc_if,
+	&gatt_if,
+	&gatt_client_if,
+	&gatt_server_if,
+	&hf_if,
+	&hh_if,
+	&pan_if,
+	&hl_if,
+	&sock_if,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	&hf_client_if,
+	&mce_if,
+	&ctrl_rc_if,
+	&av_sink_if,
+#endif
+	NULL
+};
+
+static struct method commands[];
+
+struct method *get_method(struct method *methods, const char *name)
+{
+	while (strcmp(methods->name, "") != 0) {
+		if (strcmp(methods->name, name) == 0)
+			return methods;
+		methods++;
+	}
+
+	return NULL;
+}
+
+/* function returns interface of given name or NULL if not found */
+const struct interface *get_interface(const char *name)
+{
+	int i;
+
+	for (i = 0; interfaces[i] != NULL; ++i) {
+		if (strcmp(interfaces[i]->name, name) == 0)
+			break;
+	}
+
+	return interfaces[i];
+}
+
+int haltest_error(const char *format, ...)
+{
+	va_list args;
+	int ret;
+	va_start(args, format);
+	ret = terminal_vprint(format, args);
+	va_end(args);
+	return ret;
+}
+
+int haltest_info(const char *format, ...)
+{
+	va_list args;
+	int ret;
+	va_start(args, format);
+	ret = terminal_vprint(format, args);
+	va_end(args);
+	return ret;
+}
+
+int haltest_warn(const char *format, ...)
+{
+	va_list args;
+	int ret;
+	va_start(args, format);
+	ret = terminal_vprint(format, args);
+	va_end(args);
+	return ret;
+}
+
+static void help_print_interface(const struct interface *i)
+{
+	struct method *m;
+
+	for (m = i->methods; strcmp(m->name, "") != 0; m++)
+		haltest_info("%s %s %s\n", i->name, m->name,
+						(m->help ? m->help : ""));
+}
+
+/* Help completion */
+static void help_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 2)
+		*enum_func = interface_name;
+}
+
+/* Help execution */
+static void help_p(int argc, const char **argv)
+{
+	const struct method *m = commands;
+	const struct interface **ip = interfaces;
+	const struct interface *i;
+
+	if (argc == 1) {
+		terminal_print("haltest allows to call Android HAL methods.\n");
+		terminal_print("\nAvailable commands:\n");
+		while (0 != strcmp(m->name, "")) {
+			terminal_print("\t%s %s\n", m->name,
+						(m->help ? m->help : ""));
+			m++;
+		}
+
+		terminal_print("\nAvailable interfaces to use:\n");
+		while (NULL != *ip) {
+			terminal_print("\t%s\n", (*ip)->name);
+			ip++;
+		}
+
+		terminal_print("\nTo get help on methods for each interface type:\n");
+		terminal_print("\n\thelp <inerface>\n");
+		terminal_print("\nBasic scenario:\n\tbluetooth init\n");
+		terminal_print("\tbluetooth enable\n\tbluetooth start_discovery\n");
+		terminal_print("\tbluetooth get_profile_interface handsfree\n");
+		terminal_print("\thandsfree init\n\n");
+		return;
+	}
+
+	i = get_interface(argv[1]);
+	if (i == NULL) {
+		haltest_error("No such interface\n");
+		return;
+	}
+
+	help_print_interface(i);
+}
+
+/* quit/exit execution */
+static void quit_p(int argc, const char **argv)
+{
+	char cleanup_audio[] = "audio cleanup";
+
+	close_hw_bt_dev();
+	process_line(cleanup_audio);
+
+	exit(0);
+}
+
+static int fd_stack[10];
+static int fd_stack_pointer = 0;
+
+static void stdin_handler(struct pollfd *pollfd);
+
+static void process_file(const char *name)
+{
+	int fd = open(name, O_RDONLY);
+
+	if (fd < 0) {
+		haltest_error("Can't open file: %s for reading\n", name);
+		return;
+	}
+
+	if (fd_stack_pointer >= 10) {
+		haltest_error("To many open files\n");
+		close(fd);
+		return;
+	}
+
+	fd_stack[fd_stack_pointer++] = fd;
+	poll_unregister_fd(fd_stack[fd_stack_pointer - 2], stdin_handler);
+	poll_register_fd(fd_stack[fd_stack_pointer - 1], POLLIN, stdin_handler);
+}
+
+static void source_p(int argc, const char **argv)
+{
+	if (argc < 2) {
+		haltest_error("No file specified");
+		return;
+	}
+
+	process_file(argv[1]);
+}
+
+/* Commands available without interface */
+static struct method commands[] = {
+	STD_METHODCH(help, "[<interface>]"),
+	STD_METHOD(quit),
+	METHOD("exit", quit_p, NULL, NULL),
+	STD_METHODH(source, "<file>"),
+	END_METHOD
+};
+
+/* Gets comman by name */
+struct method *get_command(const char *name)
+{
+	return get_method(commands, name);
+}
+
+/* Function to enumerate interface names */
+const char *interface_name(void *v, int i)
+{
+	return interfaces[i] ? interfaces[i]->name : NULL;
+}
+
+/* Function to enumerate command and interface names */
+const char *command_name(void *v, int i)
+{
+	int cmd_cnt = NELEM(commands);
+
+	if (i >= cmd_cnt)
+		return interface_name(v, i - cmd_cnt);
+	else
+		return commands[i].name;
+}
+
+/*
+ * This function changes input parameter line_buffer so it has
+ * null termination after each token (due to strtok)
+ * Output argv is filled with pointers to arguments
+ * returns number of tokens parsed - argc
+ */
+static int command_line_to_argv(char *line_buffer, char *argv[], int argv_size)
+{
+	static const char *token_breaks = "\r\n\t ";
+	char *token;
+	int argc = 0;
+
+	token = strtok(line_buffer, token_breaks);
+	while (token != NULL && argc < (int) argv_size) {
+		argv[argc++] = token;
+		token = strtok(NULL, token_breaks);
+	}
+
+	return argc;
+}
+
+static void process_line(char *line_buffer)
+{
+	char *argv[50];
+	int argc;
+	int i = 0;
+	struct method *m;
+
+	argc = command_line_to_argv(line_buffer, argv, 50);
+	if (argc < 1)
+		return;
+
+	while (interfaces[i] != NULL) {
+		if (strcmp(interfaces[i]->name, argv[0])) {
+			i++;
+			continue;
+		}
+
+		if (argc < 2 || strcmp(argv[1], "?") == 0) {
+			help_print_interface(interfaces[i]);
+			return;
+		}
+
+		m = get_method(interfaces[i]->methods, argv[1]);
+		if (m != NULL) {
+			m->func(argc, (const char **) argv);
+			return;
+		}
+
+		haltest_error("No function %s found\n", argv[1]);
+		return;
+	}
+	/* No interface, try commands */
+	m = get_command(argv[0]);
+	if (m == NULL)
+		haltest_error("No such command %s\n", argv[0]);
+	else
+		m->func(argc, (const char **) argv);
+}
+
+/* called when there is something on stdin */
+static void stdin_handler(struct pollfd *pollfd)
+{
+	char buf[10];
+
+	if (pollfd->revents & POLLIN) {
+		int count = read(fd_stack[fd_stack_pointer - 1], buf, 10);
+
+		if (count > 0) {
+			int i;
+
+			for (i = 0; i < count; ++i)
+				terminal_process_char(buf[i], process_line);
+			return;
+		}
+	}
+
+	if (fd_stack_pointer > 1)
+		poll_register_fd(fd_stack[fd_stack_pointer - 2], POLLIN,
+								stdin_handler);
+	if (fd_stack_pointer > 0) {
+		poll_unregister_fd(fd_stack[--fd_stack_pointer], stdin_handler);
+
+		if (fd_stack[fd_stack_pointer])
+			close(fd_stack[fd_stack_pointer]);
+	}
+}
+
+static void usage(void)
+{
+	printf("haltest Android Bluetooth HAL testing tool\n"
+		"Usage:\n");
+	printf("\thaltest [options]\n");
+	printf("options:\n"
+		"\t-i  --ivi              Initialize only IVI interfaces\n"
+		"\t-n, --no-init          Don't initialize any interfaces\n"
+		"\t-v  --version          Print version\n"
+		"\t-h, --help             Show help options\n");
+}
+
+static void print_version(void)
+{
+	printf("haltest version %s\n", VERSION);
+}
+
+static const struct option main_options[] = {
+	{ "no-init", no_argument, NULL, 'n' },
+	{ "ivi",     no_argument, NULL, 'i' },
+	{ "help",    no_argument, NULL, 'h' },
+	{ "version", no_argument, NULL, 'v' },
+	{ NULL }
+};
+
+static bool no_init = false;
+static bool ivi_only = false;
+
+static void parse_command_line(int argc, char *argv[])
+{
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "inhv", main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'n':
+			no_init = true;
+			break;
+		case 'i':
+			ivi_only = true;
+			break;
+		case 'h':
+			usage();
+			exit(0);
+		case 'v':
+			print_version();
+			exit(0);
+		default:
+			putchar('\n');
+			exit(-1);
+			break;
+		}
+	}
+}
+
+static const char * const interface_names[] = {
+	BT_PROFILE_HANDSFREE_ID,
+	BT_PROFILE_ADVANCED_AUDIO_ID,
+	BT_PROFILE_AV_RC_ID,
+	BT_PROFILE_HEALTH_ID,
+	BT_PROFILE_HIDHOST_ID,
+	BT_PROFILE_PAN_ID,
+	BT_PROFILE_GATT_ID,
+	BT_PROFILE_SOCKETS_ID,
+	NULL
+};
+
+static const char * const ivi_interface_inames[] = {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	BT_PROFILE_HANDSFREE_CLIENT_ID,
+	BT_PROFILE_MAP_CLIENT_ID,
+	BT_PROFILE_AV_RC_CTRL_ID,
+		BT_PROFILE_ADVANCED_AUDIO_SINK_ID,
+#endif
+	NULL
+};
+
+static void init(const char * const *inames)
+{
+	const struct method *m;
+	const char *argv[4];
+	char init_audio[] = "audio init";
+	char init_sco[] = "sco init";
+	char init_bt[] = "bluetooth init";
+	uint32_t i;
+
+	process_line(init_audio);
+	process_line(init_sco);
+	process_line(init_bt);
+
+	m = get_interface_method("bluetooth", "get_profile_interface");
+
+	while (*inames) {
+		argv[2] = *inames;
+		m->func(3, argv);
+		inames++;
+	}
+
+	/* Init what is available to init */
+	for (i = 3; i < NELEM(interfaces) - 1; ++i) {
+		m = get_interface_method(interfaces[i]->name, "init");
+		if (m != NULL)
+			m->func(2, argv);
+	}
+}
+
+int main(int argc, char **argv)
+{
+	struct stat rcstat;
+
+	parse_command_line(argc, argv);
+
+	terminal_setup();
+
+	if (!no_init) {
+		if (ivi_only)
+			init(ivi_interface_inames);
+		else
+			init(interface_names);
+	}
+
+	history_restore(".haltest_history");
+
+	fd_stack[fd_stack_pointer++] = 0;
+	/* Register command line handler */
+	poll_register_fd(0, POLLIN, stdin_handler);
+
+	if (stat(".haltestrc", &rcstat) == 0 && (rcstat.st_mode & S_IFREG) != 0)
+		process_file(".haltestrc");
+
+	poll_dispatch_loop();
+
+	return 0;
+}
diff --git a/repo/android/client/history.c b/repo/android/client/history.c
new file mode 100644
index 0000000..ca4664c
--- /dev/null
+++ b/repo/android/client/history.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "history.h"
+
+/* Very simple history storage for easy usage of tool */
+
+#define HISTORY_DEPTH 40
+#define LINE_SIZE 200
+static char lines[HISTORY_DEPTH][LINE_SIZE];
+static int last_line = 0;
+static int history_size = 0;
+
+/* TODO: Storing history not implemented yet */
+void history_store(const char *filename)
+{
+}
+
+/* Restoring history from file */
+void history_restore(const char *filename)
+{
+	char line[1000];
+	FILE *f = fopen(filename, "rt");
+
+	if (f == NULL)
+		return;
+
+	for (;;) {
+		if (fgets(line, 1000, f) != NULL) {
+			int l = strlen(line);
+
+			while (l > 0 && isspace(line[--l]))
+				line[l] = 0;
+
+			if (l > 0)
+				history_add_line(line);
+		} else
+			break;
+	}
+
+	fclose(f);
+}
+
+/* Add new line to history buffer */
+void history_add_line(const char *line)
+{
+	if (line == NULL || strlen(line) == 0)
+		return;
+
+	if (strcmp(line, lines[last_line]) == 0)
+		return;
+
+	last_line = (last_line + 1) % HISTORY_DEPTH;
+	strncpy(&lines[last_line][0], line, LINE_SIZE - 1);
+	if (history_size < HISTORY_DEPTH)
+		history_size++;
+}
+
+/*
+ * Get n-th line from history
+ * 0 - means latest
+ * -1 - means oldest
+ * return -1 if there is no such line
+ */
+int history_get_line(int n, char *buf, int buf_size)
+{
+	if (n == -1)
+		n = history_size - 1;
+
+	if (n >= history_size || buf_size == 0 || n < 0)
+		return -1;
+
+	strncpy(buf,
+		&lines[(HISTORY_DEPTH + last_line - n) % HISTORY_DEPTH][0],
+		buf_size - 1);
+	buf[buf_size - 1] = 0;
+
+	return n;
+}
diff --git a/repo/android/client/history.h b/repo/android/client/history.h
new file mode 100644
index 0000000..26085b5
--- /dev/null
+++ b/repo/android/client/history.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+void history_store(const char *filename);
+void history_restore(const char *filename);
+void history_add_line(const char *line);
+int history_get_line(int n, char *buf, int buf_size);
diff --git a/repo/android/client/if-audio.c b/repo/android/client/if-audio.c
new file mode 100644
index 0000000..65e2f2f
--- /dev/null
+++ b/repo/android/client/if-audio.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <pthread.h>
+#include <unistd.h>
+#include <math.h>
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+audio_hw_device_t *if_audio = NULL;
+static struct audio_stream_out *stream_out = NULL;
+
+static size_t buffer_size = 0;
+static pthread_t play_thread = 0;
+static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+enum state {
+	STATE_STOPPED,
+	STATE_STOPPING,
+	STATE_PLAYING,
+	STATE_SUSPENDED,
+	STATE_MAX
+};
+
+SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)")
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_LOW_FREQUENCY),
+	DELEMENT(AUDIO_CHANNEL_OUT_BACK_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_BACK_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_BACK_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_SIDE_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_SIDE_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_MONO),
+	DELEMENT(AUDIO_CHANNEL_OUT_STEREO),
+	DELEMENT(AUDIO_CHANNEL_OUT_QUAD),
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+	DELEMENT(AUDIO_CHANNEL_OUT_SURROUND),
+#else
+	DELEMENT(AUDIO_CHANNEL_OUT_QUAD_BACK),
+	DELEMENT(AUDIO_CHANNEL_OUT_QUAD_SIDE),
+	DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_BACK),
+	DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_SIDE),
+#endif
+	DELEMENT(AUDIO_CHANNEL_OUT_5POINT1),
+	DELEMENT(AUDIO_CHANNEL_OUT_7POINT1),
+	DELEMENT(AUDIO_CHANNEL_OUT_ALL),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ENDMAP
+
+SINTMAP(audio_format_t, -1, "(AUDIO_FORMAT_INVALID)")
+	DELEMENT(AUDIO_FORMAT_DEFAULT),
+	DELEMENT(AUDIO_FORMAT_PCM),
+	DELEMENT(AUDIO_FORMAT_MP3),
+	DELEMENT(AUDIO_FORMAT_AMR_NB),
+	DELEMENT(AUDIO_FORMAT_AMR_WB),
+	DELEMENT(AUDIO_FORMAT_AAC),
+	DELEMENT(AUDIO_FORMAT_HE_AAC_V1),
+	DELEMENT(AUDIO_FORMAT_HE_AAC_V2),
+	DELEMENT(AUDIO_FORMAT_VORBIS),
+	DELEMENT(AUDIO_FORMAT_MAIN_MASK),
+	DELEMENT(AUDIO_FORMAT_SUB_MASK),
+	DELEMENT(AUDIO_FORMAT_PCM_16_BIT),
+	DELEMENT(AUDIO_FORMAT_PCM_8_BIT),
+	DELEMENT(AUDIO_FORMAT_PCM_32_BIT),
+	DELEMENT(AUDIO_FORMAT_PCM_8_24_BIT),
+ENDMAP
+
+static int current_state = STATE_STOPPED;
+
+#define SAMPLERATE 44100
+static short sample[SAMPLERATE];
+static uint16_t sample_pos;
+
+static void init_p(int argc, const char **argv)
+{
+	int err;
+	const hw_module_t *module;
+	audio_hw_device_t *device;
+
+	err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID,
+					AUDIO_HARDWARE_MODULE_ID_A2DP, &module);
+	if (err) {
+		haltest_error("hw_get_module_by_class returned %d\n", err);
+		return;
+	}
+
+	err = audio_hw_device_open(module, &device);
+	if (err) {
+		haltest_error("audio_hw_device_open returned %d\n", err);
+		return;
+	}
+
+	if_audio = device;
+}
+
+static int feed_from_file(short *buffer, void *data)
+{
+	FILE *in = data;
+	return fread(buffer, buffer_size, 1, in);
+}
+
+static int feed_from_generator(short *buffer, void *data)
+{
+	size_t i = 0;
+	float volume = 0.5;
+	float *freq = data;
+	float f = 1;
+
+	if (freq)
+		f = *freq;
+
+	/* buffer_size is in bytes but we are using buffer of shorts (2 bytes)*/
+	for (i = 0; i < buffer_size / sizeof(*buffer) - 1;) {
+		if (sample_pos >= SAMPLERATE)
+			sample_pos = sample_pos % SAMPLERATE;
+
+		/* Use the same sample for both channels */
+		buffer[i++] = sample[sample_pos] * volume;
+		buffer[i++] = sample[sample_pos] * volume;
+
+		sample_pos += f;
+	}
+
+	return buffer_size;
+}
+
+static void prepare_sample(void)
+{
+	int x;
+	double s;
+
+	haltest_info("Preparing audio sample...\n");
+
+	for (x = 0; x < SAMPLERATE; x++) {
+		/* prepare sinusoidal 1Hz sample */
+		s = (2.0 * 3.14159) * ((double)x / SAMPLERATE);
+		s = sin(s);
+
+		/* remap <-1, 1> to signed 16bit PCM range */
+		sample[x] = s * 32767;
+	}
+
+	sample_pos = 0;
+}
+
+static void *playback_thread(void *data)
+{
+	int (*filbuff_cb) (short*, void*);
+	short buffer[buffer_size / sizeof(short)];
+	size_t len = 0;
+	ssize_t w_len = 0;
+	FILE *in = data;
+	void *cb_data = NULL;
+	float freq = 440.0;
+
+	/* Use file or fall back to generator */
+	if (in) {
+		filbuff_cb = feed_from_file;
+		cb_data = in;
+	} else {
+		prepare_sample();
+		filbuff_cb = feed_from_generator;
+		cb_data = &freq;
+	}
+
+	pthread_mutex_lock(&state_mutex);
+	current_state = STATE_PLAYING;
+	pthread_mutex_unlock(&state_mutex);
+
+	do {
+		pthread_mutex_lock(&state_mutex);
+
+		if (current_state == STATE_STOPPING) {
+			pthread_mutex_unlock(&state_mutex);
+			break;
+		} else if (current_state == STATE_SUSPENDED) {
+			pthread_mutex_unlock(&state_mutex);
+			usleep(500);
+			continue;
+		}
+
+		pthread_mutex_unlock(&state_mutex);
+
+		len = filbuff_cb(buffer, cb_data);
+
+		pthread_mutex_lock(&outstream_mutex);
+		if (!stream_out) {
+			pthread_mutex_unlock(&outstream_mutex);
+			break;
+		}
+
+		w_len = stream_out->write(stream_out, buffer, buffer_size);
+		pthread_mutex_unlock(&outstream_mutex);
+	} while (len && w_len > 0);
+
+	if (in)
+		fclose(in);
+
+	pthread_mutex_lock(&state_mutex);
+	current_state = STATE_STOPPED;
+	pthread_mutex_unlock(&state_mutex);
+
+	haltest_info("Done playing.\n");
+
+	return NULL;
+}
+
+static void play_p(int argc, const char **argv)
+{
+	const char *fname = NULL;
+	FILE *in = NULL;
+
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3) {
+		haltest_error("Invalid audio file path.\n");
+		haltest_info("Using sound generator.\n");
+	} else {
+		fname = argv[2];
+		in = fopen(fname, "r");
+
+		if (in == NULL) {
+			haltest_error("Cannot open file: %s\n", fname);
+			return;
+		}
+		haltest_info("Playing file: %s\n", fname);
+	}
+
+	if (buffer_size == 0) {
+		haltest_error("Invalid buffer size. Was stream_out opened?\n");
+		goto fail;
+	}
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_STOPPED) {
+		haltest_error("Already playing or stream suspended!\n");
+		pthread_mutex_unlock(&state_mutex);
+		goto fail;
+	}
+	pthread_mutex_unlock(&state_mutex);
+
+	if (pthread_create(&play_thread, NULL, playback_thread, in) != 0) {
+		haltest_error("Cannot create playback thread!\n");
+		goto fail;
+	}
+
+	return;
+fail:
+	if (in)
+		fclose(in);
+}
+
+static void stop_p(int argc, const char **argv)
+{
+	pthread_mutex_lock(&state_mutex);
+	if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) {
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+
+	current_state = STATE_STOPPING;
+	pthread_mutex_unlock(&state_mutex);
+
+	pthread_mutex_lock(&outstream_mutex);
+	stream_out->common.standby(&stream_out->common);
+	pthread_mutex_unlock(&outstream_mutex);
+}
+
+static void open_output_stream_p(int argc, const char **argv)
+{
+	int err;
+
+	RETURN_IF_NULL(if_audio);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state == STATE_PLAYING) {
+		haltest_error("Already playing!\n");
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+	pthread_mutex_unlock(&state_mutex);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	err = if_audio->open_output_stream(if_audio,
+						0,
+						AUDIO_DEVICE_OUT_ALL_A2DP,
+						AUDIO_OUTPUT_FLAG_NONE,
+						NULL,
+						&stream_out, NULL);
+#else
+	err = if_audio->open_output_stream(if_audio,
+						0,
+						AUDIO_DEVICE_OUT_ALL_A2DP,
+						AUDIO_OUTPUT_FLAG_NONE,
+						NULL,
+						&stream_out);
+#endif
+	if (err < 0) {
+		haltest_error("open output stream returned %d\n", err);
+		return;
+	}
+
+	buffer_size = stream_out->common.get_buffer_size(&stream_out->common);
+	if (buffer_size == 0)
+		haltest_error("Invalid buffer size received!\n");
+	else
+		haltest_info("Using buffer size: %zu\n", buffer_size);
+}
+
+static void close_output_stream_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	stop_p(argc, argv);
+
+	haltest_info("Waiting for playback thread...\n");
+	pthread_join(play_thread, NULL);
+
+	if_audio->close_output_stream(if_audio, stream_out);
+
+	stream_out = NULL;
+	buffer_size = 0;
+}
+
+static void cleanup_p(int argc, const char **argv)
+{
+	int err;
+
+	RETURN_IF_NULL(if_audio);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_STOPPED) {
+		pthread_mutex_unlock(&state_mutex);
+		close_output_stream_p(0, NULL);
+	} else {
+		pthread_mutex_unlock(&state_mutex);
+	}
+
+	err = audio_hw_device_close(if_audio);
+	if (err < 0) {
+		haltest_error("audio_hw_device_close returned %d\n", err);
+		return;
+	}
+
+	if_audio = NULL;
+}
+
+static void suspend_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_PLAYING) {
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+	current_state = STATE_SUSPENDED;
+	pthread_mutex_unlock(&state_mutex);
+
+	pthread_mutex_lock(&outstream_mutex);
+	stream_out->common.standby(&stream_out->common);
+	pthread_mutex_unlock(&outstream_mutex);
+}
+
+static void resume_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state == STATE_SUSPENDED)
+		current_state = STATE_PLAYING;
+	pthread_mutex_unlock(&state_mutex);
+}
+
+static void get_latency_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	haltest_info("Output audio stream latency: %d\n",
+					stream_out->get_latency(stream_out));
+}
+
+static void get_buffer_size_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	haltest_info("Current output buffer size: %zu\n",
+		stream_out->common.get_buffer_size(&stream_out->common));
+}
+
+static void get_channels_p(int argc, const char **argv)
+{
+	audio_channel_mask_t channels;
+
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	channels = stream_out->common.get_channels(&stream_out->common);
+
+	haltest_info("Channels: %s\n", audio_channel_mask_t2str(channels));
+}
+
+static void get_format_p(int argc, const char **argv)
+{
+	audio_format_t format;
+
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	format = stream_out->common.get_format(&stream_out->common);
+
+	haltest_info("Format: %s\n", audio_format_t2str(format));
+}
+
+static void get_sample_rate_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	haltest_info("Current sample rate: %d\n",
+		stream_out->common.get_sample_rate(&stream_out->common));
+}
+
+static void get_parameters_p(int argc, const char **argv)
+{
+	const char *keystr;
+
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3) {
+		haltest_info("No keys given.\n");
+		keystr = "";
+	} else {
+		keystr = argv[2];
+	}
+
+	haltest_info("Current parameters: %s\n",
+			stream_out->common.get_parameters(&stream_out->common,
+								keystr));
+}
+
+static void set_parameters_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3) {
+		haltest_error("No key=value; pairs given.\n");
+		return;
+	}
+
+	stream_out->common.set_parameters(&stream_out->common, argv[2]);
+}
+
+static void set_sample_rate_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3)
+		return;
+
+	stream_out->common.set_sample_rate(&stream_out->common, atoi(argv[2]));
+}
+
+static void init_check_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+
+	haltest_info("Init check result: %d\n", if_audio->init_check(if_audio));
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHOD(cleanup),
+	STD_METHOD(open_output_stream),
+	STD_METHOD(close_output_stream),
+	STD_METHODH(play, "<path to pcm file>"),
+	STD_METHOD(stop),
+	STD_METHOD(suspend),
+	STD_METHOD(resume),
+	STD_METHOD(get_latency),
+	STD_METHOD(get_buffer_size),
+	STD_METHOD(get_channels),
+	STD_METHOD(get_format),
+	STD_METHOD(get_sample_rate),
+	STD_METHODH(get_parameters, "<A2dpSuspended;closing>"),
+	STD_METHODH(set_parameters, "<A2dpSuspended=value;closing=value>"),
+	STD_METHODH(set_sample_rate, "<sample rate>"),
+	STD_METHOD(init_check),
+	END_METHOD
+};
+
+const struct interface audio_if = {
+	.name = "audio",
+	.methods = methods
+};
diff --git a/repo/android/client/if-av-sink.c b/repo/android/client/if-av-sink.c
new file mode 100644
index 0000000..3087dcf
--- /dev/null
+++ b/repo/android/client/if-av-sink.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const btav_interface_t *if_av_sink = NULL;
+
+SINTMAP(btav_connection_state_t, -1, "(unknown)")
+	DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTED),
+	DELEMENT(BTAV_CONNECTION_STATE_CONNECTING),
+	DELEMENT(BTAV_CONNECTION_STATE_CONNECTED),
+	DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(btav_audio_state_t, -1, "(unknown)")
+	DELEMENT(BTAV_AUDIO_STATE_REMOTE_SUSPEND),
+	DELEMENT(BTAV_AUDIO_STATE_STOPPED),
+	DELEMENT(BTAV_AUDIO_STATE_STARTED),
+ENDMAP
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+static void connection_state(btav_connection_state_t state,
+							bt_bdaddr_t *bd_addr)
+{
+	haltest_info("(sink) %s: connection_state=%s remote_bd_addr=%s\n",
+				__func__, btav_connection_state_t2str(state),
+				bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+static void audio_state(btav_audio_state_t state, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("(sink) %s: audio_state=%s remote_bd_addr=%s\n", __func__,
+					btav_audio_state_t2str(state),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+static void audio_config(bt_bdaddr_t *bd_addr, uint32_t sample_rate,
+							uint8_t channel_count) {
+	haltest_info("(sink) %s: addr=%s\n sample_rate=%d\n channel_count=%d\n",
+				__func__, bt_bdaddr_t2str(bd_addr, last_addr),
+				sample_rate, channel_count);
+}
+
+static btav_callbacks_t av_cbacks = {
+	.size = sizeof(av_cbacks),
+	.connection_state_cb = connection_state,
+	.audio_state_cb = audio_state,
+	.audio_config_cb = audio_config,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_av_sink);
+
+	EXEC(if_av_sink->init, &av_cbacks);
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+static void connect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_av_sink);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_av_sink->connect, &addr);
+}
+
+/* disconnect */
+
+static void disconnect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void disconnect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_av_sink);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_av_sink->disconnect, &addr);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_av_sink);
+
+	EXECV(if_av_sink->cleanup);
+	if_av_sink = NULL;
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(connect, "<addr>"),
+	STD_METHODCH(disconnect, "<addr>"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface av_sink_if = {
+	.name = "av-sink",
+	.methods = methods
+};
diff --git a/repo/android/client/if-av.c b/repo/android/client/if-av.c
new file mode 100644
index 0000000..85c641b
--- /dev/null
+++ b/repo/android/client/if-av.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const btav_interface_t *if_av = NULL;
+
+SINTMAP(btav_connection_state_t, -1, "(unknown)")
+	DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTED),
+	DELEMENT(BTAV_CONNECTION_STATE_CONNECTING),
+	DELEMENT(BTAV_CONNECTION_STATE_CONNECTED),
+	DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(btav_audio_state_t, -1, "(unknown)")
+	DELEMENT(BTAV_AUDIO_STATE_REMOTE_SUSPEND),
+	DELEMENT(BTAV_AUDIO_STATE_STOPPED),
+	DELEMENT(BTAV_AUDIO_STATE_STARTED),
+ENDMAP
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+static void connection_state(btav_connection_state_t state,
+							bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: connection_state=%s remote_bd_addr=%s\n", __func__,
+					btav_connection_state_t2str(state),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+static void audio_state(btav_audio_state_t state, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: audio_state=%s remote_bd_addr=%s\n", __func__,
+					btav_audio_state_t2str(state),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void audio_config(bt_bdaddr_t *bd_addr, uint32_t sample_rate,
+							uint8_t channel_count) {
+	haltest_info("%s: remote_addr=%s\n sample_rate=%d\n channel_count=%d\n",
+				__func__, bt_bdaddr_t2str(bd_addr, last_addr),
+				sample_rate, channel_count);
+}
+#endif
+
+static btav_callbacks_t av_cbacks = {
+	.size = sizeof(av_cbacks),
+	.connection_state_cb = connection_state,
+	.audio_state_cb = audio_state,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.audio_config_cb = audio_config,
+#endif
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_av);
+
+	EXEC(if_av->init, &av_cbacks);
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+static void connect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_av);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_av->connect, &addr);
+}
+
+/* disconnect */
+
+static void disconnect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void disconnect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_av);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_av->disconnect, &addr);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_av);
+
+	EXECV(if_av->cleanup);
+	if_av = NULL;
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(connect, "<addr>"),
+	STD_METHODCH(disconnect, "<addr>"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface av_if = {
+	.name = "av",
+	.methods = methods
+};
diff --git a/repo/android/client/if-bt.c b/repo/android/client/if-bt.c
new file mode 100644
index 0000000..c9acf6c
--- /dev/null
+++ b/repo/android/client/if-bt.c
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <string.h>
+#include <inttypes.h>
+
+#include "if-main.h"
+#include "terminal.h"
+#include "../hal-msg.h"
+#include "../hal-utils.h"
+
+static hw_device_t *bt_device;
+const bt_interface_t *if_bluetooth;
+
+#define VERIFY_PROP_TYPE_ARG(n, typ) \
+	do { \
+		if (n < argc) \
+			typ = str2btpropertytype(argv[n]); \
+		else { \
+			haltest_error("No property type specified\n"); \
+			return;\
+		} \
+	} while (0)
+
+static bt_scan_mode_t str2btscanmode(const char *str)
+{
+	bt_scan_mode_t v = str2bt_scan_mode_t(str);
+
+	if ((int) v != -1)
+		return v;
+
+	haltest_warn("WARN: %s cannot convert %s\n", __func__, str);
+	return (bt_scan_mode_t) atoi(str);
+}
+
+static bt_ssp_variant_t str2btsspvariant(const char *str)
+{
+	bt_ssp_variant_t v = str2bt_ssp_variant_t(str);
+
+	if ((int) v != -1)
+		return v;
+
+	haltest_warn("WARN: %s cannot convert %s\n", __func__, str);
+	return (bt_ssp_variant_t) atoi(str);
+}
+
+static bt_property_type_t str2btpropertytype(const char *str)
+{
+	bt_property_type_t v = str2bt_property_type_t(str);
+
+	if ((int) v != -1)
+		return v;
+
+	haltest_warn("WARN: %s cannot convert %s\n", __func__, str);
+	return (bt_property_type_t) atoi(str);
+}
+
+static void dump_properties(int num_properties, bt_property_t *properties)
+{
+	int i;
+
+	for (i = 0; i < num_properties; i++) {
+		/*
+		 * properities sometimes come unaligned hence memcp to
+		 * aligned buffer
+		 */
+		bt_property_t prop;
+		memcpy(&prop, properties + i, sizeof(prop));
+
+		haltest_info("prop: %s\n", btproperty2str(&prop));
+	}
+}
+
+/* Cache for remote devices, stored in sorted array */
+static bt_bdaddr_t *remote_devices = NULL;
+static int remote_devices_cnt = 0;
+static int remote_devices_capacity = 0;
+
+/* Adds address to remote device set so it can be used in tab completion */
+void add_remote_device(const bt_bdaddr_t *addr)
+{
+	int i;
+
+	if (remote_devices == NULL) {
+		remote_devices = malloc(4 * sizeof(bt_bdaddr_t));
+		remote_devices_cnt = 0;
+		if (remote_devices == NULL) {
+			remote_devices_capacity = 0;
+			return;
+		}
+
+		remote_devices_capacity = 4;
+	}
+
+	/* Array is sorted, search for right place */
+	for (i = 0; i < remote_devices_cnt; ++i) {
+		int res = memcmp(&remote_devices[i], addr, sizeof(*addr));
+
+		if (res == 0)
+			return; /* Already added */
+		else if (res > 0)
+			break;
+	}
+
+	/* Realloc space if needed */
+	if (remote_devices_cnt >= remote_devices_capacity) {
+		bt_bdaddr_t *tmp;
+
+		remote_devices_capacity *= 2;
+		/*
+		 * Save reference to previously allocated memory block so that
+		 * it can be freed in case realloc fails.
+		 */
+		tmp = remote_devices;
+
+		remote_devices = realloc(remote_devices, sizeof(bt_bdaddr_t) *
+						remote_devices_capacity);
+		if (remote_devices == NULL) {
+			free(tmp);
+			remote_devices_capacity = 0;
+			remote_devices_cnt = 0;
+			return;
+		}
+	}
+
+	if (i < remote_devices_cnt)
+		memmove(remote_devices + i + 1, remote_devices + i,
+				(remote_devices_cnt - i) * sizeof(bt_bdaddr_t));
+	remote_devices[i] = *addr;
+	remote_devices_cnt++;
+}
+
+const char *enum_devices(void *v, int i)
+{
+	static char buf[MAX_ADDR_STR_LEN];
+
+	if (i >= remote_devices_cnt)
+		return NULL;
+
+	bt_bdaddr_t2str(&remote_devices[i], buf);
+	return buf;
+}
+
+static void add_remote_device_from_props(int num_properties,
+						const bt_property_t *properties)
+{
+	int i;
+
+	for (i = 0; i < num_properties; i++) {
+		/*
+		 * properities sometimes come unaligned hence memcp to
+		 * aligned buffer
+		 */
+		bt_property_t property;
+
+		memcpy(&property, properties + i, sizeof(property));
+		if (property.type == BT_PROPERTY_BDADDR)
+			add_remote_device((bt_bdaddr_t *) property.val);
+	}
+}
+
+bool close_hw_bt_dev(void)
+{
+	if (!bt_device)
+		return false;
+
+	bt_device->close(bt_device);
+	return true;
+}
+
+static void adapter_state_changed_cb(bt_state_t state)
+{
+	haltest_info("%s: state=%s\n", __func__, bt_state_t2str(state));
+}
+
+static void adapter_properties_cb(bt_status_t status, int num_properties,
+						bt_property_t *properties)
+{
+	haltest_info("%s: status=%s num_properties=%d\n", __func__,
+				bt_status_t2str(status), num_properties);
+
+	dump_properties(num_properties, properties);
+}
+
+static void remote_device_properties_cb(bt_status_t status,
+					bt_bdaddr_t *bd_addr,
+					int num_properties,
+					bt_property_t *properties)
+{
+	haltest_info("%s: status=%s bd_addr=%s num_properties=%d\n", __func__,
+			bt_status_t2str(status), bdaddr2str(bd_addr),
+			num_properties);
+
+	add_remote_device(bd_addr);
+
+	dump_properties(num_properties, properties);
+}
+
+static void device_found_cb(int num_properties, bt_property_t *properties)
+{
+	haltest_info("%s: num_properties=%d\n", __func__, num_properties);
+
+	add_remote_device_from_props(num_properties, properties);
+
+	dump_properties(num_properties, properties);
+}
+
+static void discovery_state_changed_cb(bt_discovery_state_t state)
+{
+	haltest_info("%s: state=%s\n", __func__,
+					bt_discovery_state_t2str(state));
+}
+
+/*
+ * Buffer for remote addres that came from one of bind request.
+ * It's stored for command completion.
+ */
+static char last_remote_addr[MAX_ADDR_STR_LEN];
+static bt_ssp_variant_t last_ssp_variant = (bt_ssp_variant_t) -1;
+
+static bt_bdaddr_t pin_request_addr;
+static void pin_request_answer(char *reply)
+{
+	bt_pin_code_t pin;
+	int accept = 0;
+	int pin_len = strlen(reply);
+
+	if (pin_len > 0) {
+		accept = 1;
+		if (pin_len > 16)
+			pin_len = 16;
+		memcpy(&pin.pin, reply, pin_len);
+	}
+
+	EXEC(if_bluetooth->pin_reply, &pin_request_addr, accept, pin_len, &pin);
+}
+
+static void pin_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name,
+								uint32_t cod)
+{
+	/* Store for command completion */
+	bt_bdaddr_t2str(remote_bd_addr, last_remote_addr);
+	pin_request_addr = *remote_bd_addr;
+
+	haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x\n", __func__,
+					last_remote_addr, bd_name->name, cod);
+	terminal_prompt_for("Enter pin: ", pin_request_answer);
+}
+
+/* Variables to store information from ssp_request_cb used for ssp_reply */
+static bt_bdaddr_t ssp_request_addr;
+static bt_ssp_variant_t ssp_request_variant;
+static uint32_t ssp_request_pask_key;
+
+/* Called when user hit enter on prompt for confirmation */
+static void ssp_request_yes_no_answer(char *reply)
+{
+	int accept = *reply == 0 || *reply == 'y' || *reply == 'Y';
+
+	if_bluetooth->ssp_reply(&ssp_request_addr, ssp_request_variant, accept,
+							ssp_request_pask_key);
+}
+
+static void ssp_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name,
+				uint32_t cod, bt_ssp_variant_t pairing_variant,
+				uint32_t pass_key)
+{
+	static char prompt[50];
+
+	/* Store for command completion */
+	bt_bdaddr_t2str(remote_bd_addr, last_remote_addr);
+	last_ssp_variant = pairing_variant;
+
+	haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x pairing_variant=%s pass_key=%d\n",
+			__func__, last_remote_addr, bd_name->name, cod,
+			bt_ssp_variant_t2str(pairing_variant), pass_key);
+
+	switch (pairing_variant) {
+	case BT_SSP_VARIANT_PASSKEY_CONFIRMATION:
+		sprintf(prompt, "Does other device show %d [Y/n] ?", pass_key);
+
+		ssp_request_addr = *remote_bd_addr;
+		ssp_request_variant = pairing_variant;
+		ssp_request_pask_key = pass_key;
+
+		terminal_prompt_for(prompt, ssp_request_yes_no_answer);
+		break;
+	case BT_SSP_VARIANT_CONSENT:
+		sprintf(prompt, "Consent pairing [Y/n] ?");
+
+		ssp_request_addr = *remote_bd_addr;
+		ssp_request_variant = pairing_variant;
+		ssp_request_pask_key = 0;
+
+		terminal_prompt_for(prompt, ssp_request_yes_no_answer);
+		break;
+	case BT_SSP_VARIANT_PASSKEY_ENTRY:
+	case BT_SSP_VARIANT_PASSKEY_NOTIFICATION:
+	default:
+		haltest_info("Not automatically handled\n");
+		break;
+	}
+}
+
+static void bond_state_changed_cb(bt_status_t status,
+						bt_bdaddr_t *remote_bd_addr,
+						bt_bond_state_t state)
+{
+	haltest_info("%s: status=%s remote_bd_addr=%s state=%s\n", __func__,
+			bt_status_t2str(status), bdaddr2str(remote_bd_addr),
+			bt_bond_state_t2str(state));
+}
+
+static void acl_state_changed_cb(bt_status_t status,
+						bt_bdaddr_t *remote_bd_addr,
+						bt_acl_state_t state)
+{
+	haltest_info("%s: status=%s remote_bd_addr=%s state=%s\n", __func__,
+			bt_status_t2str(status), bdaddr2str(remote_bd_addr),
+			bt_acl_state_t2str(state));
+}
+
+static void thread_evt_cb(bt_cb_thread_evt evt)
+{
+	haltest_info("%s: evt=%s\n", __func__, bt_cb_thread_evt2str(evt));
+}
+
+static void dut_mode_recv_cb(uint16_t opcode, uint8_t *buf, uint8_t len)
+{
+	haltest_info("%s\n", __func__);
+}
+
+static void le_test_mode_cb(bt_status_t status, uint16_t num_packets)
+{
+	haltest_info("%s %s %d\n", __func__, bt_status_t2str(status),
+								num_packets);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void energy_info_cb(bt_activity_energy_info *energy_info)
+{
+	haltest_info("%s status=%s, ctrl_state=0x%02X, tx_time=0x%jx,"
+			"rx_time=0x%jx, idle_time=0x%jx, energu_used=0x%jx\n",
+			__func__, bt_status_t2str(energy_info->status),
+			energy_info->ctrl_state, energy_info->tx_time,
+			energy_info->rx_time, energy_info->idle_time,
+			energy_info->energy_used);
+}
+#endif
+
+static bt_callbacks_t bt_callbacks = {
+	.size = sizeof(bt_callbacks),
+	.adapter_state_changed_cb = adapter_state_changed_cb,
+	.adapter_properties_cb = adapter_properties_cb,
+	.remote_device_properties_cb = remote_device_properties_cb,
+	.device_found_cb = device_found_cb,
+	.discovery_state_changed_cb = discovery_state_changed_cb,
+	.pin_request_cb = pin_request_cb,
+	.ssp_request_cb = ssp_request_cb,
+	.bond_state_changed_cb = bond_state_changed_cb,
+	.acl_state_changed_cb = acl_state_changed_cb,
+	.thread_evt_cb = thread_evt_cb,
+	.dut_mode_recv_cb = dut_mode_recv_cb,
+	.le_test_mode_cb = le_test_mode_cb,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.energy_info_cb = energy_info_cb,
+#endif
+};
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static alarm_cb alarm_cb_p = NULL;
+static void *alarm_cb_p_data = NULL;
+
+static bool set_wake_alarm(uint64_t delay_millis, bool should_wake, alarm_cb cb,
+								void *data)
+{
+	haltest_info("%s: delay %"PRIu64" should_wake %u cb %p data %p\n",
+				__func__, delay_millis, should_wake, cb, data);
+
+	/* TODO call alarm callback after specified delay */
+	alarm_cb_p = cb;
+	alarm_cb_p_data = data;
+
+	return true;
+}
+
+static int acquire_wake_lock(const char *lock_name)
+{
+	haltest_info("%s: %s\n", __func__, lock_name);
+
+	return BT_STATUS_SUCCESS;
+}
+
+static int release_wake_lock(const char *lock_name)
+{
+	haltest_info("%s: %s\n", __func__, lock_name);
+
+	return BT_STATUS_SUCCESS;
+}
+
+static bt_os_callouts_t bt_os_callouts = {
+	.size = sizeof(bt_os_callouts),
+	.set_wake_alarm = set_wake_alarm,
+	.acquire_wake_lock = acquire_wake_lock,
+	.release_wake_lock = release_wake_lock,
+};
+#endif
+
+static void init_p(int argc, const char **argv)
+{
+	int err;
+	const hw_module_t *module;
+
+	err = hw_get_module(BT_HARDWARE_MODULE_ID, &module);
+	if (err) {
+		haltest_error("he_get_module returned %d\n", err);
+		return;
+	}
+
+	err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &bt_device);
+	if (err) {
+		haltest_error("module->methods->open returned %d\n", err);
+		return;
+	}
+
+	if_bluetooth =
+		((bluetooth_device_t *) bt_device)->get_bluetooth_interface();
+	if (!if_bluetooth) {
+		haltest_error("get_bluetooth_interface returned NULL\n");
+		return;
+	}
+
+	EXEC(if_bluetooth->init, &bt_callbacks);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	EXEC(if_bluetooth->set_os_callouts, &bt_os_callouts);
+#endif
+}
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_bluetooth);
+
+	EXECV(if_bluetooth->cleanup);
+
+	if_bluetooth = NULL;
+}
+
+static void enable_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_bluetooth);
+
+	EXEC(if_bluetooth->enable);
+}
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void read_energy_info_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_bluetooth);
+
+	EXEC(if_bluetooth->read_energy_info);
+}
+
+#define get_connection_state_c complete_addr_c
+
+static void get_connection_state_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_bluetooth);
+
+	VERIFY_ADDR_ARG(2, &addr);
+
+	haltest_info("if_bluetooth->get_connection_state : %d\n",
+				if_bluetooth->get_connection_state(&addr));
+}
+#endif
+
+static void disable_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_bluetooth);
+
+	EXEC(if_bluetooth->disable);
+}
+
+static void get_adapter_properties_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_bluetooth);
+
+	EXEC(if_bluetooth->get_adapter_properties);
+}
+
+static void get_adapter_property_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(bt_property_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void get_adapter_property_p(int argc, const char **argv)
+{
+	int type;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_PROP_TYPE_ARG(2, type);
+
+	EXEC(if_bluetooth->get_adapter_property, type);
+}
+
+static const char * const names[] = {
+	"BT_PROPERTY_BDNAME",
+	"BT_PROPERTY_ADAPTER_SCAN_MODE",
+	"BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT",
+	NULL
+};
+
+static void set_adapter_property_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = (void *) names;
+		*enum_func = enum_strings;
+	} else if (argc == 4) {
+		if (0 == strcmp(argv[2], "BT_PROPERTY_ADAPTER_SCAN_MODE")) {
+			*user = TYPE_ENUM(bt_scan_mode_t);
+			*enum_func = enum_defines;
+		}
+	}
+}
+
+static void set_adapter_property_p(int argc, const char **argv)
+{
+	bt_property_t property;
+	bt_scan_mode_t mode;
+	int timeout;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_PROP_TYPE_ARG(2, property.type);
+
+	if (argc <= 3) {
+		haltest_error("No property value specified\n");
+		return;
+	}
+	switch (property.type) {
+	case BT_PROPERTY_BDNAME:
+		property.len = strlen(argv[3]) + 1;
+		property.val = (char *) argv[3];
+		break;
+
+	case BT_PROPERTY_ADAPTER_SCAN_MODE:
+		mode = str2btscanmode(argv[3]);
+		property.len = sizeof(bt_scan_mode_t);
+		property.val = &mode;
+		break;
+
+	case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
+		timeout = atoi(argv[3]);
+		property.val = &timeout;
+		property.len = sizeof(timeout);
+		break;
+
+	case BT_PROPERTY_BDADDR:
+	case BT_PROPERTY_UUIDS:
+	case BT_PROPERTY_CLASS_OF_DEVICE:
+	case BT_PROPERTY_TYPE_OF_DEVICE:
+	case BT_PROPERTY_SERVICE_RECORD:
+	case BT_PROPERTY_ADAPTER_BONDED_DEVICES:
+	case BT_PROPERTY_REMOTE_FRIENDLY_NAME:
+	case BT_PROPERTY_REMOTE_RSSI:
+	case BT_PROPERTY_REMOTE_VERSION_INFO:
+	case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP:
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	case BT_PROPERTY_LOCAL_LE_FEATURES:
+#endif
+	default:
+		haltest_error("Invalid property %s\n", argv[3]);
+		return;
+	}
+
+	EXEC(if_bluetooth->set_adapter_property, &property);
+}
+
+/* This function is to be used for completion methods that need only address */
+static void complete_addr_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define get_remote_device_properties_c complete_addr_c
+
+static void get_remote_device_properties_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_bluetooth->get_remote_device_properties, &addr);
+}
+
+static void get_remote_device_property_c(int argc, const char **argv,
+							enum_func *enum_func,
+							void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	} else if (argc == 4) {
+		*user = TYPE_ENUM(bt_property_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void get_remote_device_property_p(int argc, const char **argv)
+{
+	bt_property_type_t type;
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+	VERIFY_PROP_TYPE_ARG(3, type);
+
+	EXEC(if_bluetooth->get_remote_device_property, &addr, type);
+}
+
+/*
+ * Same completion as for get_remote_device_property_c can be used for
+ * set_remote_device_property_c. No need to create separate function.
+ */
+#define set_remote_device_property_c get_remote_device_property_c
+
+static void set_remote_device_property_p(int argc, const char **argv)
+{
+	bt_property_t property;
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+	VERIFY_PROP_TYPE_ARG(3, property.type);
+
+	switch (property.type) {
+	case BT_PROPERTY_REMOTE_FRIENDLY_NAME:
+		property.len = strlen(argv[4]);
+		property.val = (char *) argv[4];
+		break;
+	case BT_PROPERTY_BDNAME:
+	case BT_PROPERTY_BDADDR:
+	case BT_PROPERTY_UUIDS:
+	case BT_PROPERTY_CLASS_OF_DEVICE:
+	case BT_PROPERTY_TYPE_OF_DEVICE:
+	case BT_PROPERTY_SERVICE_RECORD:
+	case BT_PROPERTY_ADAPTER_SCAN_MODE:
+	case BT_PROPERTY_ADAPTER_BONDED_DEVICES:
+	case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
+	case BT_PROPERTY_REMOTE_RSSI:
+	case BT_PROPERTY_REMOTE_VERSION_INFO:
+	case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP:
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	case BT_PROPERTY_LOCAL_LE_FEATURES:
+#endif
+	default:
+		return;
+	}
+
+	EXEC(if_bluetooth->set_remote_device_property, &addr, &property);
+}
+
+/* For now uuid is not autocompleted. Use routine for complete_addr_c */
+#define get_remote_service_record_c complete_addr_c
+
+static void get_remote_service_record_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bt_uuid_t uuid;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc <= 3) {
+		haltest_error("No uuid specified\n");
+		return;
+	}
+
+	str2bt_uuid_t(argv[3], &uuid);
+
+	EXEC(if_bluetooth->get_remote_service_record, &addr, &uuid);
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define get_remote_services_c complete_addr_c
+
+static void get_remote_services_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_bluetooth->get_remote_services, &addr);
+}
+
+static void start_discovery_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_bluetooth);
+
+	EXEC(if_bluetooth->start_discovery);
+}
+
+static void cancel_discovery_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_bluetooth);
+
+	EXEC(if_bluetooth->cancel_discovery);
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define create_bond_c complete_addr_c
+
+static void create_bond_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	int transport;
+#endif
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	if (argc < 4)
+		transport = BT_TRANSPORT_UNKNOWN;
+	else
+		transport = atoi(argv[3]);
+
+	EXEC(if_bluetooth->create_bond, &addr, transport);
+#else
+	EXEC(if_bluetooth->create_bond, &addr);
+#endif
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define remove_bond_c complete_addr_c
+
+static void remove_bond_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_bluetooth->remove_bond, &addr);
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define cancel_bond_c complete_addr_c
+
+static void cancel_bond_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_bluetooth->cancel_bond, &addr);
+}
+
+static void pin_reply_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	static const char *const completions[] = { last_remote_addr, NULL };
+
+	if (argc == 3) {
+		*user = (void *) completions;
+		*enum_func = enum_strings;
+	}
+}
+
+static void pin_reply_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bt_pin_code_t pin;
+	int pin_len = 0;
+	int accept = 0;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc > 3) {
+		accept = 1;
+		pin_len = strlen(argv[3]);
+		memcpy(pin.pin, argv[3], pin_len);
+	}
+
+	EXEC(if_bluetooth->pin_reply, &addr, accept, pin_len, &pin);
+}
+
+static void ssp_reply_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = last_remote_addr;
+		*enum_func = enum_one_string;
+	} else if (argc == 5) {
+		*user = "1";
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		if (-1 != (int) last_ssp_variant) {
+			*user = (void *) bt_ssp_variant_t2str(last_ssp_variant);
+			*enum_func = enum_one_string;
+		} else {
+			*user = TYPE_ENUM(bt_ssp_variant_t);
+			*enum_func = enum_defines;
+		}
+	}
+}
+
+static void ssp_reply_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bt_ssp_variant_t var;
+	int accept;
+	int passkey;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc < 4) {
+		haltest_error("No ssp variant specified\n");
+		return;
+	}
+
+	var = str2btsspvariant(argv[3]);
+	if (argc < 5) {
+		haltest_error("No accept value specified\n");
+		return;
+	}
+
+	accept = atoi(argv[4]);
+	passkey = 0;
+
+	if (accept && var == BT_SSP_VARIANT_PASSKEY_ENTRY && argc >= 5)
+		passkey = atoi(argv[4]);
+
+	EXEC(if_bluetooth->ssp_reply, &addr, var, accept, passkey);
+}
+
+static void get_profile_interface_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	static const char *const profile_ids[] = {
+		BT_PROFILE_HANDSFREE_ID,
+		BT_PROFILE_ADVANCED_AUDIO_ID,
+		BT_PROFILE_HEALTH_ID,
+		BT_PROFILE_SOCKETS_ID,
+		BT_PROFILE_HIDHOST_ID,
+		BT_PROFILE_PAN_ID,
+		BT_PROFILE_GATT_ID,
+		BT_PROFILE_AV_RC_ID,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		BT_PROFILE_HANDSFREE_CLIENT_ID,
+		BT_PROFILE_MAP_CLIENT_ID,
+		BT_PROFILE_AV_RC_CTRL_ID,
+		BT_PROFILE_ADVANCED_AUDIO_SINK_ID,
+#endif
+		NULL
+	};
+
+	if (argc == 3) {
+		*user = (void *) profile_ids;
+		*enum_func = enum_strings;
+	}
+}
+
+static void get_profile_interface_p(int argc, const char **argv)
+{
+	const char *id;
+	const void **pif = NULL;
+
+	RETURN_IF_NULL(if_bluetooth);
+	if (argc <= 2) {
+		haltest_error("No interface specified\n");
+		return;
+	}
+
+	id = argv[2];
+
+	if (strcmp(BT_PROFILE_HANDSFREE_ID, id) == 0)
+		pif = (const void **) &if_hf;
+	else if (strcmp(BT_PROFILE_ADVANCED_AUDIO_ID, id) == 0)
+		pif = (const void **) &if_av;
+	else if (strcmp(BT_PROFILE_HEALTH_ID, id) == 0)
+		pif = (const void **) &if_hl;
+	else if (strcmp(BT_PROFILE_SOCKETS_ID, id) == 0)
+		pif = (const void **) &if_sock;
+	else if (strcmp(BT_PROFILE_HIDHOST_ID, id) == 0)
+		pif = (const void **) &if_hh;
+	else if (strcmp(BT_PROFILE_PAN_ID, id) == 0)
+		pif = (const void **) &if_pan;
+	else if (strcmp(BT_PROFILE_AV_RC_ID, id) == 0)
+		pif = (const void **) &if_rc;
+	else if (strcmp(BT_PROFILE_GATT_ID, id) == 0)
+		pif = (const void **) &if_gatt;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	else if (strcmp(BT_PROFILE_AV_RC_CTRL_ID, id) == 0)
+		pif = (const void **) &if_rc_ctrl;
+	else if (strcmp(BT_PROFILE_HANDSFREE_CLIENT_ID, id) == 0)
+		pif = (const void **) &if_hf_client;
+	else if (strcmp(BT_PROFILE_MAP_CLIENT_ID, id) == 0)
+		pif = (const void **) &if_mce;
+	else if (strcmp(BT_PROFILE_ADVANCED_AUDIO_SINK_ID, id) == 0)
+		pif = (const void **) &if_av_sink;
+#endif
+	else
+		haltest_error("%s is not correct for get_profile_interface\n",
+									id);
+
+	if (pif != NULL) {
+		*pif = if_bluetooth->get_profile_interface(id);
+		haltest_info("get_profile_interface(%s) : %p\n", id, *pif);
+	}
+}
+
+static void dut_mode_configure_p(int argc, const char **argv)
+{
+	uint8_t mode;
+
+	RETURN_IF_NULL(if_bluetooth);
+
+	if (argc <= 2) {
+		haltest_error("No dut mode specified\n");
+		return;
+	}
+
+	mode = strtol(argv[2], NULL, 0);
+
+	EXEC(if_bluetooth->dut_mode_configure, mode);
+}
+
+static void dut_mode_send_p(int argc, const char **argv)
+{
+	haltest_error("not implemented\n");
+}
+
+static void le_test_mode_p(int argc, const char **argv)
+{
+	haltest_error("not implemented\n");
+}
+
+static void config_hci_snoop_log_p(int argc, const char **argv)
+{
+	uint8_t mode;
+
+	RETURN_IF_NULL(if_bluetooth);
+
+	if (argc <= 2) {
+		haltest_error("No mode specified\n");
+		return;
+	}
+
+	mode = strtol(argv[2], NULL, 0);
+
+	EXEC(if_bluetooth->config_hci_snoop_log, mode);
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHOD(cleanup),
+	STD_METHOD(enable),
+	STD_METHOD(disable),
+	STD_METHOD(get_adapter_properties),
+	STD_METHODCH(get_adapter_property, "<prop_type>"),
+	STD_METHODCH(set_adapter_property, "<prop_type> <prop_value>"),
+	STD_METHODCH(get_remote_device_properties, "<addr>"),
+	STD_METHODCH(get_remote_device_property, "<addr> <property_type>"),
+	STD_METHODCH(set_remote_device_property,
+					"<addr> <property_type> <value>"),
+	STD_METHODCH(get_remote_service_record, "<addr> <uuid>"),
+	STD_METHODCH(get_remote_services, "<addr>"),
+	STD_METHOD(start_discovery),
+	STD_METHOD(cancel_discovery),
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	STD_METHODCH(create_bond, "<addr> [<transport>]"),
+	STD_METHOD(read_energy_info),
+	STD_METHODCH(get_connection_state, "<addr>"),
+#else
+	STD_METHODCH(create_bond, "<addr>"),
+#endif
+	STD_METHODCH(remove_bond, "<addr>"),
+	STD_METHODCH(cancel_bond, "<addr>"),
+	STD_METHODCH(pin_reply, "<address> [<pin>]"),
+	STD_METHODCH(ssp_reply, "<address> <ssp_veriant> 1|0 [<passkey>]"),
+	STD_METHODCH(get_profile_interface, "<profile id>"),
+	STD_METHODH(dut_mode_configure, "<dut mode>"),
+	STD_METHOD(dut_mode_send),
+	STD_METHOD(le_test_mode),
+	STD_METHODH(config_hci_snoop_log, "<mode>"),
+	END_METHOD
+};
+
+const struct interface bluetooth_if = {
+	.name = "bluetooth",
+	.methods = methods
+};
diff --git a/repo/android/client/if-gatt.c b/repo/android/client/if-gatt.c
new file mode 100644
index 0000000..3526783
--- /dev/null
+++ b/repo/android/client/if-gatt.c
@@ -0,0 +1,2675 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+
+#include "../hal-utils.h"
+#include "if-main.h"
+
+const btgatt_interface_t *if_gatt = NULL;
+
+/*
+ * In version 19 some callback were changed.
+ * btgatt_char_id_t -> btgatt_gatt_id_t
+ * bt_uuid_t        -> btgatt_gatt_id_t
+ */
+#define str2btgatt_descr_id_t str2btgatt_gatt_id_t
+#define btgatt_descr_id_t2str btgatt_gatt_id_t2str
+#define btgatt_descr_id_t btgatt_gatt_id_t
+
+#define MAX_CHAR_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11)
+#define MAX_SRVC_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11 + 1 + 11)
+/* How man characters print from binary objects (arbitrary) */
+#define MAX_HEX_VAL_STR_LEN 100
+#define MAX_NOTIFY_PARAMS_STR_LEN (MAX_SRVC_ID_STR_LEN + MAX_CHAR_ID_STR_LEN \
+		+ MAX_ADDR_STR_LEN + MAX_HEX_VAL_STR_LEN + 60)
+#define MAX_READ_PARAMS_STR_LEN (MAX_SRVC_ID_STR_LEN + MAX_CHAR_ID_STR_LEN \
+		+ MAX_UUID_STR_LEN + MAX_HEX_VAL_STR_LEN + 80)
+
+/* Hex arguments must have "0x" or "0X" prefix */
+#define VERIFY_INT_ARG(n, v, err) \
+	do { \
+		if (n < argc) \
+			v = strtol(argv[n], NULL, 0); \
+		else { \
+			haltest_error(err); \
+			return;\
+		} \
+	} while (0)
+
+#define VERIFY_HEX_ARG(n, v, err) \
+	do { \
+		if (n < argc) \
+			v = strtol(argv[n], NULL, 16); \
+		else { \
+			haltest_error(err); \
+			return;\
+		} \
+	} while (0)
+
+/* Helper macros to verify arguments of methods */
+#define VERIFY_CLIENT_IF(n, v) VERIFY_INT_ARG(n, v, "No client_if specified\n")
+#define VERIFY_SERVER_IF(n, v) VERIFY_INT_ARG(n, v, "No server_if specified\n")
+#define VERIFY_CONN_ID(n, v) VERIFY_INT_ARG(n, v, "No conn_if specified\n")
+#define VERIFY_TRANS_ID(n, v) VERIFY_INT_ARG(n, v, "No trans_id specified\n")
+#define VERIFY_STATUS(n, v) VERIFY_INT_ARG(n, v, "No status specified\n")
+#define VERIFY_OFFSET(n, v) VERIFY_INT_ARG(n, v, "No offset specified\n")
+#define VERIFY_TEST_ARG(n, v) VERIFY_INT_ARG(n, v, "No test arg specified\n")
+#define VERIFY_ACTION(n, v) VERIFY_INT_ARG(n, v, "No action specified\n")
+#define VERIFY_FILT_TYPE(n, v) VERIFY_INT_ARG(n, v, "No filter specified\n")
+#define VERIFY_FILT_INDEX(n, v) VERIFY_INT_ARG(n, v, \
+						"No filter index specified\n")
+#define VERIFY_FEAT_SELN(n, v) VERIFY_INT_ARG(n, v, "No feat seln specified\n")
+#define VERIFY_LIST_LOGIC_TYPE(n, v) VERIFY_INT_ARG(n, v, \
+					"No list logic type specified\n")
+#define VERIFY_FILT_LOGIC_TYPE(n, v) VERIFY_INT_ARG(n, v, \
+					"No filt logic type specified\n")
+#define VERIFY_RSSI_HI_THR(n, v) VERIFY_INT_ARG(n, v, \
+					"No rssi hi threshold specified\n")
+#define VERIFY_RSSI_LOW_THR(n, v) VERIFY_INT_ARG(n, v, \
+						"No low threshold specified\n")
+#define VERIFY_DELY_MODE(n, v) VERIFY_INT_ARG(n, v, "No delay mode specified\n")
+#define VERIFY_FND_TIME(n, v) VERIFY_INT_ARG(n, v, "No found time specified\n")
+#define VERIFY_LOST_TIME(n, v) VERIFY_INT_ARG(n, v, "No lost time specified\n")
+#define VERIFY_FND_TIME_CNT(n, v) VERIFY_INT_ARG(n, v, \
+					"No found timer counter specified\n")
+#define VERIFY_COMP_ID(n, v) VERIFY_INT_ARG(n, v, "No company id specified\n")
+#define VERIFY_COMP_ID_MASK(n, v) VERIFY_INT_ARG(n, v, \
+						"No comp. id mask specified\n")
+#define VERIFY_DATA_LEN(n, v) VERIFY_INT_ARG(n, v, "No data len specified\n")
+#define VERIFY_MASK_LEN(n, v) VERIFY_INT_ARG(n, v, "No mask len specified\n")
+#define VERIFY_MIN_INTERVAL(n, v) VERIFY_INT_ARG(n, v, \
+						"No min interval specified\n")
+#define VERIFY_MAX_INTERVAL(n, v) VERIFY_INT_ARG(n, v, \
+						"No max interval specified\n")
+#define VERIFY_APPEARANCE(n, v) VERIFY_INT_ARG(n, v, "No apperance specified\n")
+#define VERIFY_MANUFACTURER_LEN(n, v) VERIFY_INT_ARG(n, v, \
+					"No manufacturer len specified\n")
+#define VERIFY_SERVICE_DATA_LEN(n, v) VERIFY_INT_ARG(n, v, \
+					"No service data len specified\n")
+#define VERIFY_SERVICE_UUID_LEN(n, v) VERIFY_INT_ARG(n, v, \
+					"No service uuid len specified\n")
+#define VERIFY_MTU(n, v) VERIFY_INT_ARG(n, v, "No mtu specified\n")
+#define VERIFY_LATENCY(n, v) VERIFY_INT_ARG(n, v, \
+						"No latency specified\n")
+#define VERIFY_TIMEOUT(n, v) VERIFY_INT_ARG(n, v, "No timeout specified\n")
+#define VERIFY_SCAN_INTERVAL(n, v) VERIFY_INT_ARG(n, v, \
+						"No scan interval specified\n")
+#define VERIFY_SCAN_WINDOW(n, v) VERIFY_INT_ARG(n, v, \
+						"No scan window specified\n")
+#define VERIFY_ADV_TYPE(n, v) VERIFY_INT_ARG(n, v, \
+					"No advertising type specified\n")
+#define VERIFY_CHNL_MAP(n, v) VERIFY_INT_ARG(n, v, \
+						"No channel map specified\n")
+#define VERIFY_TX_POWER(n, v) VERIFY_INT_ARG(n, v, \
+						"No tx power specified\n")
+#define VERIFY_TIMEOUT_S(n, v) VERIFY_INT_ARG(n, v, \
+						"No timeout in sec specified\n")
+#define VERIFY_BATCH_SCAN_FULL_MAX(n, v) VERIFY_INT_ARG(n, v, \
+					"No batch scan full max specified\n")
+#define VERIFY_BATCH_SCAN_TRUNC_MAX(n, v) VERIFY_INT_ARG(n, v, \
+					"No batch scan trunc max specified\n")
+#define VERIFY_BATCH_SCAN_NOTIFY_THR(n, v) VERIFY_INT_ARG(n, v, \
+				"No batch scan notify threshold specified\n")
+#define VERIFY_SCAN_MODE(n, v) VERIFY_INT_ARG(n, v, \
+						"No scan mode specified\n")
+#define VERIFY_ADDR_TYPE(n, v) VERIFY_INT_ARG(n, v, \
+						"No address type specified\n")
+#define VERIFY_DISCARD_RULE(n, v) VERIFY_INT_ARG(n, v, \
+						"No discard rule specified\n")
+#define VERIFY_HANDLE(n, v) VERIFY_HEX_ARG(n, v, "No "#v" specified\n")
+#define VERIFY_SERVICE_HANDLE(n, v) VERIFY_HANDLE(n, v)
+
+#define VERIFY_UUID(n, v) \
+	do { \
+		if (n < argc) \
+			gatt_str2bt_uuid_t(argv[n], -1, v); \
+		else { \
+			haltest_error("No uuid specified\n"); \
+			return;\
+		} \
+	} while (0)
+
+#define VERIFY_SRVC_ID(n, v) \
+	do { \
+		if (n < argc) \
+			str2btgatt_srvc_id_t(argv[n], v); \
+		else { \
+			haltest_error("No srvc_id specified\n"); \
+			return;\
+		} \
+	} while (0)
+
+#define VERIFY_CHAR_ID(n, v) \
+	do { \
+		if (n < argc) \
+			str2btgatt_gatt_id_t(argv[n], v); \
+		else { \
+			haltest_error("No char_id specified\n"); \
+			return;\
+		} \
+	} while (0)
+
+#define VERIFY_DESCR_ID(n, v) \
+	do { \
+		if (n < argc) \
+			str2btgatt_descr_id_t(argv[n], v); \
+		else { \
+			haltest_error("No descr_id specified\n"); \
+			return;\
+		} \
+	} while (0)
+
+#define GET_VERIFY_HEX_STRING(i, v, l) \
+	do { \
+		int ll;\
+		const char *n = argv[i]; \
+		if (argc <= i) { \
+			haltest_error("No value specified\n"); \
+			return; \
+		} \
+		if (n[0] != '0' || (n[1] != 'X' && n[1] != 'x')) { \
+			haltest_error("Value must be hex string\n"); \
+			return; \
+		} \
+		ll = fill_buffer(n + 2, (uint8_t *) v, sizeof(v)); \
+		if (ll < 0) { \
+			haltest_error("Value must be byte hex string\n"); \
+			return; \
+		} \
+		l = ll; \
+	} while (0)
+
+/* Gatt uses little endian uuid */
+static const char GATT_BASE_UUID[] = {
+	0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+	0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * converts gatt uuid to string
+ * buf should be at least 39 bytes
+ *
+ * This function formats 16, 32 and 128 bits uuid
+ *
+ * returns string representation of uuid
+ */
+static char *gatt_uuid_t2str(const bt_uuid_t *uuid, char *buf)
+{
+	int shift = 0;
+	int i = 16;
+	int limit = 0;
+	int j = 0;
+
+	/* for bluetooth uuid only 32 bits */
+	if (0 == memcmp(&uuid->uu, &GATT_BASE_UUID,
+						sizeof(bt_uuid_t) - 4)) {
+		limit = 12;
+		/* make it 16 bits */
+		if (uuid->uu[15] == 0 && uuid->uu[14] == 0)
+			i = 14;
+	}
+
+	while (i-- > limit) {
+		if (i == 11 || i == 9 || i == 7 || i == 5) {
+			buf[j * 2 + shift] = '-';
+			shift++;
+		}
+
+		sprintf(buf + j * 2 + shift, "%02x", uuid->uu[i]);
+		++j;
+	}
+
+	return buf;
+}
+
+/*
+ * Tries to convert hex string of given size into out buffer.
+ * Output buffer is little endian.
+ */
+static void scan_field(const char *str, int len, uint8_t *out, int out_size)
+{
+	int i;
+
+	memset(out, 0, out_size);
+	if (out_size * 2 > len + 1)
+		out_size = (len + 1) / 2;
+
+	for (i = 0; i < out_size && len > 0; ++i) {
+		len -= 2;
+		if (len >= 0)
+			sscanf(str + len, "%02hhx", &out[i]);
+		else
+			sscanf(str, "%1hhx", &out[i]);
+	}
+}
+
+/* Like strchr but with upper limit instead of 0 terminated string */
+static const char *strchrlimit(const char *p, const char *e, int c)
+{
+	while (p < e && *p != (char) c)
+		++p;
+
+	return p < e ? p : NULL;
+}
+
+/*
+ * converts string to uuid
+ * it accepts uuid in following forms:
+ *	123
+ *	0000123
+ *	0000123-0014-1234-0000-000056789abc
+ *	0000123001412340000000056789abc
+ *	123-14-1234-0-56789abc
+ */
+static void gatt_str2bt_uuid_t(const char *str, int len, bt_uuid_t *uuid)
+{
+	int dash_cnt = 0;
+	int dashes[6] = {-1}; /* indexes of '-' or \0 */
+	static uint8_t filed_offset[] = { 16, 12, 10, 8, 6, 0 };
+	const char *p = str;
+	const char *e;
+	int i;
+
+	e = str + ((len >= 0) ? len : (int) strlen(str));
+
+	while (p != NULL && dash_cnt < 5) {
+		const char *f = strchrlimit(p, e, '-');
+
+		if (f != NULL)
+			dashes[++dash_cnt] = f++ - str;
+		p = f;
+	}
+
+	/* get index of \0 to dashes table */
+	if (dash_cnt < 5)
+		dashes[++dash_cnt] = e - str;
+
+	memcpy(uuid, GATT_BASE_UUID, sizeof(bt_uuid_t));
+
+	/* whole uuid in one string without dashes */
+	if (dash_cnt == 1 && dashes[1] > 8) {
+		if (dashes[1] > 32)
+			dashes[1] = 32;
+		scan_field(str, dashes[1],
+				&uuid->uu[16 - (dashes[1] + 1) / 2],
+				(dashes[1] + 1) / 2);
+	} else {
+		for (i = 0; i < dash_cnt; ++i) {
+			scan_field(str + dashes[i] + 1,
+					dashes[i + 1] - dashes[i] - 1,
+					&uuid->uu[filed_offset[i + 1]],
+					filed_offset[i] - filed_offset[i + 1]);
+		}
+	}
+}
+
+/* char_id formating function */
+static char *btgatt_gatt_id_t2str(const btgatt_gatt_id_t *char_id, char *buf)
+{
+	char uuid_buf[MAX_UUID_STR_LEN];
+
+	sprintf(buf, "{%s,%d}", gatt_uuid_t2str(&char_id->uuid, uuid_buf),
+							char_id->inst_id);
+	return buf;
+}
+
+/* Parse btgatt_gatt_id_t */
+static void str2btgatt_gatt_id_t(const char *buf, btgatt_gatt_id_t *char_id)
+{
+	const char *e;
+
+	memcpy(&char_id->uuid, &GATT_BASE_UUID, sizeof(bt_uuid_t));
+	char_id->inst_id = 0;
+
+	if (*buf == '{')
+		buf++;
+	e = strpbrk(buf, " ,}");
+	if (e == NULL)
+		e = buf + strlen(buf);
+
+	gatt_str2bt_uuid_t(buf, e - buf, &char_id->uuid);
+	if (*e == ',') {
+		buf = e + 1;
+		e = strpbrk(buf, " ,}");
+		if (e == NULL)
+			e = buf + strlen(buf);
+		if (buf < e)
+			char_id->inst_id = atoi(buf);
+	}
+}
+
+/* service_id formating function */
+static char *btgatt_srvc_id_t2str(const btgatt_srvc_id_t *srvc_id, char *buf)
+{
+	char uuid_buf[MAX_UUID_STR_LEN];
+
+	sprintf(buf, "{%s,%d,%d}", gatt_uuid_t2str(&srvc_id->id.uuid, uuid_buf),
+				srvc_id->id.inst_id, srvc_id->is_primary);
+	return buf;
+}
+
+/* Parse btgatt_srvc_id_t */
+static void str2btgatt_srvc_id_t(const char *buf, btgatt_srvc_id_t *srvc_id)
+{
+	const char *e;
+
+	memcpy(&srvc_id->id.uuid, &GATT_BASE_UUID, sizeof(bt_uuid_t));
+	srvc_id->id.inst_id = 0;
+	srvc_id->is_primary = 1;
+
+	if (*buf == '{')
+		buf++;
+	e = strpbrk(buf, " ,}");
+	if (e == NULL)
+		e = buf + strlen(buf);
+
+	gatt_str2bt_uuid_t(buf, e - buf, &srvc_id->id.uuid);
+	if (*e == ',') {
+		buf = e + 1;
+		e = strpbrk(buf, " ,}");
+		if (e == NULL)
+			e = buf + strlen(buf);
+		if (buf < e)
+			srvc_id->id.inst_id = atoi(buf);
+	}
+
+	if (*e == ',') {
+		buf = e + 1;
+		e = strpbrk(buf, " ,}");
+		if (e == NULL)
+			e = buf + strlen(buf);
+		if (buf < e)
+			srvc_id->is_primary = atoi(buf);
+	}
+}
+
+/* Converts array of uint8_t to string representation */
+static char *array2str(const uint8_t *v, int size, char *buf, int out_size)
+{
+	int limit = size;
+	int i;
+
+	if (out_size > 0) {
+		*buf = '\0';
+		if (size >= 2 * out_size)
+			limit = (out_size - 2) / 2;
+
+		for (i = 0; i < limit; ++i)
+			sprintf(buf + 2 * i, "%02x", v[i]);
+
+		/* output buffer not enough to hold whole field fill with ...*/
+		if (limit < size)
+			sprintf(buf + 2 * i, "...");
+	}
+
+	return buf;
+}
+
+/* Converts btgatt_notify_params_t to string */
+static char *btgatt_notify_params_t2str(const btgatt_notify_params_t *data,
+								char *buf)
+{
+	char addr[MAX_ADDR_STR_LEN];
+	char srvc_id[MAX_SRVC_ID_STR_LEN];
+	char char_id[MAX_CHAR_ID_STR_LEN];
+	char value[MAX_HEX_VAL_STR_LEN];
+
+	sprintf(buf, "{bda=%s, srvc_id=%s, char_id=%s, val=%s, is_notify=%u}",
+		bt_bdaddr_t2str(&data->bda, addr),
+		btgatt_srvc_id_t2str(&data->srvc_id, srvc_id),
+		btgatt_gatt_id_t2str(&data->char_id, char_id),
+		array2str(data->value, data->len, value, sizeof(value)),
+							data->is_notify);
+	return buf;
+}
+
+static char *btgatt_unformatted_value_t2str(const btgatt_unformatted_value_t *v,
+							char *buf, int size)
+{
+	return array2str(v->value, v->len, buf, size);
+}
+
+static char *btgatt_read_params_t2str(const btgatt_read_params_t *data,
+								char *buf)
+{
+	char srvc_id[MAX_SRVC_ID_STR_LEN];
+	char char_id[MAX_CHAR_ID_STR_LEN];
+	char descr_id[MAX_UUID_STR_LEN];
+	char value[MAX_HEX_VAL_STR_LEN];
+
+	sprintf(buf, "{srvc_id=%s, char_id=%s, descr_id=%s, val=%s value_type=%d, status=%d}",
+		btgatt_srvc_id_t2str(&data->srvc_id, srvc_id),
+		btgatt_gatt_id_t2str(&data->char_id, char_id),
+		btgatt_descr_id_t2str(&data->descr_id, descr_id),
+		btgatt_unformatted_value_t2str(&data->value, value, 100),
+		data->value_type, data->status);
+	return buf;
+}
+
+/* BT-GATT Client callbacks. */
+
+/* Cache client_if and conn_id for tab completion */
+static char client_if_str[20];
+static char conn_id_str[20];
+/* Cache address for tab completion */
+static char last_addr[MAX_ADDR_STR_LEN];
+
+/* Callback invoked in response to register_client */
+static void gattc_register_client_cb(int status, int client_if,
+							bt_uuid_t *app_uuid)
+{
+	char buf[MAX_UUID_STR_LEN];
+
+	snprintf(client_if_str, sizeof(client_if_str), "%d", client_if);
+
+	haltest_info("%s: status=%d client_if=%d app_uuid=%s\n", __func__,
+						status, client_if,
+						gatt_uuid_t2str(app_uuid, buf));
+}
+
+/* Callback for scan results */
+static void gattc_scan_result_cb(bt_bdaddr_t *bda, int rssi, uint8_t *adv_data)
+{
+	char buf[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: bda=%s rssi=%d adv_data=%p\n", __func__,
+				bt_bdaddr_t2str(bda, buf), rssi, adv_data);
+}
+
+/* GATT open callback invoked in response to open */
+static void gattc_connect_cb(int conn_id, int status, int client_if,
+							bt_bdaddr_t *bda)
+{
+	haltest_info("%s: conn_id=%d status=%d, client_if=%d bda=%s\n",
+					__func__, conn_id, status, client_if,
+					bt_bdaddr_t2str(bda, last_addr));
+}
+
+/* Callback invoked in response to close */
+static void gattc_disconnect_cb(int conn_id, int status, int client_if,
+							bt_bdaddr_t *bda)
+{
+	char buf[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: conn_id=%d status=%d, client_if=%d bda=%s\n",
+					__func__, conn_id, status, client_if,
+					bt_bdaddr_t2str(bda, buf));
+}
+
+/*
+ * Invoked in response to search_service when the GATT service search
+ * has been completed.
+ */
+static void gattc_search_complete_cb(int conn_id, int status)
+{
+	haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status);
+}
+
+/* Reports GATT services on a remote device */
+static void gattc_search_result_cb(int conn_id, btgatt_srvc_id_t *srvc_id)
+{
+	char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+
+	haltest_info("%s: conn_id=%d srvc_id=%s\n", __func__, conn_id,
+				btgatt_srvc_id_t2str(srvc_id, srvc_id_buf));
+}
+
+/* GATT characteristic enumeration result callback */
+static void gattc_get_characteristic_cb(int conn_id, int status,
+					btgatt_srvc_id_t *srvc_id,
+					btgatt_gatt_id_t *char_id,
+					int char_prop)
+{
+	char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+	char char_id_buf[MAX_CHAR_ID_STR_LEN];
+
+	haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, char_prop=0x%x\n",
+			__func__, conn_id, status,
+			btgatt_srvc_id_t2str(srvc_id, srvc_id_buf),
+			btgatt_gatt_id_t2str(char_id, char_id_buf), char_prop);
+
+	/* enumerate next characteristic */
+	if (status == 0)
+		EXEC(if_gatt->client->get_characteristic, conn_id, srvc_id,
+								char_id);
+}
+
+/* GATT descriptor enumeration result callback */
+static void gattc_get_descriptor_cb(int conn_id, int status,
+		btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+		btgatt_descr_id_t *descr_id)
+{
+	char buf[MAX_UUID_STR_LEN];
+	char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+	char char_id_buf[MAX_CHAR_ID_STR_LEN];
+
+	haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, descr_id=%s\n",
+				__func__, conn_id, status,
+				btgatt_srvc_id_t2str(srvc_id, srvc_id_buf),
+				btgatt_gatt_id_t2str(char_id, char_id_buf),
+				btgatt_descr_id_t2str(descr_id, buf));
+
+	if (status == 0)
+		EXEC(if_gatt->client->get_descriptor, conn_id, srvc_id, char_id,
+								descr_id);
+}
+
+/* GATT included service enumeration result callback */
+static void gattc_get_included_service_cb(int conn_id, int status,
+						btgatt_srvc_id_t *srvc_id,
+						btgatt_srvc_id_t *incl_srvc_id)
+{
+	char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+	char incl_srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+
+	haltest_info("%s: conn_id=%d status=%d srvc_id=%s incl_srvc_id=%s)\n",
+			__func__, conn_id, status,
+			btgatt_srvc_id_t2str(srvc_id, srvc_id_buf),
+			btgatt_srvc_id_t2str(incl_srvc_id, incl_srvc_id_buf));
+
+	if (status == 0)
+		EXEC(if_gatt->client->get_included_service, conn_id, srvc_id,
+								incl_srvc_id);
+}
+
+/* Callback invoked in response to [de]register_for_notification */
+static void gattc_register_for_notification_cb(int conn_id, int registered,
+						int status,
+						btgatt_srvc_id_t *srvc_id,
+						btgatt_gatt_id_t *char_id)
+{
+	char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+	char char_id_buf[MAX_CHAR_ID_STR_LEN];
+
+	haltest_info("%s: conn_id=%d registered=%d status=%d srvc_id=%s char_id=%s\n",
+				__func__, conn_id, registered, status,
+				btgatt_srvc_id_t2str(srvc_id, srvc_id_buf),
+				btgatt_gatt_id_t2str(char_id, char_id_buf));
+}
+
+/*
+ * Remote device notification callback, invoked when a remote device sends
+ * a notification or indication that a client has registered for.
+ */
+static void gattc_notify_cb(int conn_id, btgatt_notify_params_t *p_data)
+{
+	char buf[MAX_NOTIFY_PARAMS_STR_LEN];
+
+	haltest_info("%s: conn_id=%d data=%s\n", __func__, conn_id,
+				btgatt_notify_params_t2str(p_data, buf));
+}
+
+/* Reports result of a GATT read operation */
+static void gattc_read_characteristic_cb(int conn_id, int status,
+						btgatt_read_params_t *p_data)
+{
+	char buf[MAX_READ_PARAMS_STR_LEN];
+
+	haltest_info("%s: conn_id=%d status=%d data=%s\n", __func__, conn_id,
+				status, btgatt_read_params_t2str(p_data, buf));
+}
+
+/* GATT write characteristic operation callback */
+static void gattc_write_characteristic_cb(int conn_id, int status,
+						btgatt_write_params_t *p_data)
+{
+	haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status);
+}
+
+/* GATT execute prepared write callback */
+static void gattc_execute_write_cb(int conn_id, int status)
+{
+	haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status);
+}
+
+/* Callback invoked in response to read_descriptor */
+static void gattc_read_descriptor_cb(int conn_id, int status,
+						btgatt_read_params_t *p_data)
+{
+	char buf[MAX_READ_PARAMS_STR_LEN];
+
+	haltest_info("%s: conn_id=%d status=%d data=%s\n", __func__, conn_id,
+				status, btgatt_read_params_t2str(p_data, buf));
+}
+
+/* Callback invoked in response to write_descriptor */
+static void gattc_write_descriptor_cb(int conn_id, int status,
+						btgatt_write_params_t *p_data)
+{
+	haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status);
+}
+
+/* Callback triggered in response to read_remote_rssi */
+static void gattc_read_remote_rssi_cb(int client_if, bt_bdaddr_t *bda, int rssi,
+								int status)
+{
+	char buf[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: client_if=%d bda=%s rssi=%d satus=%d\n", __func__,
+			client_if, bt_bdaddr_t2str(bda, buf), rssi, status);
+}
+
+/* Callback invoked in response to listen */
+static void gattc_listen_cb(int status, int client_if)
+{
+	haltest_info("%s: client_if=%d status=%d\n", __func__, client_if,
+								status);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+/* Callback invoked when the MTU for a given connection changes */
+static void gattc_configure_mtu_cb(int conn_id, int status, int mtu)
+{
+	haltest_info("%s: conn_id=%d, status=%d, mtu=%d\n", __func__, conn_id,
+								status, mtu);
+}
+
+/* Callback invoked when a scan filter configuration command has completed */
+static void gattc_scan_filter_cfg_cb(int action, int client_if, int status,
+						int filt_type, int avbl_space)
+{
+	haltest_info("%s: action=%d, client_if=%d, status=%d, filt_type=%d"
+			", avbl_space=%d", __func__, action, client_if, status,
+			filt_type, avbl_space);
+}
+
+/* Callback invoked when scan param has been added, cleared, or deleted */
+static void gattc_scan_filter_param_cb(int action, int client_if, int status,
+								int avbl_space)
+{
+	haltest_info("%s: action=%d, client_if=%d, status=%d, avbl_space=%d",
+			__func__, action, client_if, status, avbl_space);
+}
+
+/* Callback invoked when a scan filter configuration command has completed */
+static void gattc_scan_filter_status_cb(int enable, int client_if, int status)
+{
+	haltest_info("%s: enable=%d, client_if=%d, status=%d", __func__,
+						enable, client_if, status);
+}
+
+/* Callback invoked when multi-adv enable operation has completed */
+static void gattc_multi_adv_enable_cb(int client_if, int status)
+{
+	haltest_info("%s: client_if=%d, status=%d", __func__, client_if,
+									status);
+}
+
+/* Callback invoked when multi-adv param update operation has completed */
+static void gattc_multi_adv_update_cb(int client_if, int status)
+{
+	haltest_info("%s: client_if=%d, status=%d", __func__, client_if,
+									status);
+}
+
+/* Callback invoked when multi-adv instance data set operation has completed */
+static void gattc_multi_adv_data_cb(int client_if, int status)
+{
+	haltest_info("%s: client_if=%d, status=%d", __func__, client_if,
+									status);
+}
+
+/* Callback invoked when multi-adv disable operation has completed */
+static void gattc_multi_adv_disable_cb(int client_if, int status)
+{
+	haltest_info("%s: client_if=%d, status=%d", __func__, client_if,
+									status);
+}
+
+/*
+ * Callback notifying an application that a remote device connection is
+ * currently congested and cannot receive any more data. An application should
+ * avoid sending more data until a further callback is received indicating the
+ * congestion status has been cleared.
+ */
+static void gattc_congestion_cb(int conn_id, bool congested)
+{
+	haltest_info("%s: conn_id=%d, congested=%d", __func__, conn_id,
+								congested);
+}
+
+/* Callback invoked when batchscan storage config operation has completed */
+static void gattc_batchscan_cfg_storage_cb(int client_if, int status)
+{
+	haltest_info("%s: client_if=%d, status=%d", __func__, client_if,
+									status);
+}
+
+/* Callback invoked when batchscan enable / disable operation has completed */
+static void gattc_batchscan_enable_disable_cb(int action, int client_if,
+								int status)
+{
+	haltest_info("%s: action=%d, client_if=%d, status=%d", __func__, action,
+							client_if, status);
+}
+
+/* Callback invoked when batchscan reports are obtained */
+static void gattc_batchscan_reports_cb(int client_if, int status,
+					int report_format, int num_records,
+					int data_len, uint8_t* rep_data)
+{
+	/* BTGATT_MAX_ATTR_LEN = 600 */
+	char valbuf[600];
+
+	haltest_info("%s: client_if=%d, status=%d, report_format=%d"
+			", num_records=%d, data_len=%d, rep_data=%s", __func__,
+			client_if, status, report_format, num_records, data_len,
+			array2str(rep_data, data_len, valbuf, sizeof(valbuf)));
+}
+
+/* Callback invoked when batchscan storage threshold limit is crossed */
+static void gattc_batchscan_threshold_cb(int client_if)
+{
+	haltest_info("%s: client_if=%d", __func__, client_if);
+}
+
+/* Track ADV VSE callback invoked when tracked device is found or lost */
+static void gattc_track_adv_event_cb(int client_if, int filt_index,
+						int addr_type, bt_bdaddr_t* bda,
+						int adv_state)
+{
+	char buf[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s, client_if=%d, filt_index=%d, addr_type=%d, bda=%s"
+			", adv_state=%d", __func__, client_if, filt_index,
+			addr_type, bt_bdaddr_t2str(bda, buf), adv_state);
+}
+#endif
+
+static const btgatt_client_callbacks_t btgatt_client_callbacks = {
+	.register_client_cb = gattc_register_client_cb,
+	.scan_result_cb = gattc_scan_result_cb,
+	.open_cb = gattc_connect_cb,
+	.close_cb = gattc_disconnect_cb,
+	.search_complete_cb = gattc_search_complete_cb,
+	.search_result_cb = gattc_search_result_cb,
+	.get_characteristic_cb = gattc_get_characteristic_cb,
+	.get_descriptor_cb = gattc_get_descriptor_cb,
+	.get_included_service_cb = gattc_get_included_service_cb,
+	.register_for_notification_cb = gattc_register_for_notification_cb,
+	.notify_cb = gattc_notify_cb,
+	.read_characteristic_cb = gattc_read_characteristic_cb,
+	.write_characteristic_cb = gattc_write_characteristic_cb,
+	.read_descriptor_cb = gattc_read_descriptor_cb,
+	.write_descriptor_cb = gattc_write_descriptor_cb,
+	.execute_write_cb = gattc_execute_write_cb,
+	.read_remote_rssi_cb = gattc_read_remote_rssi_cb,
+	.listen_cb = gattc_listen_cb,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.configure_mtu_cb = gattc_configure_mtu_cb,
+	.scan_filter_cfg_cb = gattc_scan_filter_cfg_cb,
+	.scan_filter_param_cb = gattc_scan_filter_param_cb,
+	.scan_filter_status_cb = gattc_scan_filter_status_cb,
+	.multi_adv_enable_cb = gattc_multi_adv_enable_cb,
+	.multi_adv_update_cb = gattc_multi_adv_update_cb,
+	.multi_adv_data_cb = gattc_multi_adv_data_cb,
+	.multi_adv_disable_cb = gattc_multi_adv_disable_cb,
+	.congestion_cb = gattc_congestion_cb,
+	.batchscan_cfg_storage_cb = gattc_batchscan_cfg_storage_cb,
+	.batchscan_enb_disable_cb = gattc_batchscan_enable_disable_cb,
+	.batchscan_reports_cb = gattc_batchscan_reports_cb,
+	.batchscan_threshold_cb = gattc_batchscan_threshold_cb,
+	.track_adv_event_cb = gattc_track_adv_event_cb,
+#endif
+};
+
+/* BT-GATT Server callbacks */
+
+/* Cache server_if and conn_id for tab completion */
+static char server_if_str[20];
+
+/* Callback invoked in response to register_server */
+static void gatts_register_server_cb(int status, int server_if,
+							bt_uuid_t *app_uuid)
+{
+	char buf[MAX_UUID_STR_LEN];
+
+	haltest_info("%s: status=%d server_if=%d app_uuid=%s\n", __func__,
+			status, server_if, gatt_uuid_t2str(app_uuid, buf));
+}
+
+/*
+ * Callback indicating that a remote device has connected
+ * or been disconnected
+ */
+static void gatts_connection_cb(int conn_id, int server_if, int connected,
+							bt_bdaddr_t *bda)
+{
+	haltest_info("%s: conn_id=%d server_if=%d connected=%d bda=%s\n",
+					__func__, conn_id, server_if, connected,
+					bt_bdaddr_t2str(bda, last_addr));
+	snprintf(conn_id_str, sizeof(conn_id_str), "%d", conn_id);
+}
+
+/* Callback invoked in response to create_service */
+static void gatts_service_added_cb(int status, int server_if,
+				btgatt_srvc_id_t *srvc_id, int srvc_handle)
+{
+	char buf[MAX_SRVC_ID_STR_LEN];
+
+	snprintf(server_if_str, sizeof(server_if_str), "%d", server_if);
+
+	haltest_info("%s: status=%d server_if=%d srvc_id=%s handle=0x%x\n",
+			__func__, status, server_if,
+			btgatt_srvc_id_t2str(srvc_id, buf), srvc_handle);
+}
+
+/* Callback indicating that an included service has been added to a service */
+static void gatts_included_service_added_cb(int status, int server_if,
+							int srvc_handle,
+							int incl_srvc_handle)
+{
+	haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x inc_srvc_handle=0x%x\n",
+						__func__, status, server_if,
+						srvc_handle, incl_srvc_handle);
+}
+
+/* Callback invoked when a characteristic has been added to a service */
+static void gatts_characteristic_added_cb(int status, int server_if,
+								bt_uuid_t *uuid,
+								int srvc_handle,
+								int char_handle)
+{
+	char buf[MAX_SRVC_ID_STR_LEN];
+
+	haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=0x%x char_handle=0x%x\n",
+			__func__, status, server_if, gatt_uuid_t2str(uuid, buf),
+			srvc_handle, char_handle);
+}
+
+/* Callback invoked when a descriptor has been added to a characteristic */
+static void gatts_descriptor_added_cb(int status, int server_if,
+					bt_uuid_t *uuid, int srvc_handle,
+							int descr_handle)
+{
+	char buf[MAX_SRVC_ID_STR_LEN];
+
+	haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=0x%x descr_handle=0x%x\n",
+			__func__, status, server_if, gatt_uuid_t2str(uuid, buf),
+			srvc_handle, descr_handle);
+}
+
+/* Callback invoked in response to start_service */
+static void gatts_service_started_cb(int status, int server_if, int srvc_handle)
+{
+	haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n",
+				__func__, status, server_if, srvc_handle);
+}
+
+/* Callback invoked in response to stop_service */
+static void gatts_service_stopped_cb(int status, int server_if, int srvc_handle)
+{
+	haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n",
+				__func__, status, server_if, srvc_handle);
+}
+
+/* Callback triggered when a service has been deleted */
+static void gatts_service_deleted_cb(int status, int server_if, int srvc_handle)
+{
+	haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n",
+				__func__, status, server_if, srvc_handle);
+}
+
+/*
+ * Callback invoked when a remote device has requested to read a characteristic
+ * or descriptor. The application must respond by calling send_response
+ */
+static void gatts_request_read_cb(int conn_id, int trans_id, bt_bdaddr_t *bda,
+						int attr_handle, int offset,
+						bool is_long)
+{
+	char buf[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=0x%x offset=%d is_long=%d\n",
+			__func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf),
+			attr_handle, offset, is_long);
+}
+
+/*
+ * Callback invoked when a remote device has requested to write to a
+ * characteristic or descriptor.
+ */
+static void gatts_request_write_cb(int conn_id, int trans_id, bt_bdaddr_t *bda,
+					int attr_handle, int offset, int length,
+					bool need_rsp, bool is_prep,
+					uint8_t *value)
+{
+	char buf[MAX_ADDR_STR_LEN];
+	char valbuf[100];
+
+	haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=0x%x offset=%d length=%d need_rsp=%d is_prep=%d value=%s\n",
+			__func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf),
+			attr_handle, offset, length, need_rsp, is_prep,
+			array2str(value, length, valbuf, sizeof(valbuf)));
+}
+
+/* Callback invoked when a previously prepared write is to be executed */
+static void gatts_request_exec_write_cb(int conn_id, int trans_id,
+					bt_bdaddr_t *bda, int exec_write)
+{
+	char buf[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: conn_id=%d trans_id=%d bda=%s exec_write=%d\n",
+			__func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf),
+			exec_write);
+}
+
+/*
+ * Callback triggered in response to send_response if the remote device
+ * sends a confirmation.
+ */
+static void gatts_response_confirmation_cb(int status, int handle)
+{
+	haltest_info("%s: status=%d handle=0x%x\n", __func__, status, handle);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+/**
+ * Callback confirming that a notification or indication has been sent
+ * to a remote device.
+ */
+static void gatts_indication_sent_cb(int conn_id, int status)
+{
+	haltest_info("%s: status=%d conn_id=%d", __func__, status, conn_id);
+}
+
+/**
+ * Callback notifying an application that a remote device connection is
+ * currently congested and cannot receive any more data. An application
+ * should avoid sending more data until a further callback is received
+ * indicating the congestion status has been cleared.
+ */
+static void gatts_congestion_cb(int conn_id, bool congested)
+{
+	haltest_info("%s: conn_id=%d congested=%d", __func__, conn_id,
+								congested);
+}
+#endif
+
+static const btgatt_server_callbacks_t btgatt_server_callbacks = {
+	.register_server_cb = gatts_register_server_cb,
+	.connection_cb = gatts_connection_cb,
+	.service_added_cb = gatts_service_added_cb,
+	.included_service_added_cb = gatts_included_service_added_cb,
+	.characteristic_added_cb = gatts_characteristic_added_cb,
+	.descriptor_added_cb = gatts_descriptor_added_cb,
+	.service_started_cb = gatts_service_started_cb,
+	.service_stopped_cb = gatts_service_stopped_cb,
+	.service_deleted_cb = gatts_service_deleted_cb,
+	.request_read_cb = gatts_request_read_cb,
+	.request_write_cb = gatts_request_write_cb,
+	.request_exec_write_cb = gatts_request_exec_write_cb,
+	.response_confirmation_cb = gatts_response_confirmation_cb,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.indication_sent_cb = gatts_indication_sent_cb,
+	.congestion_cb = gatts_congestion_cb,
+#endif
+};
+
+static const btgatt_callbacks_t gatt_cbacks = {
+	.size = sizeof(gatt_cbacks),
+	.client = &btgatt_client_callbacks,
+	.server = &btgatt_server_callbacks
+};
+
+/*
+ * convert hex string to uint8_t array
+ */
+static int fill_buffer(const char *str, uint8_t *out, int out_size)
+{
+	int str_len;
+	int i, j;
+	char c;
+	uint8_t b;
+
+	str_len = strlen(str);
+
+	/* Hex string must be byte format */
+	if (str_len % 2)
+		return -1;
+
+	for (i = 0, j = 0; i < out_size && j < str_len; i++, j++) {
+		c = str[j];
+
+		if (c >= 'a' && c <= 'f')
+			c += 'A' - 'a';
+
+		if (c >= '0' && c <= '9')
+			b = c - '0';
+		else if (c >= 'A' && c <= 'F')
+			b = 10 + c - 'A';
+		else
+			return 0;
+
+		j++;
+
+		c = str[j];
+
+		if (c >= 'a' && c <= 'f')
+			c += 'A' - 'a';
+
+		if (c >= '0' && c <= '9')
+			b = b * 16 + c - '0';
+		else if (c >= 'A' && c <= 'F')
+			b = b * 16 + 10 + c - 'A';
+		else
+			return 0;
+
+		out[i] = b;
+	}
+
+	return i;
+}
+
+/* gatt client methods */
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_gatt);
+
+	EXEC(if_gatt->init, &gatt_cbacks);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_gatt);
+
+	EXECV(if_gatt->cleanup);
+
+	if_gatt = NULL;
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface gatt_if = {
+	.name = "gatt",
+	.methods = methods
+};
+
+/* register_client */
+
+static void register_client_p(int argc, const char **argv)
+{
+	bt_uuid_t uuid;
+
+	RETURN_IF_NULL(if_gatt);
+
+	/* uuid */
+	if (argc <= 2)
+		gatt_str2bt_uuid_t("babe4bed", -1, &uuid);
+	else
+		gatt_str2bt_uuid_t(argv[2], -1, &uuid);
+
+	EXEC(if_gatt->client->register_client, &uuid);
+}
+
+/* unregister_client */
+
+static void unregister_client_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void unregister_client_p(int argc, const char **argv)
+{
+	int client_if;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+
+	EXEC(if_gatt->client->unregister_client, client_if);
+}
+
+/* scan */
+
+/* Same completion as unregister for now, start stop is not auto completed */
+#define scan_c unregister_client_c
+
+static void scan_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+	int client_if;
+#endif
+	int start = 1;
+
+	RETURN_IF_NULL(if_gatt);
+
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+	VERIFY_CLIENT_IF(2, client_if);
+
+	/* start */
+	if (argc >= 4)
+		start = strtol(argv[3], NULL, 0);
+
+	EXEC(if_gatt->client->scan, client_if, start);
+#else
+	/* start */
+	if (argc >= 3)
+		start = strtol(argv[2], NULL, 0);
+
+	EXEC(if_gatt->client->scan, start);
+#endif
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+static void connect_p(int argc, const char **argv)
+{
+	int client_if;
+	bt_bdaddr_t bd_addr;
+	int is_direct = 1;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	int transport = 1;
+#endif
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+
+	/* is_direct */
+	if (argc > 4)
+		is_direct = strtol(argv[4], NULL, 0);
+
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+	EXEC(if_gatt->client->connect, client_if, &bd_addr, is_direct);
+#else
+	/* transport */
+	if (argc > 5)
+		transport = strtol(argv[5], NULL, 0);
+
+	EXEC(if_gatt->client->connect, client_if, &bd_addr, is_direct,
+								transport);
+#endif
+}
+
+/* disconnect */
+
+static void disconnect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	} else if (argc == 5) {
+		*user = conn_id_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void disconnect_p(int argc, const char **argv)
+{
+	int client_if;
+	bt_bdaddr_t bd_addr;
+	int conn_id;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+	VERIFY_CONN_ID(4, conn_id);
+
+	EXEC(if_gatt->client->disconnect, client_if, &bd_addr, conn_id);
+}
+
+/* listen */
+
+/* Same completion as unregister for now, start stop is not auto completed */
+#define listen_c unregister_client_c
+
+static void listen_p(int argc, const char **argv)
+{
+	int client_if;
+	int start = 1;
+
+	RETURN_IF_NULL(if_gatt);
+
+	VERIFY_CLIENT_IF(2, client_if);
+
+	/* start */
+	if (argc >= 4)
+		start = strtol(argv[3], NULL, 0);
+
+	EXEC(if_gatt->client->listen, client_if, start);
+}
+
+/* refresh */
+
+static void refresh_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*enum_func = enum_devices;
+	}
+}
+
+static void refresh_p(int argc, const char **argv)
+{
+	int client_if;
+	bt_bdaddr_t bd_addr;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+
+	EXEC(if_gatt->client->refresh, client_if, &bd_addr);
+}
+
+/* search_service */
+
+static void search_service_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = conn_id_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void search_service_p(int argc, const char **argv)
+{
+	int conn_id;
+
+	RETURN_IF_NULL(if_gatt);
+
+	VERIFY_CONN_ID(2, conn_id);
+
+	/* uuid */
+	if (argc <= 3) {
+		EXEC(if_gatt->client->search_service, conn_id, NULL);
+
+	} else {
+		bt_uuid_t filter_uuid;
+
+		gatt_str2bt_uuid_t(argv[3], -1, &filter_uuid);
+		EXEC(if_gatt->client->search_service, conn_id, &filter_uuid);
+	}
+}
+
+/* get_included_service */
+
+static void get_included_service_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = conn_id_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void get_included_service_p(int argc, const char **argv)
+{
+	int conn_id;
+	btgatt_srvc_id_t srvc_id;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_SRVC_ID(3, &srvc_id);
+
+	EXEC(if_gatt->client->get_included_service, conn_id, &srvc_id, NULL);
+}
+
+/* get_characteristic */
+
+/* Same completion as get_included_service_c */
+#define get_characteristic_c get_included_service_c
+
+static void get_characteristic_p(int argc, const char **argv)
+{
+	int conn_id;
+	btgatt_srvc_id_t srvc_id;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_SRVC_ID(3, &srvc_id);
+
+	EXEC(if_gatt->client->get_characteristic, conn_id, &srvc_id, NULL);
+}
+
+/* get_descriptor */
+
+/* Same completion as get_included_service_c */
+#define get_descriptor_c get_included_service_c
+
+static void get_descriptor_p(int argc, const char **argv)
+{
+	int conn_id;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_gatt_id_t char_id;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_SRVC_ID(3, &srvc_id);
+	VERIFY_CHAR_ID(4, &char_id);
+
+	EXEC(if_gatt->client->get_descriptor, conn_id, &srvc_id, &char_id,
+									NULL);
+}
+
+/* read_characteristic */
+
+/* Same completion as get_included_service_c */
+#define read_characteristic_c get_included_service_c
+
+static void read_characteristic_p(int argc, const char **argv)
+{
+	int conn_id;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_gatt_id_t char_id;
+	int auth_req = 0;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_SRVC_ID(3, &srvc_id);
+	VERIFY_CHAR_ID(4, &char_id);
+
+	/* auth_req */
+	if (argc > 5)
+		auth_req = strtol(argv[5], NULL, 0);
+
+	EXEC(if_gatt->client->read_characteristic, conn_id, &srvc_id, &char_id,
+								auth_req);
+}
+
+/* write_characteristic */
+
+static void write_characteristic_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	/*
+	 * This should be from tGATT_WRITE_TYPE but it's burried
+	 * inside bluedroid guts
+	 */
+	static const char *wrtypes[] = { "1", "2", "3", NULL };
+
+	if (argc == 3) {
+		*user = conn_id_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 6) {
+		*user = wrtypes;
+		*enum_func = enum_strings;
+	}
+}
+
+static void write_characteristic_p(int argc, const char **argv)
+{
+	int conn_id;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_gatt_id_t char_id;
+	int write_type;
+	int len;
+	int auth_req = 0;
+	uint8_t value[100];
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_SRVC_ID(3, &srvc_id);
+	VERIFY_CHAR_ID(4, &char_id);
+
+	/* write type */
+	if (argc <= 5) {
+		haltest_error("No write type specified\n");
+		return;
+	}
+	write_type = strtol(argv[5], NULL, 0);
+
+	GET_VERIFY_HEX_STRING(6, value, len);
+
+	/* auth_req */
+	if (argc > 7)
+		auth_req = strtol(argv[7], NULL, 0);
+
+	EXEC(if_gatt->client->write_characteristic, conn_id, &srvc_id, &char_id,
+				write_type, len, auth_req, (char *) value);
+}
+
+/* read_descriptor */
+
+/* Same completion as get_included_service_c */
+#define read_descriptor_c get_included_service_c
+
+static void read_descriptor_p(int argc, const char **argv)
+{
+	int conn_id;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_gatt_id_t char_id;
+	btgatt_descr_id_t descr_id;
+	int auth_req = 0;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_SRVC_ID(3, &srvc_id);
+	VERIFY_CHAR_ID(4, &char_id);
+	VERIFY_DESCR_ID(5, &descr_id);
+
+	/* auth_req */
+	if (argc > 6)
+		auth_req = strtol(argv[6], NULL, 0);
+
+	EXEC(if_gatt->client->read_descriptor, conn_id, &srvc_id, &char_id,
+							&descr_id, auth_req);
+}
+
+/* write_descriptor */
+
+static void write_descriptor_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	/*
+	 * This should be from tGATT_WRITE_TYPE but it's burried
+	 * inside bluedroid guts
+	 */
+	static const char *wrtypes[] = { "1", "2", "3", NULL };
+
+	if (argc == 3) {
+		*user = conn_id_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 7) {
+		*user = wrtypes;
+		*enum_func = enum_strings;
+	}
+}
+
+static void write_descriptor_p(int argc, const char **argv)
+{
+	int conn_id;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_gatt_id_t char_id;
+	btgatt_descr_id_t descr_id;
+	int write_type;
+	int len;
+	int auth_req = 0;
+	uint8_t value[200] = {0};
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_SRVC_ID(3, &srvc_id);
+	VERIFY_CHAR_ID(4, &char_id);
+	VERIFY_DESCR_ID(5, &descr_id);
+
+	/* write type */
+	if (argc <= 6) {
+		haltest_error("No write type specified\n");
+		return;
+	}
+	write_type = strtol(argv[6], NULL, 0);
+
+	/* value */
+	if (argc <= 7) {
+		haltest_error("No value specified\n");
+		return;
+	}
+
+	/* len in chars */
+	if (strncmp(argv[7], "0X", 2) && strncmp(argv[7], "0x", 2)) {
+		haltest_error("Value must be hex string");
+		return;
+	}
+
+	len = fill_buffer(argv[7] + 2, value, sizeof(value));
+
+	/* auth_req */
+	if (argc > 8)
+		auth_req = strtol(argv[8], NULL, 0);
+
+	EXEC(if_gatt->client->write_descriptor, conn_id, &srvc_id, &char_id,
+			&descr_id, write_type, len, auth_req, (char *) value);
+}
+
+/* execute_write */
+
+/* Same completion as search_service */
+#define execute_write_c search_service_c
+
+static void execute_write_p(int argc, const char **argv)
+{
+	int conn_id;
+	int execute;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+
+	/* execute */
+	if (argc <= 3) {
+		haltest_error("No execute specified\n");
+		return;
+	}
+	execute = strtol(argv[3], NULL, 0);
+
+	EXEC(if_gatt->client->execute_write, conn_id, execute);
+}
+
+/* register_for_notification */
+
+static void register_for_notification_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void register_for_notification_p(int argc, const char **argv)
+{
+	int client_if;
+	bt_bdaddr_t bd_addr;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_gatt_id_t char_id;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+	VERIFY_SRVC_ID(4, &srvc_id);
+	VERIFY_CHAR_ID(5, &char_id);
+
+	EXEC(if_gatt->client->register_for_notification, client_if, &bd_addr,
+							&srvc_id, &char_id);
+}
+
+/* deregister_for_notification */
+
+/* Same completion as search_service */
+#define deregister_for_notification_c register_for_notification_c
+
+static void deregister_for_notification_p(int argc, const char **argv)
+{
+	int client_if;
+	bt_bdaddr_t bd_addr;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_gatt_id_t char_id;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+	VERIFY_SRVC_ID(4, &srvc_id);
+	VERIFY_CHAR_ID(5, &char_id);
+
+	EXEC(if_gatt->client->deregister_for_notification, client_if, &bd_addr,
+							&srvc_id, &char_id);
+}
+
+/* read_remote_rssi */
+
+static void read_remote_rssi_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*enum_func = enum_devices;
+	}
+}
+
+static void read_remote_rssi_p(int argc, const char **argv)
+{
+	int client_if;
+	bt_bdaddr_t bd_addr;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+
+	EXEC(if_gatt->client->read_remote_rssi, client_if, &bd_addr);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+/* scan filter parameter setup */
+static void scan_filter_param_setup_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void scan_filter_param_setup_p(int argc, const char **argv)
+{
+	int client_if;
+	int action;
+	int filt_index;
+	int feat_seln;
+	int list_logic_type, filt_logic_type;
+	int rssi_high_thres, rssi_low_thres;
+	int dely_mode;
+	int found_timeout, lost_timeout, found_timeout_cnt;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ACTION(3, action);
+	VERIFY_FILT_INDEX(4, filt_index);
+	VERIFY_FEAT_SELN(5, feat_seln);
+	VERIFY_LIST_LOGIC_TYPE(6, list_logic_type);
+	VERIFY_FILT_LOGIC_TYPE(7, filt_logic_type);
+	VERIFY_RSSI_HI_THR(8, rssi_high_thres);
+	VERIFY_RSSI_LOW_THR(9, rssi_low_thres);
+	VERIFY_DELY_MODE(10, dely_mode);
+	VERIFY_FND_TIME(11, found_timeout);
+	VERIFY_LOST_TIME(12, lost_timeout);
+	VERIFY_FND_TIME_CNT(13, found_timeout_cnt);
+
+	EXEC(if_gatt->client->scan_filter_param_setup, client_if, action,
+		filt_index, feat_seln, list_logic_type, filt_logic_type,
+		rssi_high_thres, rssi_low_thres, dely_mode, found_timeout,
+		lost_timeout, found_timeout_cnt);
+}
+
+/* scan filter add remove */
+
+static void scan_filter_add_remove_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 10) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void scan_filter_add_remove_p(int argc, const char **argv)
+{
+	int client_if;
+	int action;
+	int filt_type;
+	int filt_index;
+	int company_id;
+	int company_id_mask;
+	bt_uuid_t p_uuid;
+	bt_uuid_t p_uuid_mask;
+	bt_bdaddr_t bd_addr;
+	char addr_type;
+	int data_len;
+	uint8_t p_data[100];
+	int mask_len;
+	uint8_t p_mask[100];
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ACTION(3, action);
+	VERIFY_FILT_TYPE(4, filt_type);
+	VERIFY_FILT_INDEX(5, filt_index);
+	VERIFY_COMP_ID(6, company_id);
+	VERIFY_COMP_ID_MASK(7, company_id_mask);
+
+	if (argc <= 9) {
+		haltest_error("No p_uuid, p_uuid_mask specified\n");
+		return;
+	}
+	gatt_str2bt_uuid_t(argv[8], -1, &p_uuid);
+	gatt_str2bt_uuid_t(argv[9], -1, &p_uuid_mask);
+
+	VERIFY_UUID(8, &p_uuid);
+	VERIFY_UUID(9, &p_uuid_mask);
+	VERIFY_ADDR_ARG(10, &bd_addr);
+	VERIFY_ADDR_TYPE(11, addr_type);
+	GET_VERIFY_HEX_STRING(12, p_data, data_len);
+	GET_VERIFY_HEX_STRING(13, p_mask, mask_len);
+
+	EXEC(if_gatt->client->scan_filter_add_remove, client_if, action,
+		filt_type, filt_index, company_id, company_id_mask,
+		&p_uuid, &p_uuid_mask, &bd_addr, addr_type, data_len,
+		(char *) p_data, mask_len, (char *) p_mask);
+}
+
+/* scan filter clean */
+static void scan_filter_clear_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void scan_filter_clear_p(int argc, const char **argv)
+{
+	int client_if;
+	int filt_index;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_FILT_INDEX(3, filt_index);
+
+	EXEC(if_gatt->client->scan_filter_clear, client_if, filt_index);
+}
+
+/* scan filter enable */
+static void scan_filter_enable_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void scan_filter_enable_p(int argc, const char **argv)
+{
+	int client_if;
+	int enable = 0;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+
+	/* enable */
+	if (argc >= 4)
+		enable = strtol(argv[3], NULL, 0);
+
+	EXEC(if_gatt->client->scan_filter_clear, client_if, enable);
+}
+
+/* set advertising data */
+static void set_adv_data_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void set_adv_data_p(int argc, const char **argv)
+{
+	int client_if;
+	bool set_scan_rsp;
+	bool include_name, include_txpower;
+	int min_interval, max_interval;
+	int appearance;
+	uint16_t manufacturer_len;
+	uint8_t manufacturer_data[100];
+	uint16_t service_data_len;
+	uint8_t service_data[100];
+	uint16_t service_uuid_len;
+	uint8_t service_uuid[100];
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+
+	/* set scan response */
+	if (argc >= 4)
+		set_scan_rsp = strtol(argv[3], NULL, 0);
+	/* include name */
+	if (argc >= 5)
+		include_name = strtol(argv[4], NULL, 0);
+	/* include txpower */
+	if (argc >= 6)
+		include_txpower = strtol(argv[5], NULL, 0);
+
+	VERIFY_MIN_INTERVAL(6, min_interval);
+	VERIFY_MAX_INTERVAL(7, max_interval);
+	VERIFY_APPEARANCE(8, appearance);
+	GET_VERIFY_HEX_STRING(9, manufacturer_data, manufacturer_len);
+	GET_VERIFY_HEX_STRING(10, service_data, service_data_len);
+	GET_VERIFY_HEX_STRING(11, service_uuid, service_uuid_len);
+
+	EXEC(if_gatt->client->set_adv_data, client_if, set_scan_rsp,
+		include_name, include_txpower, min_interval, max_interval,
+		appearance, manufacturer_len, (char *) manufacturer_data,
+		service_data_len, (char *) service_data, service_uuid_len,
+		(char *) service_uuid);
+}
+
+/* configure mtu */
+static void configure_mtu_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = conn_id_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void configure_mtu_p(int argc, const char **argv)
+{
+	int conn_id;
+	int mtu;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_MTU(3, mtu);
+
+	EXEC(if_gatt->client->configure_mtu, conn_id, mtu);
+}
+
+/* con parameter update */
+static void conn_parameter_update_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void conn_parameter_update_p(int argc, const char **argv)
+{
+	bt_bdaddr_t bd_addr;
+	int min_interval, max_interval;
+	int latency;
+	int timeout;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_ADDR_ARG(2, &bd_addr);
+	VERIFY_MIN_INTERVAL(3, min_interval);
+	VERIFY_MAX_INTERVAL(4, max_interval);
+	VERIFY_LATENCY(5, latency);
+	VERIFY_TIMEOUT(6, timeout);
+
+	EXEC(if_gatt->client->conn_parameter_update, &bd_addr, min_interval,
+						max_interval, latency, timeout);
+}
+
+/* set scan parameters */
+static void set_scan_parameters_p(int argc, const char **argv)
+{
+	int scan_interval;
+	int scan_window;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SCAN_INTERVAL(2, scan_interval);
+	VERIFY_SCAN_WINDOW(3, scan_window);
+
+	EXEC(if_gatt->client->set_scan_parameters, scan_interval, scan_window);
+}
+
+/* enable multi advertising */
+static void multi_adv_enable_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void multi_adv_enable_p(int argc, const char **argv)
+{
+	int client_if;
+	int min_interval, max_interval;
+	int adv_type;
+	int chnl_map;
+	int tx_power;
+	int timeout_s;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_MIN_INTERVAL(3, min_interval);
+	VERIFY_MAX_INTERVAL(4, max_interval);
+	VERIFY_ADV_TYPE(5, adv_type);
+	VERIFY_CHNL_MAP(6, chnl_map);
+	VERIFY_TX_POWER(7, tx_power);
+	VERIFY_TIMEOUT_S(8, timeout_s);
+
+	EXEC(if_gatt->client->multi_adv_enable, client_if, min_interval,
+			max_interval, adv_type, chnl_map, tx_power, timeout_s);
+}
+
+/* update multi advertiser */
+static void multi_adv_update_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void multi_adv_update_p(int argc, const char **argv)
+{
+	int client_if;
+	int min_interval, max_interval;
+	int adv_type;
+	int chnl_map;
+	int tx_power;
+	int timeout_s;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_MIN_INTERVAL(3, min_interval);
+	VERIFY_MAX_INTERVAL(4, max_interval);
+	VERIFY_ADV_TYPE(5, adv_type);
+	VERIFY_CHNL_MAP(6, chnl_map);
+	VERIFY_TX_POWER(7, tx_power);
+	VERIFY_TIMEOUT_S(8, timeout_s);
+
+	EXEC(if_gatt->client->multi_adv_update, client_if, min_interval,
+			max_interval, adv_type, chnl_map, tx_power, timeout_s);
+}
+
+/* set advertising data */
+static void multi_adv_set_inst_data_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void multi_adv_set_inst_data_p(int argc, const char **argv)
+{
+	int client_if;
+	bool set_scan_rsp;
+	bool include_name, include_txpower;
+	int appearance;
+	uint16_t manufacturer_len;
+	uint8_t manufacturer_data[100];
+	uint16_t service_data_len;
+	uint8_t service_data[100];
+	uint16_t service_uuid_len;
+	uint8_t service_uuid[100];
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+
+	/* set scan response */
+	if (argc >= 4)
+		set_scan_rsp = strtol(argv[3], NULL, 0);
+	/* include name */
+	if (argc >= 5)
+		include_name = strtol(argv[4], NULL, 0);
+	/* include txpower */
+	if (argc >= 6)
+		include_txpower = strtol(argv[5], NULL, 0);
+
+	VERIFY_APPEARANCE(6, appearance);
+	GET_VERIFY_HEX_STRING(7, manufacturer_data, manufacturer_len);
+	GET_VERIFY_HEX_STRING(8, service_data, service_data_len);
+	GET_VERIFY_HEX_STRING(9, service_uuid, service_uuid_len);
+
+	EXEC(if_gatt->client->multi_adv_set_inst_data, client_if, set_scan_rsp,
+		include_name, include_txpower, appearance, manufacturer_len,
+		(char *) manufacturer_data, service_data_len,
+		(char *) service_data, service_uuid_len, (char *) service_uuid);
+}
+
+/* multi advertising disable */
+static void multi_adv_disable_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void multi_adv_disable_p(int argc, const char **argv)
+{
+	int client_if;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+
+	EXEC(if_gatt->client->multi_adv_disable, client_if);
+}
+
+/* batch scanconfigure storage */
+static void batchscan_cfg_storage_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void batchscan_cfg_storage_p(int argc, const char **argv)
+{
+	int client_if;
+	int batch_scan_full_max;
+	int batch_scan_trunc_max;
+	int batch_scan_notify_threshold;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_BATCH_SCAN_FULL_MAX(3, batch_scan_full_max);
+	VERIFY_BATCH_SCAN_TRUNC_MAX(4, batch_scan_trunc_max);
+	VERIFY_BATCH_SCAN_NOTIFY_THR(5, batch_scan_notify_threshold);
+
+	EXEC(if_gatt->client->batchscan_cfg_storage, client_if,
+				batch_scan_full_max, batch_scan_trunc_max,
+				batch_scan_notify_threshold);
+}
+
+/* enable batch scan */
+static void batchscan_enb_batch_scan_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void batchscan_enb_batch_scan_p(int argc, const char **argv)
+{
+	int client_if;
+	int scan_mode, scan_interval, scan_window;
+	int addr_type;
+	int discard_rule;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_SCAN_MODE(3, scan_mode);
+	VERIFY_SCAN_INTERVAL(4, scan_interval);
+	VERIFY_SCAN_WINDOW(5, scan_window);
+	VERIFY_ADDR_TYPE(6, addr_type);
+	VERIFY_DISCARD_RULE(7, discard_rule);
+
+	EXEC(if_gatt->client->batchscan_enb_batch_scan, client_if, scan_mode,
+			scan_interval, scan_window, addr_type, discard_rule);
+}
+
+/* batchscan disable */
+static void batchscan_dis_batch_scan_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void batchscan_dis_batch_scan_p(int argc, const char **argv)
+{
+	int client_if;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+
+	EXEC(if_gatt->client->batchscan_dis_batch_scan, client_if);
+}
+
+/* batchscan read reports */
+static void batchscan_read_reports_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void batchscan_read_reports_p(int argc, const char **argv)
+{
+	int client_if;
+	int scan_mode;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_SCAN_MODE(3, scan_mode);
+
+	EXEC(if_gatt->client->batchscan_read_reports, client_if, scan_mode);
+}
+#endif
+
+/* get_device_type */
+
+static void get_device_type_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3)
+		*enum_func = enum_devices;
+}
+
+static void get_device_type_p(int argc, const char **argv)
+{
+	bt_bdaddr_t bd_addr;
+	int dev_type;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_ADDR_ARG(2, &bd_addr);
+
+	dev_type = if_gatt->client->get_device_type(&bd_addr);
+	haltest_info("%s: %d\n", "get_device_type", dev_type);
+}
+
+/* test_command */
+
+static void test_command_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 4)
+		*enum_func = enum_devices;
+}
+
+static void test_command_p(int argc, const char **argv)
+{
+	int command;
+	int i;
+	bt_bdaddr_t bd_addr;
+	bt_uuid_t uuid;
+	btgatt_test_params_t params = {
+		.bda1 = &bd_addr,
+		.uuid1 = &uuid
+	};
+	uint16_t *u = &params.u1;
+
+	RETURN_IF_NULL(if_gatt);
+
+	/* command */
+	if (argc <= 2) {
+		haltest_error("No command specified\n");
+		return;
+	}
+	command = strtol(argv[2], NULL, 0);
+
+	VERIFY_ADDR_ARG(3, &bd_addr);
+	VERIFY_UUID(4, &uuid);
+
+	for (i = 5; i < argc; i++)
+		VERIFY_TEST_ARG(i, *u++);
+
+	EXEC(if_gatt->client->test_command, command, &params);
+}
+
+static struct method client_methods[] = {
+	STD_METHODH(register_client, "[<uuid>]"),
+	STD_METHODCH(unregister_client, "<client_if>"),
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	STD_METHODCH(scan, "[1|0]"),
+	STD_METHODCH(connect, "<client_if> <addr> [<is_direct>] [<transport]"),
+	STD_METHODCH(scan_filter_param_setup, "<client_if> <action>"
+			" <filt_index> <feat_seln> <list_logic_type>"
+			" <filt_logic_type> <rssi_high_thres> <rssi_low_thres>"
+			" <dely_mode> <found_timeout> <lost_timeout>"
+			" <found_timeout_cnt>"),
+	STD_METHODCH(scan_filter_add_remove, "<client_if> <action> <filt_type>"
+			" <filt_index> <company_id> <company_id_mask>"
+			" [<p_uuid>] <p_uuid_mask> <addr> <addr_type>"
+			" [<p_data>] [<p_mask>]"),
+	STD_METHODCH(scan_filter_clear, "<client_if> <filt_index>"),
+	STD_METHODCH(scan_filter_enable, "<client_if> [<enable>]"),
+	STD_METHODCH(set_adv_data, "<client_if> [<set_scan_rsp>] <include_name>"
+			" [<include_txpower>] <min_interval> <max_interval>"
+			" <appearance> [<manufacturer_data>] [<service_data>]"
+			" [<service_uuid>]"),
+	STD_METHODCH(configure_mtu, "<conn_id> <mtu>"),
+	STD_METHODCH(conn_parameter_update, "<bd_addr> <min_interval>"
+					" <max_interval> <latency> <timeout>"),
+	STD_METHODH(set_scan_parameters, "<scan_inverval> <scan_window>"),
+	STD_METHODCH(multi_adv_enable, "<client_if> <min_interval>"
+			" <max_interval> <adv_type> <chnl_map> <tx_power>"
+			" <timeout_s>"),
+	STD_METHODCH(multi_adv_update, "<client_if> <min_interval>"
+			" <max_interval> <adv_type> <chnl_map> <tx_power>"
+			" <timeout_s>"),
+	STD_METHODCH(multi_adv_set_inst_data, "<client_if> [<set_scan_rsp>]"
+			" <include_name> [<include_txpower>] <appearance>"
+			" [<manufacturer_data>] [<service_data>]"
+			" [<service_uuid>]"),
+	STD_METHODCH(multi_adv_disable, "<client_if>"),
+	STD_METHODCH(batchscan_cfg_storage, "<client_if> <batch_scan_full_max>"
+					" <batch_scan_trunc_max>"
+					" <batch_scan_notify_threshold>"),
+	STD_METHODCH(batchscan_enb_batch_scan, "<client_if> <scan_mode>"
+			" <scan_interval> <scan_window> <addr_type>"
+			" <discard_rule>"),
+	STD_METHODCH(batchscan_dis_batch_scan, "<client_if>"),
+	STD_METHODCH(batchscan_read_reports, "<client_if> <scan_mode>"),
+#else
+	STD_METHODCH(scan, "<client_if> [1|0]"),
+	STD_METHODCH(connect, "<client_if> <addr> [<is_direct>]"),
+#endif
+	STD_METHODCH(disconnect, "<client_if> <addr> <conn_id>"),
+	STD_METHODCH(refresh, "<client_if> <addr>"),
+	STD_METHODCH(search_service, "<conn_id> [<uuid>]"),
+	STD_METHODCH(get_included_service, "<conn_id> <srvc_id>"),
+	STD_METHODCH(get_characteristic, "<conn_id> <srvc_id>"),
+	STD_METHODCH(get_descriptor, "<conn_id> <srvc_id> <char_id>"),
+	STD_METHODCH(read_characteristic,
+			"<conn_id> <srvc_id> <char_id> [<auth_req>]"),
+	STD_METHODCH(write_characteristic,
+			"<conn_id> <srvc_id> <char_id> <write_type> <hex_value> [<auth_req>]"),
+	STD_METHODCH(read_descriptor,
+			"<conn_id> <srvc_id> <char_id> <descr_id> [<auth_req>]"),
+	STD_METHODCH(write_descriptor,
+			"<conn_id> <srvc_id> <char_id> <descr_id> <write_type> <hex_value> [<auth_req>]"),
+	STD_METHODCH(execute_write, "<conn_id> <execute>"),
+	STD_METHODCH(register_for_notification,
+			"<client_if> <addr> <srvc_id> <char_id>"),
+	STD_METHODCH(deregister_for_notification,
+			"<client_if> <addr> <srvc_id> <char_id>"),
+	STD_METHODCH(read_remote_rssi, "<client_if> <addr>"),
+	STD_METHODCH(get_device_type, "<addr>"),
+	STD_METHODCH(test_command,
+			"<cmd> <addr> <uuid> [u1] [u2] [u3] [u4] [u5]"),
+	STD_METHODCH(listen, "<client_if> [1|0]"),
+	END_METHOD
+};
+
+const struct interface gatt_client_if = {
+	.name = "gattc",
+	.methods = client_methods
+};
+
+/* gatt server methods */
+
+/* register_server */
+
+static void gatts_register_server_p(int argc, const char *argv[])
+{
+	bt_uuid_t uuid;
+
+	RETURN_IF_NULL(if_gatt);
+
+	/* uuid */
+	if (argc <= 2)
+		gatt_str2bt_uuid_t("bed4babe", -1, &uuid);
+	else
+		gatt_str2bt_uuid_t(argv[2], -1, &uuid);
+
+	EXEC(if_gatt->server->register_server, &uuid);
+}
+
+/* unregister_server */
+
+static void gatts_unregister_server_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = server_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void gatts_unregister_server_p(int argc, const char *argv[])
+{
+	int server_if;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+
+	EXEC(if_gatt->server->unregister_server, server_if);
+}
+
+/* connect */
+
+static void gatts_connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = server_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+static void gatts_connect_p(int argc, const char *argv[])
+{
+	int server_if;
+	bt_bdaddr_t bd_addr;
+	int is_direct = 1;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	int transport = 1;
+#endif
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+
+	/* is_direct */
+	if (argc > 4)
+		is_direct = strtol(argv[4], NULL, 0);
+
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+	EXEC(if_gatt->server->connect, server_if, &bd_addr, is_direct);
+#else
+	/* transport */
+	if (argc > 5)
+		transport = strtol(argv[5], NULL, 0);
+
+	EXEC(if_gatt->server->connect, server_if, &bd_addr, is_direct,
+								transport);
+#endif
+}
+
+/* disconnect */
+
+static void gatts_disconnect_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = server_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	} else if (argc == 5) {
+		*user = conn_id_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void gatts_disconnect_p(int argc, const char *argv[])
+{
+	int server_if;
+	bt_bdaddr_t bd_addr;
+	int conn_id;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+	VERIFY_CONN_ID(4, conn_id);
+
+	EXEC(if_gatt->server->disconnect, server_if, &bd_addr, conn_id);
+}
+
+/* add_service */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_add_service_c gatts_unregister_server_c
+
+static void gatts_add_service_p(int argc, const char *argv[])
+{
+	int server_if;
+	btgatt_srvc_id_t srvc_id;
+	int num_handles;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_SRVC_ID(3, &srvc_id);
+
+	/* num handles */
+	if (argc <= 4) {
+		haltest_error("No num_handles specified\n");
+		return;
+	}
+	num_handles = strtol(argv[4], NULL, 0);
+
+	EXEC(if_gatt->server->add_service, server_if, &srvc_id, num_handles);
+}
+
+/* add_included_service */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_add_included_service_c gatts_unregister_server_c
+
+static void gatts_add_included_service_p(int argc, const char *argv[])
+{
+	int server_if;
+	int service_handle;
+	int included_handle;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_SERVICE_HANDLE(3, service_handle);
+	VERIFY_HANDLE(4, included_handle);
+
+	EXEC(if_gatt->server->add_included_service, server_if, service_handle,
+							included_handle);
+}
+
+/* add_characteristic */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_add_characteristic_c gatts_unregister_server_c
+
+static void gatts_add_characteristic_p(int argc, const char *argv[])
+{
+	int server_if;
+	int service_handle;
+	int properties;
+	int permissions;
+	bt_uuid_t uuid;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_SERVICE_HANDLE(3, service_handle);
+	VERIFY_UUID(4, &uuid);
+
+	/* properties */
+	if (argc <= 5) {
+		haltest_error("No properties specified\n");
+		return;
+	}
+	properties = strtol(argv[5], NULL, 0);
+
+	/* permissions */
+	if (argc <= 6) {
+		haltest_error("No permissions specified\n");
+		return;
+	}
+	permissions = strtol(argv[6], NULL, 0);
+
+	EXEC(if_gatt->server->add_characteristic, server_if, service_handle,
+						&uuid, properties, permissions);
+}
+
+/* add_descriptor */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_add_descriptor_c gatts_unregister_server_c
+
+static void gatts_add_descriptor_p(int argc, const char *argv[])
+{
+	int server_if;
+	int service_handle;
+	int permissions;
+	bt_uuid_t uuid;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_SERVICE_HANDLE(3, service_handle);
+	VERIFY_UUID(4, &uuid);
+
+	/* permissions */
+	if (argc <= 5) {
+		haltest_error("No permissions specified\n");
+		return;
+	}
+	permissions = strtol(argv[5], NULL, 0);
+
+	EXEC(if_gatt->server->add_descriptor, server_if, service_handle, &uuid,
+								permissions);
+}
+
+/* start_service */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_start_service_c gatts_unregister_server_c
+
+static void gatts_start_service_p(int argc, const char *argv[])
+{
+	int server_if;
+	int service_handle;
+	int transport;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_SERVICE_HANDLE(3, service_handle);
+
+	/* transport */
+	if (argc <= 4) {
+		haltest_error("No transport specified\n");
+		return;
+	}
+	transport = strtol(argv[4], NULL, 0);
+
+	EXEC(if_gatt->server->start_service, server_if, service_handle,
+								transport);
+}
+
+/* stop_service */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_stop_service_c gatts_unregister_server_c
+
+static void gatts_stop_service_p(int argc, const char *argv[])
+{
+	int server_if;
+	int service_handle;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_SERVICE_HANDLE(3, service_handle);
+
+	EXEC(if_gatt->server->stop_service, server_if, service_handle);
+}
+
+/* delete_service */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_delete_service_c gatts_unregister_server_c
+
+static void gatts_delete_service_p(int argc, const char *argv[])
+{
+	int server_if;
+	int service_handle;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_SERVICE_HANDLE(3, service_handle);
+
+	EXEC(if_gatt->server->delete_service, server_if, service_handle);
+}
+
+/* send_indication */
+
+static void gatts_send_indication_p(int argc, const char *argv[])
+{
+	int server_if;
+	int attr_handle;
+	int conn_id;
+	int confirm;
+	char data[200];
+	int len = 0;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_HANDLE(3, attr_handle);
+	VERIFY_CONN_ID(4, conn_id);
+
+	/* confirm */
+	if (argc <= 5) {
+		haltest_error("No transport specified\n");
+		return;
+	}
+	confirm = strtol(argv[5], NULL, 0);
+
+	GET_VERIFY_HEX_STRING(6, data, len);
+
+	EXEC(if_gatt->server->send_indication, server_if, attr_handle, conn_id,
+							len, confirm, data);
+}
+
+/* send_response */
+
+static void gatts_send_response_p(int argc, const char *argv[])
+{
+	int conn_id;
+	int trans_id;
+	int status;
+	btgatt_response_t data;
+
+	memset(&data, 0, sizeof(data));
+
+	RETURN_IF_NULL(if_gatt);
+
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_TRANS_ID(3, trans_id);
+	VERIFY_STATUS(4, status);
+	VERIFY_HANDLE(5, data.attr_value.handle);
+	VERIFY_OFFSET(6, data.attr_value.offset);
+
+	data.attr_value.auth_req = 0;
+	data.attr_value.len = 0;
+
+	GET_VERIFY_HEX_STRING(7, data.attr_value.value, data.attr_value.len);
+
+	haltest_info("conn_id %d, trans_id %d, status %d", conn_id, trans_id,
+									status);
+
+	EXEC(if_gatt->server->send_response, conn_id, trans_id, status, &data);
+}
+
+#define GATTS_METHODH(n, h) METHOD(#n, gatts_##n##_p, NULL, h)
+#define GATTS_METHODCH(n, h) METHOD(#n, gatts_##n##_p, gatts_##n##_c, h)
+
+static struct method server_methods[] = {
+	GATTS_METHODH(register_server, "[<uuid>]"),
+	GATTS_METHODCH(unregister_server, "<server_if>"),
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	GATTS_METHODCH(connect, "<server_if> <addr> [<is_direct>] [<transport>]"),
+#else
+	GATTS_METHODCH(connect, "<server_if> <addr> [<is_direct>]"),
+#endif
+	GATTS_METHODCH(disconnect, "<server_if> <addr> <conn_id>"),
+	GATTS_METHODCH(add_service, "<server_if> <srvc_id> <num_handles>"),
+	GATTS_METHODCH(add_included_service,
+			"<server_if> <service_handle> <included_handle>"),
+	GATTS_METHODCH(add_characteristic,
+		"<server_if> <service_handle> <uuid> <properites> <permissions>"),
+	GATTS_METHODCH(add_descriptor,
+			"<server_if> <service_handle> <uuid> <permissions>"),
+	GATTS_METHODCH(start_service,
+				"<server_if> <service_handle> <transport>"),
+	GATTS_METHODCH(stop_service, "<server_if> <service_handle>"),
+	GATTS_METHODCH(delete_service, "<server_if> <service_handle>"),
+	GATTS_METHODH(send_indication,
+			"<server_if> <attr_handle> <conn_id> <confirm> [<data>]"),
+	GATTS_METHODH(send_response,
+		"<conn_id> <trans_id> <status> <handle> <offset> [<data>]"),
+	END_METHOD
+};
+
+const struct interface gatt_server_if = {
+	.name = "gatts",
+	.methods = server_methods
+};
diff --git a/repo/android/client/if-hf-client.c b/repo/android/client/if-hf-client.c
new file mode 100644
index 0000000..3f6f7c2
--- /dev/null
+++ b/repo/android/client/if-hf-client.c
@@ -0,0 +1,668 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const bthf_client_interface_t *if_hf_client = NULL;
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+SINTMAP(bthf_client_connection_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CONNECTION_STATE_DISCONNECTED),
+	DELEMENT(BTHF_CLIENT_CONNECTION_STATE_CONNECTING),
+	DELEMENT(BTHF_CLIENT_CONNECTION_STATE_CONNECTED),
+	DELEMENT(BTHF_CLIENT_CONNECTION_STATE_SLC_CONNECTED),
+	DELEMENT(BTHF_CLIENT_CONNECTION_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(bthf_client_audio_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_AUDIO_STATE_DISCONNECTED),
+	DELEMENT(BTHF_CLIENT_AUDIO_STATE_CONNECTING),
+	DELEMENT(BTHF_CLIENT_AUDIO_STATE_CONNECTED),
+	DELEMENT(BTHF_CLIENT_AUDIO_STATE_CONNECTED_MSBC),
+ENDMAP
+
+SINTMAP(bthf_client_vr_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_VR_STATE_STOPPED),
+	DELEMENT(BTHF_CLIENT_VR_STATE_STARTED),
+ENDMAP
+
+SINTMAP(bthf_client_network_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_NETWORK_STATE_NOT_AVAILABLE),
+	DELEMENT(BTHF_CLIENT_NETWORK_STATE_AVAILABLE),
+ENDMAP
+
+SINTMAP(bthf_client_service_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_SERVICE_TYPE_HOME),
+	DELEMENT(BTHF_CLIENT_SERVICE_TYPE_ROAMING),
+ENDMAP
+
+SINTMAP(bthf_client_call_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CALL_NO_CALLS_IN_PROGRESS),
+	DELEMENT(BTHF_CLIENT_CALL_CALLS_IN_PROGRESS),
+ENDMAP
+
+SINTMAP(bthf_client_callsetup_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CALLSETUP_NONE),
+	DELEMENT(BTHF_CLIENT_CALLSETUP_INCOMING),
+	DELEMENT(BTHF_CLIENT_CALLSETUP_OUTGOING),
+	DELEMENT(BTHF_CLIENT_CALLSETUP_ALERTING),
+ENDMAP
+
+SINTMAP(bthf_client_callheld_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CALLHELD_NONE),
+	DELEMENT(BTHF_CLIENT_CALLHELD_HOLD_AND_ACTIVE),
+	DELEMENT(BTHF_CLIENT_CALLHELD_HOLD),
+ENDMAP
+
+SINTMAP(bthf_client_resp_and_hold_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_RESP_AND_HOLD_HELD),
+	DELEMENT(BTRH_CLIENT_RESP_AND_HOLD_ACCEPT),
+	DELEMENT(BTRH_CLIENT_RESP_AND_HOLD_REJECT),
+ENDMAP
+
+SINTMAP(bthf_client_call_direction_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CALL_DIRECTION_OUTGOING),
+	DELEMENT(BTHF_CLIENT_CALL_DIRECTION_INCOMING),
+ENDMAP
+
+SINTMAP(bthf_client_call_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CALL_STATE_ACTIVE),
+	DELEMENT(BTHF_CLIENT_CALL_STATE_HELD),
+	DELEMENT(BTHF_CLIENT_CALL_STATE_DIALING),
+	DELEMENT(BTHF_CLIENT_CALL_STATE_ALERTING),
+	DELEMENT(BTHF_CLIENT_CALL_STATE_INCOMING),
+	DELEMENT(BTHF_CLIENT_CALL_STATE_WAITING),
+	DELEMENT(BTHF_CLIENT_CALL_STATE_HELD_BY_RESP_HOLD),
+ENDMAP
+
+SINTMAP(bthf_client_call_mpty_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CALL_MPTY_TYPE_SINGLE),
+	DELEMENT(BTHF_CLIENT_CALL_MPTY_TYPE_MULTI),
+ENDMAP
+
+SINTMAP(bthf_client_volume_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_VOLUME_TYPE_SPK),
+	DELEMENT(BTHF_CLIENT_VOLUME_TYPE_MIC),
+ENDMAP
+
+SINTMAP(bthf_client_cmd_complete_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_OK),
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR),
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_CARRIER),
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_BUSY),
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_ANSWER),
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_DELAYED),
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_BLACKLISTED),
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_CME),
+ENDMAP
+
+SINTMAP(bthf_client_subscriber_service_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_SERVICE_UNKNOWN),
+	DELEMENT(BTHF_CLIENT_SERVICE_VOICE),
+	DELEMENT(BTHF_CLIENT_SERVICE_FAX),
+ENDMAP
+
+SINTMAP(bthf_client_in_band_ring_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_IN_BAND_RINGTONE_NOT_PROVIDED),
+	DELEMENT(BTHF_CLIENT_IN_BAND_RINGTONE_PROVIDED),
+ENDMAP
+
+SINTMAP(bthf_client_call_action_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_0),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_1),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_2),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_3),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_4),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_1x),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_2x),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_ATA),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHUP),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_BTRH_0),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_BTRH_1),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_BTRH_2),
+ENDMAP
+
+/* Callbacks */
+
+static char features_str[512];
+
+static const char *pear_features_t2str(int feat)
+{
+	memset(features_str, 0, sizeof(features_str));
+
+	sprintf(features_str, "BTHF_CLIENT_PEER_FEAT_3WAY: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_ECNR: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_VREC: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_INBAND: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_VTAG: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_REJECT: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_ECS: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_ECC: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_EXTERR: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_CODEC: %s,\n",
+			feat & BTHF_CLIENT_PEER_FEAT_3WAY ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_ECNR ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_VREC ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_INBAND ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_VTAG ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_REJECT ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_ECS ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_ECC ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_EXTERR ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_CODEC ? "True" : "False");
+
+	return features_str;
+}
+
+static const char *chld_features_t2str(int feat)
+{
+	memset(features_str, 0, sizeof(features_str));
+
+	sprintf(features_str,
+		"BTHF_CLIENT_CHLD_FEAT_REL: %s,\n"
+		"BTHF_CLIENT_CHLD_FEAT_REL_ACC: %s,\n"
+		"BTHF_CLIENT_CHLD_FEAT_REL_X: %s,\n"
+		"BTHF_CLIENT_CHLD_FEAT_HOLD_ACC: %s,\n"
+		"BTHF_CLIENT_CHLD_FEAT_PRIV_X: %s,\n"
+		"BTHF_CLIENT_CHLD_FEAT_MERGE: %s,\n"
+		"BTHF_CLIENT_CHLD_FEAT_MERGE_DETACH: %s,\n",
+		feat & BTHF_CLIENT_CHLD_FEAT_REL ? "True" : "False",
+		feat & BTHF_CLIENT_CHLD_FEAT_REL_ACC ? "True" : "False",
+		feat & BTHF_CLIENT_CHLD_FEAT_REL_X ? "True" : "False",
+		feat & BTHF_CLIENT_CHLD_FEAT_HOLD_ACC ? "True" : "False",
+		feat & BTHF_CLIENT_CHLD_FEAT_PRIV_X ? "True" : "False",
+		feat & BTHF_CLIENT_CHLD_FEAT_MERGE ? "True" : "False",
+		feat & BTHF_CLIENT_CHLD_FEAT_MERGE_DETACH ? "True" : "False");
+
+	return features_str;
+}
+
+/* Callback for connection state change. */
+static void hf_client_connection_state_callback(
+					bthf_client_connection_state_t state,
+					unsigned int peer_feat,
+					unsigned int chld_feat,
+					bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+				bthf_client_connection_state_t2str(state),
+				bt_bdaddr_t2str(bd_addr, last_addr));
+
+	if (state != BTHF_CLIENT_CONNECTION_STATE_CONNECTED)
+		return;
+
+	haltest_info("\tpeer_features%s\n", pear_features_t2str(peer_feat));
+	haltest_info("\tchld_feat=%s\n", chld_features_t2str(chld_feat));
+}
+
+/* Callback for audio connection state change. */
+static void hf_client_audio_state_callback(bthf_client_audio_state_t state,
+							bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+				bthf_client_audio_state_t2str(state),
+				bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+/* Callback for VR connection state change. */
+static void hf_client_vr_cmd_callback(bthf_client_vr_state_t state)
+{
+	haltest_info("%s: vr_state=%s\n", __func__,
+					bthf_client_vr_state_t2str(state));
+}
+
+/* Callback for network state change */
+static void hf_client_network_state_callback(bthf_client_network_state_t state)
+{
+	haltest_info("%s: network_state=%s\n", __func__,
+					bthf_client_network_state_t2str(state));
+}
+
+/* Callback for network roaming status change */
+static void hf_client_network_roaming_callback(bthf_client_service_type_t type)
+{
+	haltest_info("%s: service_type=%s\n", __func__,
+					bthf_client_service_type_t2str(type));
+}
+
+/* Callback for signal strength indication */
+static void hf_client_network_signal_callback(int signal_strength)
+{
+	haltest_info("%s: signal strength=%d\n", __func__, signal_strength);
+}
+
+/* Callback for battery level indication */
+static void hf_client_battery_level_callback(int battery_level)
+{
+	haltest_info("%s: battery_lvl=%d\n", __func__, battery_level);
+}
+
+/* Callback for current operator name */
+static void hf_client_current_operator_callback(const char *name)
+{
+	haltest_info("%s: operator_name=%s\n", __func__, name);
+}
+
+/* Callback for call indicator */
+static void hf_client_call_callback(bthf_client_call_t call)
+{
+	haltest_info("%s: call_state=%s\n", __func__,
+						bthf_client_call_t2str(call));
+}
+
+/* Callback for callsetup indicator */
+static void hf_client_callsetup_callback(bthf_client_callsetup_t callsetup)
+{
+	haltest_info("%s: callsetup=%s\n", __func__,
+					bthf_client_callsetup_t2str(callsetup));
+}
+
+/* Callback for callheld indicator */
+static void hf_client_callheld_callback(bthf_client_callheld_t callheld)
+{
+	haltest_info("%s: callheld=%s\n", __func__,
+					bthf_client_callheld_t2str(callheld));
+}
+
+/* Callback for response and hold */
+static void hf_client_resp_and_hold_callback(
+				bthf_client_resp_and_hold_t resp_and_hold)
+{
+	haltest_info("%s: resp_and_hold=%s\n", __func__,
+				bthf_client_resp_and_hold_t2str(resp_and_hold));
+}
+
+/* Callback for Calling Line Identification notification */
+static void hf_client_clip_callback(const char *number)
+{
+	haltest_info("%s: number=%s\n", __func__, number);
+}
+
+/* Callback for Call Waiting notification */
+static void hf_client_call_waiting_callback(const char *number)
+{
+	haltest_info("%s: number=%s\n", __func__, number);
+}
+
+/* Callback for listing current calls. Can be called multiple time. */
+static void hf_client_current_calls_callback(int index,
+					bthf_client_call_direction_t dir,
+					bthf_client_call_state_t state,
+					bthf_client_call_mpty_type_t mpty,
+					const char *number)
+{
+	haltest_info("%s: index=%d, direction=%s, state=%s, m_party=%s\n",
+					__func__, index,
+					bthf_client_call_direction_t2str(dir),
+					bthf_client_call_state_t2str(state),
+					bthf_client_call_mpty_type_t2str(mpty));
+
+	if (number)
+		haltest_info("%s: number=%s\n", __func__, number);
+}
+
+/* Callback for audio volume change */
+static void hf_client_volume_change_callback(bthf_client_volume_type_t type,
+								int volume)
+{
+	haltest_info("%s: vol_type=%s, value=%d\n", __func__,
+				bthf_client_volume_type_t2str(type), volume);
+}
+
+/* Callback for command complete event */
+static void hf_client_cmd_complete_callback(bthf_client_cmd_complete_t type,
+									int cme)
+{
+	haltest_info("%s: type=%s, cme=%d\n", __func__,
+				bthf_client_cmd_complete_t2str(type), cme);
+}
+
+/* Callback for subscriber information */
+static void hf_client_subscriber_info_callback(const char *name,
+				bthf_client_subscriber_service_type_t type)
+{
+	haltest_info("%s: name=%s, type=%s\n", __func__, name,
+			bthf_client_subscriber_service_type_t2str(type));
+}
+
+/* Callback for in-band ring tone settings */
+static void hf_client_in_band_ring_tone_callback(
+				bthf_client_in_band_ring_state_t state)
+{
+	haltest_info("%s: state=%s\n", __func__,
+				bthf_client_in_band_ring_state_t2str(state));
+}
+
+/* Callback for requested number from AG */
+static void hf_client_last_voice_tag_number_callback(const char *number)
+{
+	haltest_info("%s: number=%s\n", __func__, number);
+}
+
+/* Callback for sending ring indication to app */
+static void hf_client_ring_indication_callback(void)
+{
+	haltest_info("%s\n", __func__);
+}
+
+static bthf_client_callbacks_t hf_client_cbacks = {
+	.size = sizeof(hf_client_cbacks),
+	.connection_state_cb = hf_client_connection_state_callback,
+	.audio_state_cb = hf_client_audio_state_callback,
+	.vr_cmd_cb = hf_client_vr_cmd_callback,
+	.network_state_cb = hf_client_network_state_callback,
+	.network_roaming_cb = hf_client_network_roaming_callback,
+	.network_signal_cb = hf_client_network_signal_callback,
+	.battery_level_cb = hf_client_battery_level_callback,
+	.current_operator_cb = hf_client_current_operator_callback,
+	.call_cb = hf_client_call_callback,
+	.callsetup_cb = hf_client_callsetup_callback,
+	.callheld_cb = hf_client_callheld_callback,
+	.resp_and_hold_cb = hf_client_resp_and_hold_callback,
+	.clip_cb = hf_client_clip_callback,
+	.call_waiting_cb = hf_client_call_waiting_callback,
+	.current_calls_cb = hf_client_current_calls_callback,
+	.volume_change_cb = hf_client_volume_change_callback,
+	.cmd_complete_cb = hf_client_cmd_complete_callback,
+	.subscriber_info_cb = hf_client_subscriber_info_callback,
+	.in_band_ring_tone_cb = hf_client_in_band_ring_tone_callback,
+	.last_voice_tag_number_callback =
+				hf_client_last_voice_tag_number_callback,
+	.ring_indication_cb = hf_client_ring_indication_callback,
+};
+
+/* init */
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->init, &hf_client_cbacks);
+}
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+/* connect to audio gateway */
+static void connect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf_client);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf_client->connect, &addr);
+}
+
+/*
+ * This completion function will be used for several methods
+ * returning recently connected address
+ */
+static void connected_addr_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+/* Map completion to connected_addr_c */
+#define disconnect_c connected_addr_c
+
+/* disconnect from audio gateway */
+static void disconnect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf_client);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf_client->disconnect, &addr);
+}
+
+static void connect_audio_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+/* create an audio connection */
+static void connect_audio_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf_client);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf_client->connect_audio, &addr);
+}
+
+/* Map completion to connected_addr_c */
+#define disconnect_audio_c connected_addr_c
+
+/* close the audio connection */
+static void disconnect_audio_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf_client);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf_client->disconnect_audio, &addr);
+}
+
+/* start voice recognition */
+static void start_voice_recognition_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->start_voice_recognition);
+}
+
+/* stop voice recognition */
+static void stop_voice_recognition_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->stop_voice_recognition);
+}
+
+static void volume_control_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(bthf_client_volume_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+/* volume control */
+static void volume_control_p(int argc, const char **argv)
+{
+	bthf_client_volume_type_t type;
+	int volume;
+
+	RETURN_IF_NULL(if_hf_client);
+
+	/* volume type */
+	if (argc <= 2) {
+		haltest_error("No volume type specified\n");
+		return;
+	}
+	type = str2bthf_client_volume_type_t(argv[2]);
+
+	/* volume */
+	if (argc <= 3) {
+		haltest_error("No volume specified\n");
+		return;
+	}
+	volume = atoi(argv[3]);
+
+	EXEC(if_hf_client->volume_control, type, volume);
+}
+
+/* place a call with number a number */
+static void dial_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	/* number string */
+	if (argc <= 2) {
+		haltest_info("Number not specified. Redial\n");
+		EXEC(if_hf_client->dial, NULL);
+		return;
+	}
+
+	EXEC(if_hf_client->dial, argv[2]);
+}
+
+/* place a call with number specified by location (speed dial) */
+static void dial_memory_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	/* memory index */
+	if (argc <= 2) {
+		haltest_error("No memory index specified\n");
+		return;
+	}
+
+	EXEC(if_hf_client->dial_memory, atoi(argv[2]));
+}
+
+static void handle_call_action_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(bthf_client_call_action_t);
+		*enum_func = enum_defines;
+	}
+}
+
+/* perform specified call related action */
+static void handle_call_action_p(int argc, const char **argv)
+{
+	bthf_client_call_action_t action;
+	int index = 0;
+
+	RETURN_IF_NULL(if_hf_client);
+
+	/* action */
+	if (argc <= 2) {
+		haltest_error("No action specified\n");
+		return;
+	}
+	action = str2bthf_client_call_action_t(argv[2]);
+
+	/* call index */
+	if (action == BTHF_CLIENT_CALL_ACTION_CHLD_1x ||
+				action == BTHF_CLIENT_CALL_ACTION_CHLD_2x) {
+		if (argc <= 3) {
+			haltest_error("No call index specified\n");
+			return;
+		}
+		index = atoi(argv[3]);
+	}
+
+	EXEC(if_hf_client->handle_call_action, action, index);
+}
+
+/* query list of current calls */
+static void query_current_calls_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->query_current_calls);
+}
+
+/* query name of current selected operator */
+static void query_current_operator_name_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->query_current_operator_name);
+}
+
+/* Retrieve subscriber information */
+static void retrieve_subscriber_info_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->retrieve_subscriber_info);
+}
+
+/* Send DTMF code*/
+static void send_dtmf_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->send_dtmf, *argv[2]);
+}
+
+/* Request a phone number from AG corresponding to last voice tag recorded */
+static void request_last_voice_tag_number_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->request_last_voice_tag_number);
+}
+
+/* Closes the interface. */
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXECV(if_hf_client->cleanup);
+	if_hf_client = NULL;
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(connect, "<addr>"),
+	STD_METHODCH(disconnect, "<addr>"),
+	STD_METHODCH(connect_audio, "<addr>"),
+	STD_METHODCH(disconnect_audio, "<addr>"),
+	STD_METHOD(start_voice_recognition),
+	STD_METHOD(stop_voice_recognition),
+	STD_METHODCH(volume_control, "<volume_type> <value>"),
+	STD_METHODH(dial, "<destination_number>"),
+	STD_METHODH(dial_memory, "<memory_location>"),
+	STD_METHODCH(handle_call_action, "<call_action> <call_index>"),
+	STD_METHOD(query_current_calls),
+	STD_METHOD(query_current_operator_name),
+	STD_METHOD(retrieve_subscriber_info),
+	STD_METHODH(send_dtmf, "<code>"),
+	STD_METHOD(request_last_voice_tag_number),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface hf_client_if = {
+	.name = "handsfree_client",
+	.methods = methods
+};
diff --git a/repo/android/client/if-hf.c b/repo/android/client/if-hf.c
new file mode 100644
index 0000000..6b798f8
--- /dev/null
+++ b/repo/android/client/if-hf.c
@@ -0,0 +1,1062 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const bthf_interface_t *if_hf = NULL;
+
+SINTMAP(bthf_at_response_t, -1, "(unknown)")
+	DELEMENT(BTHF_AT_RESPONSE_ERROR),
+	DELEMENT(BTHF_AT_RESPONSE_OK),
+ENDMAP
+
+SINTMAP(bthf_connection_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CONNECTION_STATE_DISCONNECTED),
+	DELEMENT(BTHF_CONNECTION_STATE_CONNECTING),
+	DELEMENT(BTHF_CONNECTION_STATE_CONNECTED),
+	DELEMENT(BTHF_CONNECTION_STATE_SLC_CONNECTED),
+	DELEMENT(BTHF_CONNECTION_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(bthf_audio_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_AUDIO_STATE_DISCONNECTED),
+	DELEMENT(BTHF_AUDIO_STATE_CONNECTING),
+	DELEMENT(BTHF_AUDIO_STATE_CONNECTED),
+	DELEMENT(BTHF_AUDIO_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(bthf_vr_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_VR_STATE_STOPPED),
+	DELEMENT(BTHF_VR_STATE_STARTED),
+ENDMAP
+
+SINTMAP(bthf_volume_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_VOLUME_TYPE_SPK),
+	DELEMENT(BTHF_VOLUME_TYPE_MIC),
+ENDMAP
+
+SINTMAP(bthf_nrec_t, -1, "(unknown)")
+	DELEMENT(BTHF_NREC_STOP),
+	DELEMENT(BTHF_NREC_START),
+ENDMAP
+
+SINTMAP(bthf_chld_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_CHLD_TYPE_RELEASEHELD),
+	DELEMENT(BTHF_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD),
+	DELEMENT(BTHF_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD),
+	DELEMENT(BTHF_CHLD_TYPE_ADDHELDTOCONF),
+ENDMAP
+
+/* Network Status */
+SINTMAP(bthf_network_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_NETWORK_STATE_NOT_AVAILABLE),
+	DELEMENT(BTHF_NETWORK_STATE_AVAILABLE),
+ENDMAP
+
+/* Service type */
+SINTMAP(bthf_service_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_SERVICE_TYPE_HOME),
+	DELEMENT(BTHF_SERVICE_TYPE_ROAMING),
+ENDMAP
+
+SINTMAP(bthf_call_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CALL_STATE_ACTIVE),
+	DELEMENT(BTHF_CALL_STATE_HELD),
+	DELEMENT(BTHF_CALL_STATE_DIALING),
+	DELEMENT(BTHF_CALL_STATE_ALERTING),
+	DELEMENT(BTHF_CALL_STATE_INCOMING),
+	DELEMENT(BTHF_CALL_STATE_WAITING),
+	DELEMENT(BTHF_CALL_STATE_IDLE),
+ENDMAP
+
+SINTMAP(bthf_call_direction_t, -1, "(unknown)")
+	DELEMENT(BTHF_CALL_DIRECTION_OUTGOING),
+	DELEMENT(BTHF_CALL_DIRECTION_INCOMING),
+ENDMAP
+
+SINTMAP(bthf_call_mode_t, -1, "(unknown)")
+	DELEMENT(BTHF_CALL_TYPE_VOICE),
+	DELEMENT(BTHF_CALL_TYPE_DATA),
+	DELEMENT(BTHF_CALL_TYPE_FAX),
+ENDMAP
+
+SINTMAP(bthf_call_mpty_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_CALL_MPTY_TYPE_SINGLE),
+	DELEMENT(BTHF_CALL_MPTY_TYPE_MULTI),
+ENDMAP
+
+SINTMAP(bthf_call_addrtype_t, -1, "(unknown)")
+	DELEMENT(BTHF_CALL_ADDRTYPE_UNKNOWN),
+	DELEMENT(BTHF_CALL_ADDRTYPE_INTERNATIONAL),
+ENDMAP
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+SINTMAP(bthf_wbs_config_t, -1, "(unknown)")
+	DELEMENT(BTHF_WBS_NONE),
+	DELEMENT(BTHF_WBS_NO),
+	DELEMENT(BTHF_WBS_YES),
+ENDMAP
+#endif
+
+/* Callbacks */
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+/*
+ * Callback for connection state change.
+ * state will have one of the values from BtHfConnectionState
+ */
+static void connection_state_cb(bthf_connection_state_t state,
+							bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+					bthf_connection_state_t2str(state),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+/*
+ * Callback for audio connection state change.
+ * state will have one of the values from BtHfAudioState
+ */
+static void audio_state_cb(bthf_audio_state_t state, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+					bthf_audio_state_t2str(state),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+/*
+ * Callback for VR connection state change.
+ * state will have one of the values from BtHfVRState
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void vr_cmd_cb(bthf_vr_state_t state, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+					bthf_vr_state_t2str(state),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void vr_cmd_cb(bthf_vr_state_t state)
+{
+	haltest_info("%s: state=%s\n", __func__, bthf_vr_state_t2str(state));
+}
+#endif
+
+/* Callback for answer incoming call (ATA) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void answer_call_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void answer_call_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+#endif
+
+/* Callback for disconnect call (AT+CHUP) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void hangup_call_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void hangup_call_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+#endif
+
+/*
+ * Callback for disconnect call (AT+CHUP)
+ * type will denote Speaker/Mic gain (BtHfVolumeControl).
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void volume_cmd_cb(bthf_volume_type_t type, int volume,
+							bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: type=%s volume=%d bd_addr=%s\n", __func__,
+					bthf_volume_type_t2str(type), volume,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void volume_cmd_cb(bthf_volume_type_t type, int volume)
+{
+	haltest_info("%s: type=%s volume=%d\n", __func__,
+					bthf_volume_type_t2str(type), volume);
+}
+#endif
+
+/*
+ * Callback for dialing an outgoing call
+ * If number is NULL, redial
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void dial_call_cmd_cb(char *number, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: number=%s bd_addr=%s\n", __func__, number,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void dial_call_cmd_cb(char *number)
+{
+	haltest_info("%s: number=%s\n", __func__, number);
+}
+#endif
+
+/*
+ * Callback for sending DTMF tones
+ * tone contains the dtmf character to be sent
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void dtmf_cmd_cb(char tone, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: tone=%d bd_addr=%s\n", __func__, tone,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void dtmf_cmd_cb(char tone)
+{
+	haltest_info("%s: tone=%d\n", __func__, tone);
+}
+#endif
+
+/*
+ * Callback for enabling/disabling noise reduction/echo cancellation
+ * value will be 1 to enable, 0 to disable
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void nrec_cmd_cb(bthf_nrec_t nrec, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: nrec=%s bd_addr=%s\n", __func__,
+					bthf_nrec_t2str(nrec),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void nrec_cmd_cb(bthf_nrec_t nrec)
+{
+	haltest_info("%s: nrec=%s\n", __func__, bthf_nrec_t2str(nrec));
+}
+#endif
+
+/*
+ * Callback for call hold handling (AT+CHLD)
+ * value will contain the call hold command (0, 1, 2, 3)
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void chld_cmd_cb(bthf_chld_type_t chld, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: chld=%s bd_addr=%s\n", __func__,
+					bthf_chld_type_t2str(chld),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void chld_cmd_cb(bthf_chld_type_t chld)
+{
+	haltest_info("%s: chld=%s\n", __func__, bthf_chld_type_t2str(chld));
+}
+#endif
+
+/* Callback for CNUM (subscriber number) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void cnum_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void cnum_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+#endif
+
+/* Callback for indicators (CIND) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void cind_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void cind_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+#endif
+
+/* Callback for operator selection (COPS) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void cops_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void cops_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+#endif
+
+/* Callback for call list (AT+CLCC) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void clcc_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void clcc_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+#endif
+
+/*
+ * Callback for unknown AT command recd from HF
+ * at_string will contain the unparsed AT string
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void unknown_at_cmd_cb(char *at_string, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: at_string=%s bd_addr=%s\n", __func__, at_string,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void unknown_at_cmd_cb(char *at_string)
+{
+	haltest_info("%s: at_string=%s\n", __func__, at_string);
+}
+#endif
+
+/* Callback for keypressed (HSP) event. */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void key_pressed_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void key_pressed_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+#endif
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void wbs_cb(bthf_wbs_config_t wbs, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#endif
+
+static bthf_callbacks_t hf_cbacks = {
+	.size = sizeof(hf_cbacks),
+	.connection_state_cb = connection_state_cb,
+	.audio_state_cb = audio_state_cb,
+	.vr_cmd_cb = vr_cmd_cb,
+	.answer_call_cmd_cb = answer_call_cmd_cb,
+	.hangup_call_cmd_cb = hangup_call_cmd_cb,
+	.volume_cmd_cb = volume_cmd_cb,
+	.dial_call_cmd_cb = dial_call_cmd_cb,
+	.dtmf_cmd_cb = dtmf_cmd_cb,
+	.nrec_cmd_cb = nrec_cmd_cb,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.wbs_cb = wbs_cb,
+#endif
+	.chld_cmd_cb = chld_cmd_cb,
+	.cnum_cmd_cb = cnum_cmd_cb,
+	.cind_cmd_cb = cind_cmd_cb,
+	.cops_cmd_cb = cops_cmd_cb,
+	.clcc_cmd_cb = clcc_cmd_cb,
+	.unknown_at_cmd_cb = unknown_at_cmd_cb,
+	.key_pressed_cmd_cb = key_pressed_cmd_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	int max_hf_clients;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	if (argc <= 2)
+		max_hf_clients = 1;
+	else
+		max_hf_clients = atoi(argv[2]);
+
+	EXEC(if_hf->init, &hf_cbacks, max_hf_clients);
+#else
+	EXEC(if_hf->init, &hf_cbacks);
+#endif
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+static void connect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf->connect, &addr);
+}
+
+/* disconnect */
+
+/*
+ * This completion function will be used for several methods
+ * returning recently connected address
+ */
+static void connected_addr_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+/* Map completion to connected_addr_c */
+#define disconnect_c connected_addr_c
+
+static void disconnect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf->disconnect, &addr);
+}
+
+/* create an audio connection */
+
+/* Map completion to connected_addr_c */
+#define connect_audio_c connected_addr_c
+
+static void connect_audio_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf->connect_audio, &addr);
+}
+
+/* close the audio connection */
+
+/* Map completion to connected_addr_c */
+#define disconnect_audio_c connected_addr_c
+
+static void disconnect_audio_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf->disconnect_audio, &addr);
+}
+
+/* start voice recognition */
+
+static void start_voice_recognition_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf->start_voice_recognition, &addr);
+#else
+	EXEC(if_hf->start_voice_recognition);
+#endif
+}
+
+/* stop voice recognition */
+
+static void stop_voice_recognition_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf->stop_voice_recognition, &addr);
+#else
+	EXEC(if_hf->stop_voice_recognition);
+#endif
+}
+
+/* volume control */
+
+static void volume_control_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(bthf_volume_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void volume_control_p(int argc, const char **argv)
+{
+	bthf_volume_type_t type;
+	int volume;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+	/* volume type */
+	if (argc <= 2) {
+		haltest_error("No volume type specified\n");
+		return;
+	}
+	type = str2bthf_volume_type_t(argv[2]);
+
+	/* volume */
+	if (argc <= 3) {
+		haltest_error("No volume specified\n");
+		return;
+	}
+	volume = atoi(argv[3]);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(4, &addr);
+
+	EXEC(if_hf->volume_control, type, volume, &addr);
+#else
+	EXEC(if_hf->volume_control, type, volume);
+#endif
+}
+
+/* Combined device status change notification */
+
+static void device_status_notification_c(int argc, const char **argv,
+							enum_func *enum_func,
+							void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(bthf_network_state_t);
+		*enum_func = enum_defines;
+	} else if (argc == 4) {
+		*user = TYPE_ENUM(bthf_service_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void device_status_notification_p(int argc, const char **argv)
+{
+	bthf_network_state_t ntk_state;
+	bthf_service_type_t svc_type;
+	int signal;
+	int batt_chg;
+
+	RETURN_IF_NULL(if_hf);
+
+	/* network state */
+	if (argc <= 2) {
+		haltest_error("No network state specified\n");
+		return;
+	}
+	ntk_state = str2bthf_network_state_t(argv[2]);
+
+	/* service type */
+	if (argc <= 3) {
+		haltest_error("No service type specified\n");
+		return;
+	}
+	svc_type = str2bthf_service_type_t(argv[3]);
+
+	/* signal */
+	if (argc <= 4) {
+		haltest_error("No signal specified\n");
+		return;
+	}
+	signal = atoi(argv[4]);
+
+	/* batt_chg */
+	if (argc <= 5) {
+		haltest_error("No batt_chg specified\n");
+		return;
+	}
+	batt_chg = atoi(argv[5]);
+
+	EXEC(if_hf->device_status_notification, ntk_state, svc_type, signal,
+								batt_chg);
+}
+
+/* Response for COPS command */
+
+static void cops_response_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+	/* response */
+	if (argc <= 2) {
+		haltest_error("No cops specified\n");
+		return;
+	}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(3, &addr);
+
+	EXEC(if_hf->cops_response, argv[2], &addr);
+#else
+	EXEC(if_hf->cops_response, argv[2]);
+#endif
+}
+
+/* Response for CIND command */
+
+static void cind_response_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 6) {
+		*user = TYPE_ENUM(bthf_call_state_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void cind_response_p(int argc, const char **argv)
+{
+	int svc;
+	int num_active;
+	int num_held;
+	bthf_call_state_t call_setup_state;
+	int signal;
+	int roam;
+	int batt_chg;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+	/* svc */
+	if (argc <= 2) {
+		haltest_error("No service specified\n");
+		return;
+	}
+	svc = atoi(argv[2]);
+
+	/* num active */
+	if (argc <= 3) {
+		haltest_error("No num active specified\n");
+		return;
+	}
+	num_active = atoi(argv[3]);
+
+	/* num held */
+	if (argc <= 4) {
+		haltest_error("No num held specified\n");
+		return;
+	}
+	num_held = atoi(argv[4]);
+
+	/* call setup state */
+	if (argc <= 5) {
+		haltest_error("No call setup state specified\n");
+		return;
+	}
+	call_setup_state = str2bthf_call_state_t(argv[5]);
+
+	/* signal */
+	if (argc <= 6) {
+		haltest_error("No signal specified\n");
+		return;
+	}
+	signal = atoi(argv[6]);
+
+	/* roam */
+	if (argc <= 7) {
+		haltest_error("No roam specified\n");
+		return;
+	}
+	roam = atoi(argv[7]);
+
+	/* batt_chg */
+	if (argc <= 8) {
+		haltest_error("No batt_chg specified\n");
+		return;
+	}
+	batt_chg = atoi(argv[8]);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(9, &addr);
+
+	EXEC(if_hf->cind_response, svc, num_active, num_held, call_setup_state,
+						signal, roam, batt_chg, &addr);
+#else
+	EXEC(if_hf->cind_response, svc, num_active, num_held, call_setup_state,
+							signal, roam, batt_chg);
+#endif
+}
+
+/* Pre-formatted AT response, typically in response to unknown AT cmd */
+
+static void formatted_at_response_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+	/* response */
+	if (argc <= 2) {
+		haltest_error("No response specified\n");
+		return;
+	}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(3, &addr);
+
+	EXEC(if_hf->formatted_at_response, argv[2], &addr);
+#else
+	EXEC(if_hf->formatted_at_response, argv[2]);
+#endif
+}
+
+/* at_response */
+
+static void at_response_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(bthf_at_response_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void at_response_p(int argc, const char **argv)
+{
+	bthf_at_response_t response_code;
+	int error_code;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+	/* response type */
+	if (argc <= 2) {
+		haltest_error("No response specified\n");
+		return;
+	}
+	response_code = str2bthf_at_response_t(argv[2]);
+
+	/* error code */
+	if (argc <= 3)
+		error_code = 0;
+	else
+		error_code = atoi(argv[3]);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(4, &addr);
+
+	EXEC(if_hf->at_response, response_code, error_code, &addr);
+#else
+	EXEC(if_hf->at_response, response_code, error_code);
+#endif
+}
+
+/* response for CLCC command */
+
+static void clcc_response_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 4) {
+		*user = TYPE_ENUM(bthf_call_direction_t);
+		*enum_func = enum_defines;
+	} else if (argc == 5) {
+		*user = TYPE_ENUM(bthf_call_state_t);
+		*enum_func = enum_defines;
+	} else if (argc == 6) {
+		*user = TYPE_ENUM(bthf_call_mode_t);
+		*enum_func = enum_defines;
+	} else if (argc == 7) {
+		*user = TYPE_ENUM(bthf_call_mpty_type_t);
+		*enum_func = enum_defines;
+	} else if (argc == 9) {
+		*user = TYPE_ENUM(bthf_call_addrtype_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void clcc_response_p(int argc, const char **argv)
+{
+	int index;
+	bthf_call_direction_t dir;
+	bthf_call_state_t state;
+	bthf_call_mode_t mode;
+	bthf_call_mpty_type_t mpty;
+	const char *number;
+	bthf_call_addrtype_t type;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+	/* index */
+	if (argc <= 2) {
+		haltest_error("No index specified\n");
+		return;
+	}
+	index = atoi(argv[2]);
+
+	/* direction */
+	if (argc <= 3) {
+		haltest_error("No direction specified\n");
+		return;
+	}
+	dir = str2bthf_call_direction_t(argv[3]);
+
+	/* call state */
+	if (argc <= 4) {
+		haltest_error("No call state specified\n");
+		return;
+	}
+	state = str2bthf_call_state_t(argv[4]);
+
+	/* call mode */
+	if (argc <= 5) {
+		haltest_error("No mode specified\n");
+		return;
+	}
+	mode = str2bthf_call_mode_t(argv[5]);
+
+	/* call mpty type */
+	if (argc <= 6) {
+		haltest_error("No mpty type specified\n");
+		return;
+	}
+	mpty = str2bthf_call_mpty_type_t(argv[6]);
+
+	/* number */
+	if (argc <= 7) {
+		haltest_error("No number specified\n");
+		return;
+	}
+	number = argv[7];
+
+	/* call mpty type */
+	if (argc <= 8) {
+		haltest_error("No address type specified\n");
+		return;
+	}
+	type = str2bthf_call_addrtype_t(argv[8]);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(9, &addr);
+
+	EXEC(if_hf->clcc_response, index, dir, state, mode, mpty, number,
+								type, &addr);
+#else
+	EXEC(if_hf->clcc_response, index, dir, state, mode, mpty, number,
+									type);
+#endif
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void configure_wbs_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 4) {
+		*user = TYPE_ENUM(bthf_wbs_config_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void configure_wbs_p(int argc, const char **argv)
+{
+	bthf_wbs_config_t wbs;
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf);
+
+	if (argc <= 3) {
+		haltest_error("Too few parameters specified\n");
+		return;
+	}
+
+	VERIFY_ADDR_ARG(2, &addr);
+	wbs = str2bthf_wbs_config_t(argv[3]);
+
+	EXEC(if_hf->configure_wbs, &addr, wbs);
+}
+#endif
+
+/* phone state change */
+
+static void phone_state_change_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 5) {
+		*user = TYPE_ENUM(bthf_call_state_t);
+		*enum_func = enum_defines;
+	} else if (argc == 7) {
+		*user = TYPE_ENUM(bthf_call_addrtype_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void phone_state_change_p(int argc, const char **argv)
+{
+	int num_active;
+	int num_held;
+	bthf_call_state_t call_setup_state;
+	const char *number;
+	bthf_call_addrtype_t type;
+
+	RETURN_IF_NULL(if_hf);
+
+	/* num_active */
+	if (argc <= 2) {
+		haltest_error("No num_active specified\n");
+		return;
+	}
+	num_active = atoi(argv[2]);
+
+	/* num_held */
+	if (argc <= 3) {
+		haltest_error("No num_held specified\n");
+		return;
+	}
+	num_held = atoi(argv[3]);
+
+	/* setup state */
+	if (argc <= 4) {
+		haltest_error("No call setup state specified\n");
+		return;
+	}
+	call_setup_state = str2bthf_call_state_t(argv[4]);
+
+	/* number */
+	if (argc <= 5) {
+		haltest_error("No number specified\n");
+		return;
+	}
+	number = argv[5];
+
+	/* call mpty type */
+	if (argc <= 6) {
+		haltest_error("No address type specified\n");
+		return;
+	}
+	type = str2bthf_call_addrtype_t(argv[6]);
+
+	EXEC(if_hf->phone_state_change, num_active, num_held, call_setup_state,
+								number, type);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf);
+
+	EXECV(if_hf->cleanup);
+	if_hf = NULL;
+}
+
+static struct method methods[] = {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	STD_METHODH(init, "[<max_hf_clients>]"),
+	STD_METHODH(start_voice_recognition, "<addr>"),
+	STD_METHODH(stop_voice_recognition, "<addr>"),
+	STD_METHODCH(volume_control, "<vol_type> <volume> <addr>"),
+	STD_METHODH(cops_response, "<cops string> <addr>"),
+	STD_METHODCH(cind_response,
+			"<svc> <num_active> <num_held> <setup_state> <signal> "
+			"<roam> <batt_chg> <addr>"),
+	STD_METHODH(formatted_at_response, "<at_response> <addr>"),
+	STD_METHODCH(at_response, "<response_code> [<error_code> <bdaddr>]"),
+	STD_METHODCH(clcc_response,
+			"<index> <direction> <state> <mode> <mpty> <number> "
+			"<type> <addr>"),
+	STD_METHODCH(configure_wbs, "<addr> <wbs config>"),
+#else
+	STD_METHOD(init),
+	STD_METHOD(start_voice_recognition),
+	STD_METHOD(stop_voice_recognition),
+	STD_METHODCH(volume_control, "<vol_type> <volume>"),
+	STD_METHODH(cops_response, "<cops string>"),
+	STD_METHODCH(cind_response,
+			"<svc> <num_active> <num_held> <setup_state> <signal> "
+			"<roam> <batt_chg>"),
+	STD_METHODH(formatted_at_response, "<at_response>"),
+	STD_METHODCH(at_response, "<response_code> [<error_code>]"),
+	STD_METHODCH(clcc_response,
+			"<index> <direction> <state> <mode> <mpty> <number> "
+			"<type>"),
+#endif
+	STD_METHODCH(connect, "<addr>"),
+	STD_METHODCH(disconnect, "<addr>"),
+	STD_METHODCH(connect_audio, "<addr>"),
+	STD_METHODCH(disconnect_audio, "<addr>"),
+	STD_METHODCH(device_status_notification,
+			"<ntk_state> <svt_type> <signal> <batt_chg>"),
+	STD_METHODCH(phone_state_change,
+			"<num_active> <num_held> <setup_state> <number> "
+			"<type>"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface hf_if = {
+	.name = "handsfree",
+	.methods = methods
+};
diff --git a/repo/android/client/if-hh.c b/repo/android/client/if-hh.c
new file mode 100644
index 0000000..25519e5
--- /dev/null
+++ b/repo/android/client/if-hh.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_hh.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+const bthh_interface_t *if_hh = NULL;
+
+SINTMAP(bthh_protocol_mode_t, -1, "(unknown)")
+	DELEMENT(BTHH_REPORT_MODE),
+	DELEMENT(BTHH_BOOT_MODE),
+	DELEMENT(BTHH_UNSUPPORTED_MODE),
+ENDMAP
+
+SINTMAP(bthh_report_type_t, -1, "(unknown)")
+	DELEMENT(BTHH_INPUT_REPORT),
+	DELEMENT(BTHH_OUTPUT_REPORT),
+	DELEMENT(BTHH_FEATURE_REPORT),
+ENDMAP
+
+SINTMAP(bthh_connection_state_t, -1, "(unknown)")
+	DELEMENT(BTHH_CONN_STATE_CONNECTED),
+	DELEMENT(BTHH_CONN_STATE_CONNECTING),
+	DELEMENT(BTHH_CONN_STATE_DISCONNECTED),
+	DELEMENT(BTHH_CONN_STATE_DISCONNECTING),
+	DELEMENT(BTHH_CONN_STATE_FAILED_MOUSE_FROM_HOST),
+	DELEMENT(BTHH_CONN_STATE_FAILED_KBD_FROM_HOST),
+	DELEMENT(BTHH_CONN_STATE_FAILED_TOO_MANY_DEVICES),
+	DELEMENT(BTHH_CONN_STATE_FAILED_NO_BTHID_DRIVER),
+	DELEMENT(BTHH_CONN_STATE_FAILED_GENERIC),
+	DELEMENT(BTHH_CONN_STATE_UNKNOWN),
+ENDMAP
+
+SINTMAP(bthh_status_t, -1, "(unknown)")
+	DELEMENT(BTHH_OK),
+	DELEMENT(BTHH_HS_HID_NOT_READY),
+	DELEMENT(BTHH_HS_INVALID_RPT_ID),
+	DELEMENT(BTHH_HS_TRANS_NOT_SPT),
+	DELEMENT(BTHH_HS_INVALID_PARAM),
+	DELEMENT(BTHH_HS_ERROR),
+	DELEMENT(BTHH_ERR),
+	DELEMENT(BTHH_ERR_SDP),
+	DELEMENT(BTHH_ERR_PROTO),
+	DELEMENT(BTHH_ERR_DB_FULL),
+	DELEMENT(BTHH_ERR_TOD_UNSPT),
+	DELEMENT(BTHH_ERR_NO_RES),
+	DELEMENT(BTHH_ERR_AUTH_FAILED),
+	DELEMENT(BTHH_ERR_HDL),
+ENDMAP
+
+static char connected_device_addr[MAX_ADDR_STR_LEN];
+/*
+ * Callback for connection state change.
+ * state will have one of the values from bthh_connection_state_t
+ */
+static void connection_state_cb(bt_bdaddr_t *bd_addr,
+						bthh_connection_state_t state)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: bd_addr=%s connection_state=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, addr),
+					bthh_connection_state_t2str(state));
+	if (state == BTHH_CONN_STATE_CONNECTED)
+		strcpy(connected_device_addr, addr);
+}
+
+/*
+ * Callback for virtual unplug api.
+ * the status of the virtual unplug
+ */
+static void virtual_unplug_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: bd_addr=%s hh_status=%s\n", __func__,
+						bt_bdaddr_t2str(bd_addr, addr),
+						bthh_status_t2str(hh_status));
+}
+
+/* Callback for Android 5.0 handshake api. */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void handshake_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: bd_addr=%s hh_status=%s\n", __func__,
+						bt_bdaddr_t2str(bd_addr, addr),
+						bthh_status_t2str(hh_status));
+}
+#endif
+
+/*
+ * Callback for get hid info
+ * hid_info will contain attr_mask, sub_class, app_id, vendor_id, product_id,
+ * version, ctry_code, len
+ */
+static void hid_info_cb(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	/* TODO: bluedroid does not seem to ever call this callback */
+	haltest_info("%s: bd_addr=%s\n", __func__,
+						bt_bdaddr_t2str(bd_addr, addr));
+}
+
+/*
+ * Callback for get/set protocol api.
+ * the protocol mode is one of the value from bthh_protocol_mode_t
+ */
+static void protocol_mode_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status,
+						bthh_protocol_mode_t mode)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: bd_addr=%s hh_status=%s mode=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, addr),
+					bthh_status_t2str(hh_status),
+					bthh_protocol_mode_t2str(mode));
+}
+
+/* Callback for get/set_idle_time api. */
+static void idle_time_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status,
+								int idle_rate)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: bd_addr=%s hh_status=%s idle_rate=%d\n", __func__,
+				bt_bdaddr_t2str(bd_addr, addr),
+				bthh_status_t2str(hh_status), idle_rate);
+}
+
+
+/*
+ * Callback for get report api.
+ * if status is ok rpt_data contains the report data
+ */
+static void get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status,
+						uint8_t *rpt_data, int rpt_size)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	/* TODO: print actual report */
+	haltest_info("%s: bd_addr=%s hh_status=%s rpt_size=%d\n", __func__,
+					bt_bdaddr_t2str(bd_addr, addr),
+					bthh_status_t2str(hh_status), rpt_size);
+}
+
+static bthh_callbacks_t bthh_callbacks = {
+	.size = sizeof(bthh_callbacks),
+	.connection_state_cb = connection_state_cb,
+	.hid_info_cb = hid_info_cb,
+	.protocol_mode_cb = protocol_mode_cb,
+	.idle_time_cb = idle_time_cb,
+	.get_report_cb = get_report_cb,
+	.virtual_unplug_cb = virtual_unplug_cb,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.handshake_cb = handshake_cb
+#endif
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hh);
+
+	EXEC(if_hh->init, &bthh_callbacks);
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = (void *) connected_device_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void connect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hh->connect, &addr);
+}
+
+/* disconnect */
+
+/* Same completion as connect_c */
+#define disconnect_c connect_c
+
+static void disconnect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hh->disconnect, &addr);
+}
+
+/* virtual_unplug */
+
+/* Same completion as connect_c */
+#define virtual_unplug_c connect_c
+
+static void virtual_unplug_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hh->virtual_unplug, &addr);
+}
+
+/* set_info */
+
+/* Same completion as connect_c */
+#define set_info_c connect_c
+
+static void set_info_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bthh_hid_info_t hid_info;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	memset(&hid_info, 0, sizeof(hid_info));
+
+	/*
+	 * This command is intentionally not supported. See comment from
+	 * bt_hid_info() in android/hidhost.c
+	 */
+	EXEC(if_hh->set_info, &addr, hid_info);
+}
+
+/* get_protocol */
+
+static void get_protocol_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = connected_device_addr;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = TYPE_ENUM(bthh_protocol_mode_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void get_protocol_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bthh_protocol_mode_t protocolMode;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc < 4) {
+		haltest_error("No protocol mode specified\n");
+		return;
+	}
+	protocolMode = str2bthh_protocol_mode_t(argv[3]);
+
+	EXEC(if_hh->get_protocol, &addr, protocolMode);
+}
+
+/* set_protocol */
+
+/* Same completion as get_protocol_c */
+#define set_protocol_c get_protocol_c
+
+static void set_protocol_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bthh_protocol_mode_t protocolMode;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc < 4) {
+		haltest_error("No protocol mode specified\n");
+		return;
+	}
+	protocolMode = str2bthh_protocol_mode_t(argv[3]);
+
+	EXEC(if_hh->set_protocol, &addr, protocolMode);
+}
+
+/* get_report */
+
+static void get_report_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = connected_device_addr;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = TYPE_ENUM(bthh_report_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void get_report_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bthh_report_type_t reportType;
+	uint8_t reportId;
+	int bufferSize;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc < 4) {
+		haltest_error("No report type specified\n");
+		return;
+	}
+	reportType = str2bthh_report_type_t(argv[3]);
+
+	if (argc < 5) {
+		haltest_error("No reportId specified\n");
+		return;
+	}
+	reportId = (uint8_t) atoi(argv[4]);
+
+	if (argc < 6) {
+		haltest_error("No bufferSize specified\n");
+		return;
+	}
+	bufferSize = atoi(argv[5]);
+
+	EXEC(if_hh->get_report, &addr, reportType, reportId, bufferSize);
+}
+
+/* set_report */
+
+static void set_report_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = connected_device_addr;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = TYPE_ENUM(bthh_report_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void set_report_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bthh_report_type_t reportType;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc <= 3) {
+		haltest_error("No report type specified\n");
+		return;
+	}
+	reportType = str2bthh_report_type_t(argv[3]);
+
+	if (argc <= 4) {
+		haltest_error("No report specified\n");
+		return;
+	}
+
+	EXEC(if_hh->set_report, &addr, reportType, (char *) argv[4]);
+}
+
+/* send_data */
+
+static void send_data_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = connected_device_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void send_data_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc <= 3) {
+		haltest_error("No data to send specified\n");
+		return;
+	}
+
+	EXEC(if_hh->send_data, &addr, (char *) argv[3]);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hh);
+
+	EXECV(if_hh->cleanup);
+}
+
+/* Methods available in bthh_interface_t */
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(connect, "<addr>"),
+	STD_METHODCH(disconnect, "<addr>"),
+	STD_METHODCH(virtual_unplug, "<addr>"),
+	STD_METHODCH(set_info, "<addr>"),
+	STD_METHODCH(get_protocol, "<addr> <mode>"),
+	STD_METHODCH(set_protocol, "<addr> <mode>"),
+	STD_METHODCH(get_report, "<addr> <type> <report_id> <size>"),
+	STD_METHODCH(set_report, "<addr> <type> <hex_encoded_report>"),
+	STD_METHODCH(send_data, "<addr> <hex_encoded_data>"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface hh_if = {
+	.name = "hidhost",
+	.methods = methods
+};
diff --git a/repo/android/client/if-hl.c b/repo/android/client/if-hl.c
new file mode 100644
index 0000000..bd05671
--- /dev/null
+++ b/repo/android/client/if-hl.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_hl.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+SINTMAP(bthl_mdep_role_t, -1, "(unknown)")
+	DELEMENT(BTHL_MDEP_ROLE_SOURCE),
+	DELEMENT(BTHL_MDEP_ROLE_SINK),
+ENDMAP
+
+SINTMAP(bthl_channel_type_t, -1, "(unknown)")
+	DELEMENT(BTHL_CHANNEL_TYPE_RELIABLE),
+	DELEMENT(BTHL_CHANNEL_TYPE_STREAMING),
+	DELEMENT(BTHL_CHANNEL_TYPE_ANY),
+ENDMAP
+
+SINTMAP(bthl_app_reg_state_t, -1, "(unknown)")
+	DELEMENT(BTHL_APP_REG_STATE_REG_SUCCESS),
+	DELEMENT(BTHL_APP_REG_STATE_REG_FAILED),
+	DELEMENT(BTHL_APP_REG_STATE_DEREG_SUCCESS),
+	DELEMENT(BTHL_APP_REG_STATE_DEREG_FAILED),
+ENDMAP
+
+SINTMAP(bthl_channel_state_t, -1, "(unknown)")
+	DELEMENT(BTHL_CONN_STATE_CONNECTING),
+	DELEMENT(BTHL_CONN_STATE_CONNECTED),
+	DELEMENT(BTHL_CONN_STATE_DISCONNECTING),
+	DELEMENT(BTHL_CONN_STATE_DISCONNECTED),
+	DELEMENT(BTHL_CONN_STATE_DESTROYED),
+ENDMAP
+
+#define APP_ID_SIZE 20
+#define MDEP_CFG_SIZE 10
+#define CHANNEL_ID_SIZE 50
+
+struct channel_info {
+	int fd;
+};
+
+struct mdep_cfg {
+	uint8_t role;
+	struct channel_info channel[CHANNEL_ID_SIZE];
+};
+
+struct {
+	struct mdep_cfg mdep[MDEP_CFG_SIZE];
+} app[APP_ID_SIZE];
+
+const bthl_interface_t *if_hl = NULL;
+
+static void app_reg_state_cb(int app_id, bthl_app_reg_state_t state)
+{
+	haltest_info("%s: app_id=%d app_reg_state=%s\n", __func__,
+				app_id, bthl_app_reg_state_t2str(state));
+}
+
+static void channel_state_cb(int app_id, bt_bdaddr_t *bd_addr,
+					int index, int channel_id,
+					bthl_channel_state_t state, int fd)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: app_id=%d bd_addr=%s mdep_cfg_index=%d\n"
+			"channel_id=%d channel_state=%s fd=%d\n", __func__,
+			app_id, bt_bdaddr_t2str(bd_addr, addr), index,
+			channel_id, bthl_channel_state_t2str(state), fd);
+
+	if (app_id >= APP_ID_SIZE || index >= MDEP_CFG_SIZE
+			|| channel_id >= CHANNEL_ID_SIZE) {
+		haltest_error("exceeds maximum limit");
+		return;
+	}
+
+	if (state == BTHL_CONN_STATE_CONNECTED) {
+		app[app_id].mdep[index].channel[channel_id].fd = fd;
+
+		/*
+		 * PTS expects dummy data on fd when it
+		 * connects in source role.
+		 */
+		if (app[app_id].mdep[index].role == BTHL_MDEP_ROLE_SOURCE)
+			if (write(fd, "0", sizeof("0")) < 0)
+				haltest_error("writing data on fd failed\n");
+
+		return;
+	}
+
+	if (state == BTHL_CONN_STATE_DISCONNECTED ||
+			state == BTHL_CONN_STATE_DESTROYED) {
+		if (app[app_id].mdep[index].channel[channel_id].fd >= 0) {
+			close(app[app_id].mdep[index].channel[channel_id].fd);
+			app[app_id].mdep[index].channel[channel_id].fd = -1;
+		}
+	}
+}
+
+static bthl_callbacks_t hl_cbacks = {
+	.size = sizeof(hl_cbacks),
+	.app_reg_state_cb = app_reg_state_cb,
+	.channel_state_cb = channel_state_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	int i, j, k;
+
+	for (i = 0; i < APP_ID_SIZE; i++) {
+		for (j = 0; j < MDEP_CFG_SIZE; j++) {
+			app[i].mdep[j].role = 0;
+			for (k = 0; k < CHANNEL_ID_SIZE; k++)
+				app[i].mdep[j].channel[k].fd = -1;
+		}
+	}
+
+
+	RETURN_IF_NULL(if_hl);
+
+	EXEC(if_hl->init, &hl_cbacks);
+}
+
+/* register_application */
+
+static void register_application_p(int argc, const char **argv)
+{
+	bthl_reg_param_t reg;
+	uint16_t mdep_argc_init, mdep_argc_off;
+	int app_id = -1;
+	int i;
+
+	RETURN_IF_NULL(if_hl);
+
+	if (argc <= 2) {
+		haltest_error("No app name is specified\n");
+		return;
+	}
+
+	if (argc <= 3) {
+		haltest_error("No provider is specified\n");
+		return;
+	}
+
+	if (argc <= 4) {
+		haltest_error("No service name is specified\n");
+		return;
+	}
+
+	if (argc <= 5) {
+		haltest_error("No service description is specified\n");
+		return;
+	}
+
+	if (argc <= 6) {
+		haltest_error("No num of mdeps is specified\n");
+		return;
+	}
+
+	memset(&reg, 0, sizeof(reg));
+
+	if (argc != ((atoi(argv[6]) * 4) + 7)) {
+		haltest_error("mdep cfg argumetns are not proper\n");
+		return;
+	}
+
+	reg.application_name = argv[2];
+
+	if (strcmp("-", argv[3]))
+		reg.provider_name = argv[3];
+
+	if (strcmp("-", argv[4]))
+		reg.srv_name = argv[4];
+
+	if (strcmp("-", argv[5]))
+		reg.srv_desp = argv[5];
+
+	reg.number_of_mdeps = atoi(argv[6]);
+
+	reg.mdep_cfg = malloc(reg.number_of_mdeps * sizeof(bthl_mdep_cfg_t));
+	if (!reg.mdep_cfg) {
+		haltest_error("malloc failed\n");
+		return;
+	}
+	mdep_argc_init = 7;
+
+	for (i = 0; i < reg.number_of_mdeps; i++) {
+		mdep_argc_off = mdep_argc_init + (4 * i);
+		reg.mdep_cfg[i].mdep_role =
+				str2bthl_mdep_role_t(argv[mdep_argc_off]);
+		reg.mdep_cfg[i].data_type = atoi(argv[mdep_argc_off + 1]);
+		reg.mdep_cfg[i].channel_type =
+			str2bthl_channel_type_t(argv[mdep_argc_off + 2]);
+
+		if (!strcmp("-", argv[mdep_argc_off + 3])) {
+			reg.mdep_cfg[i].mdep_description = NULL;
+			continue;
+		}
+
+		reg.mdep_cfg[i].mdep_description = argv[mdep_argc_off + 3];
+	}
+
+	EXEC(if_hl->register_application, &reg, &app_id);
+
+	for (i = 0; i < reg.number_of_mdeps; i++)
+		app[app_id].mdep[i].role = reg.mdep_cfg[i].mdep_role;
+
+	free(reg.mdep_cfg);
+}
+
+/* unregister_application */
+
+static void unregister_application_p(int argc, const char **argv)
+{
+	uint32_t app_id;
+
+	RETURN_IF_NULL(if_hl);
+
+	if (argc <= 2) {
+		haltest_error("No app id is specified");
+		return;
+	}
+
+	app_id = (uint32_t) atoi(argv[2]);
+
+	EXEC(if_hl->unregister_application, app_id);
+}
+
+/* connect_channel */
+
+static void connect_channel_p(int argc, const char **argv)
+{
+	uint32_t app_id, mdep_cfg_index;
+	int channel_id = -1;
+	bt_bdaddr_t bd_addr;
+
+	RETURN_IF_NULL(if_hl);
+
+	if (argc <= 2) {
+		haltest_error("No app id is specified");
+		return;
+	}
+
+	VERIFY_ADDR_ARG(3, &bd_addr);
+
+	if (argc <= 4) {
+		haltest_error("No mdep cfg index is specified");
+		return;
+	}
+
+	app_id = (uint32_t) atoi(argv[2]);
+	mdep_cfg_index = (uint32_t) atoi(argv[4]);
+
+	EXEC(if_hl->connect_channel, app_id, &bd_addr, mdep_cfg_index,
+								&channel_id);
+}
+
+/* destroy_channel */
+
+static void destroy_channel_p(int argc, const char **argv)
+{
+	uint32_t channel_id;
+
+	RETURN_IF_NULL(if_hl);
+
+	if (argc <= 2) {
+		haltest_error("No channel id is specified");
+		return;
+	}
+
+	channel_id = (uint32_t) atoi(argv[2]);
+
+	EXEC(if_hl->destroy_channel, channel_id);
+}
+
+/* close_channel */
+
+static void close_channel_p(int argc, const char **argv)
+{
+	uint32_t app_id;
+	uint8_t index;
+	int channel_id;
+
+	RETURN_IF_NULL(if_hl);
+
+	if (argc <= 2) {
+		haltest_error("No app id is specified");
+		return;
+	}
+
+	if (argc <= 3) {
+		haltest_error("No mdep_cfg_index is specified");
+		return;
+	}
+
+	if (argc <= 4) {
+		haltest_error("No channel_id is specified");
+		return;
+	}
+
+	app_id = (uint32_t) atoi(argv[2]);
+	if (app_id >= APP_ID_SIZE) {
+		haltest_error("Wrong app_id specified: %u\n", app_id);
+		return;
+	}
+
+	index = (uint8_t) atoi(argv[3]);
+	if (index >= MDEP_CFG_SIZE) {
+		haltest_error("Wrong mdep cfg index: %u\n", index);
+		return;
+	}
+
+	channel_id = atoi(argv[4]);
+	if (channel_id >= CHANNEL_ID_SIZE) {
+		haltest_error("Wrong channel id: %u\n", channel_id);
+		return;
+	}
+
+	if (app[app_id].mdep[index].channel[channel_id].fd >= 0) {
+		shutdown(app[app_id].mdep[index].channel[channel_id].fd,
+								SHUT_RDWR);
+		app[app_id].mdep[index].channel[channel_id].fd = -1;
+	}
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hl);
+
+	EXECV(if_hl->cleanup);
+	if_hl = NULL;
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODH(register_application,
+		"<app_name> <provider_name> <srv_name> <srv_descr>\n"
+		"<num_of_mdeps>\n"
+		"[[<mdep_role>] [<data_type>] [<channel_type>] [<mdep_descr>]]"
+		"..."),
+	STD_METHODH(unregister_application, "<app_id>"),
+	STD_METHODH(connect_channel, "<app_id> <bd_addr> <mdep_cfg_index>"),
+	STD_METHODH(destroy_channel, "<channel_id>"),
+	STD_METHODH(close_channel, "<app_id> <mdep_cfg_index> <channel_id>"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface hl_if = {
+	.name = "hl",
+	.methods = methods
+};
diff --git a/repo/android/client/if-main.h b/repo/android/client/if-main.h
new file mode 100644
index 0000000..d239bb2
--- /dev/null
+++ b/repo/android/client/if-main.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <poll.h>
+
+#include <hardware/audio.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_av.h>
+#include <hardware/bt_hh.h>
+#include <hardware/bt_pan.h>
+#include <hardware/bt_sock.h>
+#include <hardware/bt_hf.h>
+#include <hardware/bt_hl.h>
+
+#include "hal.h"
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+#include <hardware/bt_hf_client.h>
+#include <hardware/bt_mce.h>
+#endif
+
+#include <hardware/bt_rc.h>
+#include <hardware/bt_gatt.h>
+#include <hardware/bt_gatt_types.h>
+#include <hardware/bt_gatt_client.h>
+#include <hardware/bt_gatt_server.h>
+
+extern audio_hw_device_t *if_audio;
+
+/* Interfaces from hal that can be populated during application lifetime */
+extern const bt_interface_t *if_bluetooth;
+extern const btav_interface_t *if_av;
+extern const btrc_interface_t *if_rc;
+extern const bthf_interface_t *if_hf;
+extern const bthh_interface_t *if_hh;
+extern const btpan_interface_t *if_pan;
+extern const bthl_interface_t *if_hl;
+extern const btsock_interface_t *if_sock;
+extern const btgatt_interface_t *if_gatt;
+extern const btgatt_server_interface_t *if_gatt_server;
+extern const btgatt_client_interface_t *if_gatt_client;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+extern const btrc_ctrl_interface_t *if_rc_ctrl;
+extern const bthf_client_interface_t *if_hf_client;
+extern const btmce_interface_t *if_mce;
+extern const btav_interface_t *if_av_sink;
+#endif
+
+/*
+ * Structure defines top level interfaces that can be used in test tool
+ * this will contain values as: bluetooth, av, gatt, socket, pan...
+ */
+struct interface {
+	const char *name; /* interface name */
+	struct method *methods; /* methods available for this interface */
+};
+
+extern const struct interface audio_if;
+extern const struct interface sco_if;
+extern const struct interface bluetooth_if;
+extern const struct interface av_if;
+extern const struct interface rc_if;
+extern const struct interface gatt_if;
+extern const struct interface gatt_client_if;
+extern const struct interface gatt_server_if;
+extern const struct interface pan_if;
+extern const struct interface sock_if;
+extern const struct interface hf_if;
+extern const struct interface hh_if;
+extern const struct interface hl_if;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+extern const struct interface ctrl_rc_if;
+extern const struct interface hf_client_if;
+extern const struct interface mce_if;
+extern const struct interface av_sink_if;
+#endif
+
+/* Interfaces that will show up in tool (first part of command line) */
+extern const struct interface *interfaces[];
+
+#define METHOD(name, func, comp, help) {name, func, comp, help}
+#define STD_METHOD(m) {#m, m##_p, NULL, NULL}
+#define STD_METHODC(m) {#m, m##_p, m##_c, NULL}
+#define STD_METHODH(m, h) {#m, m##_p, NULL, h}
+#define STD_METHODCH(m, h) {#m, m##_p, m##_c, h}
+#define END_METHOD {"", NULL, NULL, NULL}
+
+/*
+ * Function to parse argument for function, argv[0] and argv[1] are already
+ * parsed before this function is called and contain interface and method name
+ * up to argc - 1 arguments are finished and should be used to decide which
+ * function enumeration function to return
+ */
+typedef void (*parse_and_call)(int argc, const char **argv);
+
+/*
+ * This is prototype of function that will return string for given number.
+ * Purpose is to enumerate string for auto completion.
+ * Function of this type will always be called in loop.
+ * First time function is called i = 0, then if function returns non-NULL
+ * it will be called again till for some value of i it will return NULL
+ */
+typedef const char *(*enum_func)(void *user, int i);
+
+/*
+ * This is prototype of function that when given argc, argv will
+ * fill enum_func with pointer to function that will enumerate
+ * parameters for argc argument, user will be passed to enum_func.
+ */
+typedef void (*tab_complete)(int argc, const char **argv, enum_func *enum_func,
+								void **user);
+
+/*
+ * For each method there is name and two functions to parse command line
+ * and call proper hal function on.
+ */
+struct method {
+	const char *name;
+	parse_and_call func;
+	tab_complete complete;
+	const char *help;
+};
+
+int haltest_error(const char *format, ...)
+					__attribute__((format(printf, 1, 2)));
+int haltest_info(const char *format, ...)__attribute__((format(printf, 1, 2)));
+int haltest_warn(const char *format, ...)__attribute__((format(printf, 1, 2)));
+
+/* Enumerator for discovered devices, to be used as tab completion enum_func */
+const char *enum_devices(void *v, int i);
+const char *interface_name(void *v, int i);
+const char *command_name(void *v, int i);
+void add_remote_device(const bt_bdaddr_t *addr);
+bool close_hw_bt_dev(void);
+
+const struct interface *get_interface(const char *name);
+struct method *get_method(struct method *methods, const char *name);
+struct method *get_command(const char *name);
+const struct method *get_interface_method(const char *iname,
+							const char *mname);
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+/* Helper macro for executing function on interface and printing BT_STATUS */
+#define EXEC(f, ...) \
+	{ \
+		if (f) { \
+			int err = f(__VA_ARGS__); \
+			haltest_info("%s: %s\n", #f, bt_status_t2str(err)); \
+		} else { \
+			haltest_info("%s is NULL\n", #f); \
+		} \
+	}
+
+/* Helper macro for executing void function on interface */
+#define EXECV(f, ...) \
+	{ \
+		(void) f(__VA_ARGS__); \
+		haltest_info("%s: void\n", #f); \
+	}
+
+#define RETURN_IF_NULL(x) \
+	do { if (!x) { haltest_error("%s is NULL\n", #x); return; } } while (0)
+
+#define VERIFY_ADDR_ARG(n, adr) \
+	do { \
+		if (n < argc) {\
+			str2bt_bdaddr_t(argv[n], adr); \
+		} else { \
+			haltest_error("No address specified\n");\
+			return;\
+		} \
+	} while (0)
diff --git a/repo/android/client/if-mce.c b/repo/android/client/if-mce.c
new file mode 100644
index 0000000..5f101e0
--- /dev/null
+++ b/repo/android/client/if-mce.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const btmce_interface_t *if_mce = NULL;
+
+/*
+ *  Callback for get_remote_mas_instances
+ */
+static void btmce_remote_mas_instances_cb(bt_status_t status,
+						bt_bdaddr_t *bd_addr,
+						int num_instances,
+						btmce_mas_instance_t *instances)
+{
+	int i;
+
+	haltest_info("%s: status=%s bd_addr=%s num_instance=%d\n", __func__,
+				bt_status_t2str(status), bdaddr2str(bd_addr),
+				num_instances);
+
+	for (i = 0; i < num_instances; i++)
+		haltest_info("id=%d scn=%d msg_types=%d name=%s\n",
+				instances[i].id, instances[i].scn,
+				instances[i].msg_types, instances[i].p_name);
+}
+
+static btmce_callbacks_t mce_cbacks = {
+	.size = sizeof(mce_cbacks),
+	.remote_mas_instances_cb = btmce_remote_mas_instances_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_mce);
+
+	EXEC(if_mce->init, &mce_cbacks);
+}
+
+static void get_remote_mas_instances_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+/* search for MAS instances on remote device */
+
+static void get_remote_mas_instances_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_mce);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_mce->get_remote_mas_instances, &addr);
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(get_remote_mas_instances, "<addr>"),
+	END_METHOD
+};
+
+const struct interface mce_if = {
+	.name = "mce",
+	.methods = methods
+};
diff --git a/repo/android/client/if-pan.c b/repo/android/client/if-pan.c
new file mode 100644
index 0000000..b0c4b84
--- /dev/null
+++ b/repo/android/client/if-pan.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const btpan_interface_t *if_pan = NULL;
+
+typedef int btpan_role_t;
+
+SINTMAP(btpan_role_t, -1, "(unknown)")
+	DELEMENT(BTPAN_ROLE_NONE),
+	DELEMENT(BTPAN_ROLE_PANNAP),
+	DELEMENT(BTPAN_ROLE_PANU),
+ENDMAP
+
+SINTMAP(btpan_connection_state_t, -1, "(unknown)")
+	DELEMENT(BTPAN_STATE_CONNECTED),
+	DELEMENT(BTPAN_STATE_CONNECTING),
+	DELEMENT(BTPAN_STATE_DISCONNECTED),
+	DELEMENT(BTPAN_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(btpan_control_state_t, -1, "(unknown)")
+	DELEMENT(BTPAN_STATE_ENABLED),
+	DELEMENT(BTPAN_STATE_DISABLED),
+ENDMAP
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void control_state_cb(btpan_control_state_t state, int local_role,
+					bt_status_t error, const char *ifname)
+#else
+static void control_state_cb(btpan_control_state_t state, bt_status_t error,
+					int local_role, const char *ifname)
+#endif
+{
+	haltest_info("%s: state=%s error=%s local_role=%s ifname=%s\n",
+			__func__, btpan_control_state_t2str(state),
+			bt_status_t2str(error), btpan_role_t2str(local_role),
+			ifname);
+}
+
+static char last_used_addr[MAX_ADDR_STR_LEN];
+
+static void connection_state_cb(btpan_connection_state_t state,
+				bt_status_t error, const bt_bdaddr_t *bd_addr,
+				int local_role, int remote_role)
+{
+	haltest_info("%s: state=%s error=%s bd_addr=%s local_role=%s remote_role=%s\n",
+			__func__, btpan_connection_state_t2str(state),
+			bt_status_t2str(error),
+			bt_bdaddr_t2str(bd_addr, last_used_addr),
+			btpan_role_t2str(local_role),
+			btpan_role_t2str(remote_role));
+}
+
+static btpan_callbacks_t pan_cbacks = {
+	.size = sizeof(pan_cbacks),
+	.control_state_cb = control_state_cb,
+	.connection_state_cb = connection_state_cb
+};
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_pan);
+
+	EXEC(if_pan->init, &pan_cbacks);
+}
+
+/* enable */
+
+static void enable_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(btpan_role_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void enable_p(int argc, const char **argv)
+{
+	int local_role;
+
+	RETURN_IF_NULL(if_pan);
+
+	/* local role */
+	if (argc < 3) {
+		haltest_error("No local mode specified\n");
+		return;
+	}
+	local_role = str2btpan_role_t(argv[2]);
+	if (local_role == -1)
+		local_role = atoi(argv[2]);
+
+	EXEC(if_pan->enable, local_role);
+}
+
+/* get_local_role */
+
+static void get_local_role_p(int argc, const char **argv)
+{
+	int local_role;
+
+	RETURN_IF_NULL(if_pan);
+
+	local_role = if_pan->get_local_role();
+	haltest_info("local_role: %s\n", btpan_role_t2str(local_role));
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	} else if (argc == 4 || argc == 5) {
+		*user = TYPE_ENUM(btpan_role_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void connect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	int local_role;
+	int remote_role;
+
+	RETURN_IF_NULL(if_pan);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	/* local role */
+	if (argc < 4) {
+		haltest_error("No local mode specified\n");
+		return;
+	}
+	local_role = str2btpan_role_t(argv[3]);
+	if (local_role == -1)
+		local_role = atoi(argv[3]);
+
+	/* remote role */
+	if (argc < 5) {
+		haltest_error("No remote mode specified\n");
+		return;
+	}
+	remote_role = str2btpan_role_t(argv[4]);
+	if (remote_role == -1)
+		remote_role = atoi(argv[4]);
+
+	EXEC(if_pan->connect, &addr, local_role, remote_role);
+}
+
+/* disconnect */
+
+static void disconnect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = last_used_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void disconnect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_pan);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_pan->disconnect, &addr);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_pan);
+
+	EXECV(if_pan->cleanup);
+	if_pan = NULL;
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(connect, "<addr> <local_role> <remote_role>"),
+	STD_METHODCH(enable, "<local_role>"),
+	STD_METHOD(get_local_role),
+	STD_METHODCH(disconnect, "<addr>"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface pan_if = {
+	.name = "pan",
+	.methods = methods
+};
diff --git a/repo/android/client/if-rc-ctrl.c b/repo/android/client/if-rc-ctrl.c
new file mode 100644
index 0000000..3980764
--- /dev/null
+++ b/repo/android/client/if-rc-ctrl.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_rc.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+const btrc_ctrl_interface_t *if_rc_ctrl = NULL;
+static char last_addr[MAX_ADDR_STR_LEN];
+
+static void passthrough_rsp_cb(int id, int key_state)
+{
+	haltest_info("%s: id=%d key_state=%d\n", __func__, id, key_state);
+}
+
+static void connection_state_cb(bool state, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+					state ? "true" : "false",
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+static btrc_ctrl_callbacks_t rc_ctrl_cbacks = {
+	.size = sizeof(rc_ctrl_cbacks),
+	.passthrough_rsp_cb = passthrough_rsp_cb,
+	.connection_state_cb = connection_state_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_rc_ctrl);
+
+	EXEC(if_rc_ctrl->init, &rc_ctrl_cbacks);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_rc_ctrl);
+
+	EXECV(if_rc_ctrl->cleanup);
+	if_rc_ctrl = NULL;
+}
+
+/* send_pass_through_cmd */
+static void send_pass_through_cmd_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+static void send_pass_through_cmd_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	uint8_t key_code, key_state;
+
+	RETURN_IF_NULL(if_rc);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc < 4) {
+		haltest_error("No key code specified\n");
+		return;
+	}
+
+	key_code = (uint8_t) atoi(argv[3]);
+
+	if (argc < 5) {
+		haltest_error("No key state specified\n");
+		return;
+	}
+
+	key_state = (uint8_t) atoi(argv[4]);
+
+	EXEC(if_rc_ctrl->send_pass_through_cmd, &addr, key_code, key_state);
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(send_pass_through_cmd, "<bd_addr> <key_code> <key_state>"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface ctrl_rc_if = {
+	.name = "rc-ctrl",
+	.methods = methods
+};
diff --git a/repo/android/client/if-rc.c b/repo/android/client/if-rc.c
new file mode 100644
index 0000000..ed65600
--- /dev/null
+++ b/repo/android/client/if-rc.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_hh.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+const btrc_interface_t *if_rc = NULL;
+
+SINTMAP(btrc_play_status_t, -1, "(unknown)")
+	DELEMENT(BTRC_PLAYSTATE_STOPPED),
+	DELEMENT(BTRC_PLAYSTATE_PLAYING),
+	DELEMENT(BTRC_PLAYSTATE_PAUSED),
+	DELEMENT(BTRC_PLAYSTATE_FWD_SEEK),
+	DELEMENT(BTRC_PLAYSTATE_REV_SEEK),
+	DELEMENT(BTRC_PLAYSTATE_ERROR),
+ENDMAP
+
+SINTMAP(btrc_media_attr_t, -1, "(unknown)")
+	DELEMENT(BTRC_MEDIA_ATTR_TITLE),
+	DELEMENT(BTRC_MEDIA_ATTR_ARTIST),
+	DELEMENT(BTRC_MEDIA_ATTR_ALBUM),
+	DELEMENT(BTRC_MEDIA_ATTR_TRACK_NUM),
+	DELEMENT(BTRC_MEDIA_ATTR_NUM_TRACKS),
+	DELEMENT(BTRC_MEDIA_ATTR_GENRE),
+	DELEMENT(BTRC_MEDIA_ATTR_PLAYING_TIME),
+ENDMAP
+
+SINTMAP(btrc_status_t, -1, "(unknown)")
+	DELEMENT(BTRC_STS_BAD_CMD),
+	DELEMENT(BTRC_STS_BAD_PARAM),
+	DELEMENT(BTRC_STS_NOT_FOUND),
+	DELEMENT(BTRC_STS_INTERNAL_ERR),
+	DELEMENT(BTRC_STS_NO_ERROR),
+ENDMAP
+
+SINTMAP(btrc_event_id_t, -1, "(unknown)")
+	DELEMENT(BTRC_EVT_PLAY_STATUS_CHANGED),
+	DELEMENT(BTRC_EVT_TRACK_CHANGE),
+	DELEMENT(BTRC_EVT_TRACK_REACHED_END),
+	DELEMENT(BTRC_EVT_TRACK_REACHED_START),
+	DELEMENT(BTRC_EVT_PLAY_POS_CHANGED),
+	DELEMENT(BTRC_EVT_APP_SETTINGS_CHANGED),
+ENDMAP
+
+SINTMAP(btrc_notification_type_t, -1, "(unknown)")
+	DELEMENT(BTRC_NOTIFICATION_TYPE_INTERIM),
+	DELEMENT(BTRC_NOTIFICATION_TYPE_CHANGED),
+ENDMAP
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+static void remote_features_cb(bt_bdaddr_t *bd_addr,
+					btrc_remote_features_t features)
+{
+	haltest_info("%s: remote_bd_addr=%s features=%u\n", __func__,
+				bt_bdaddr_t2str(bd_addr, last_addr), features);
+}
+
+static void get_play_status_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+
+static void list_player_app_attr_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+
+static void list_player_app_values_cb(btrc_player_attr_t attr_id)
+{
+	haltest_info("%s, attr_id=%d\n", __func__, attr_id);
+}
+
+static void get_player_app_value_cb(uint8_t num_attr,
+						btrc_player_attr_t *p_attrs)
+{
+	int i;
+
+	haltest_info("%s, num_attr=%d\n", __func__, num_attr);
+
+	for (i = 0; i < num_attr; i++)
+		haltest_info("attribute=%u\n", p_attrs[i]);
+}
+
+static void get_player_app_attrs_text_cb(uint8_t num_attr,
+						btrc_player_attr_t *p_attrs)
+{
+	int i;
+
+	haltest_info("%s, num_attr=%d\n", __func__, num_attr);
+
+	for (i = 0; i < num_attr; i++)
+		haltest_info("attribute=%u\n", p_attrs[i]);
+
+}
+
+static void get_player_app_values_text_cb(uint8_t attr_id, uint8_t num_val,
+								uint8_t *p_vals)
+{
+	haltest_info("%s, attr_id=%d num_val=%d values=%p\n", __func__,
+						attr_id, num_val, p_vals);
+}
+
+static void set_player_app_value_cb(btrc_player_settings_t *p_vals)
+{
+	int i;
+
+	haltest_info("%s, num_attr=%u\n", __func__, p_vals->num_attr);
+
+	for (i = 0; i < p_vals->num_attr; i++)
+		haltest_info("attr id=%u, values=%u\n", p_vals->attr_ids[i],
+							p_vals->attr_values[i]);
+}
+
+static void get_element_attr_cb(uint8_t num_attr, btrc_media_attr_t *attrs)
+{
+	uint8_t i;
+
+	haltest_info("%s, num_of_attributes=%d\n", __func__, num_attr);
+
+	for (i = 0; i < num_attr; i++)
+		haltest_info("attr id=%s\n", btrc_media_attr_t2str(attrs[i]));
+}
+
+static void register_notification_cb(btrc_event_id_t event_id, uint32_t param)
+{
+	haltest_info("%s, event=%u param=%u\n", __func__, event_id, param);
+}
+
+static void volume_change_cb(uint8_t volume, uint8_t ctype)
+{
+	haltest_info("%s, volume=%d ctype=%d\n", __func__, volume, ctype);
+}
+
+static void passthrough_cmd_cb(int id, int key_state)
+{
+	haltest_info("%s, id=%d key_state=%d\n", __func__, id, key_state);
+}
+
+static btrc_callbacks_t rc_cbacks = {
+	.size = sizeof(rc_cbacks),
+	.remote_features_cb = remote_features_cb,
+	.get_play_status_cb = get_play_status_cb,
+	.list_player_app_attr_cb = list_player_app_attr_cb,
+	.list_player_app_values_cb = list_player_app_values_cb,
+	.get_player_app_value_cb = get_player_app_value_cb,
+	.get_player_app_attrs_text_cb = get_player_app_attrs_text_cb,
+	.get_player_app_values_text_cb = get_player_app_values_text_cb,
+	.set_player_app_value_cb = set_player_app_value_cb,
+	.get_element_attr_cb = get_element_attr_cb,
+	.register_notification_cb = register_notification_cb,
+	.volume_change_cb = volume_change_cb,
+	.passthrough_cmd_cb = passthrough_cmd_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_rc);
+
+	EXEC(if_rc->init, &rc_cbacks);
+}
+
+/* get_play_status_rsp */
+
+static void get_play_status_rsp_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(btrc_play_status_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void get_play_status_rsp_p(int argc, const char **argv)
+{
+	btrc_play_status_t play_status;
+	uint32_t song_len, song_pos;
+
+	RETURN_IF_NULL(if_rc);
+
+	if (argc <= 2) {
+		haltest_error("No play status specified");
+		return;
+	}
+
+	if (argc <= 3) {
+		haltest_error("No song length specified");
+		return;
+	}
+
+	if (argc <= 4) {
+		haltest_error("No song position specified");
+		return;
+	}
+
+	play_status = str2btrc_play_status_t(argv[2]);
+	song_len = (uint32_t) atoi(argv[3]);
+	song_pos = (uint32_t) atoi(argv[4]);
+
+	EXEC(if_rc->get_play_status_rsp, play_status, song_len, song_pos);
+}
+
+/* get_element_attr_rsp */
+
+static void get_element_attr_rsp_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 4) {
+		*user = TYPE_ENUM(btrc_media_attr_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void get_element_attr_rsp_p(int argc, const char **argv)
+{
+	uint8_t num_attr;
+	btrc_element_attr_val_t attrs;
+
+	RETURN_IF_NULL(if_rc);
+
+	if (argc <= 2) {
+		haltest_error("No number of attributes specified");
+		return;
+	}
+
+	if (argc <= 4) {
+		haltest_error("No attr id and value specified");
+		return;
+	}
+
+	num_attr = (uint8_t) atoi(argv[2]);
+	attrs.attr_id = str2btrc_media_attr_t(argv[3]);
+	strcpy((char *)attrs.text, argv[4]);
+
+	EXEC(if_rc->get_element_attr_rsp, num_attr, &attrs);
+}
+
+/* set_volume */
+
+static void set_volume_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+}
+
+static void set_volume_p(int argc, const char **argv)
+{
+	uint8_t volume;
+
+	RETURN_IF_NULL(if_rc);
+
+	if (argc <= 2) {
+		haltest_error("No volume specified");
+		return;
+	}
+
+	volume = (uint8_t) atoi(argv[2]);
+
+	EXEC(if_rc->set_volume, volume);
+}
+
+/* set_player_app_value_rsp */
+
+static void set_player_app_value_rsp_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(btrc_status_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void set_player_app_value_rsp_p(int argc, const char **argv)
+{
+	btrc_status_t rsp_status;
+
+	RETURN_IF_NULL(if_rc);
+
+	if (argc <= 2) {
+		haltest_error("No response status specified");
+		return;
+	}
+
+	rsp_status = str2btrc_status_t(argv[2]);
+
+	EXEC(if_rc->set_player_app_value_rsp, rsp_status);
+}
+
+/* register_notification_rsp */
+
+static void register_notification_rsp_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(btrc_event_id_t);
+		*enum_func = enum_defines;
+	}
+
+	if (argc == 4) {
+		*user = TYPE_ENUM(btrc_notification_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void register_notification_rsp_p(int argc, const char **argv)
+{
+	btrc_event_id_t event_id;
+	btrc_notification_type_t type;
+	btrc_register_notification_t reg;
+	uint32_t song_pos;
+	uint64_t track;
+
+	RETURN_IF_NULL(if_rc);
+
+	memset(&reg, 0, sizeof(reg));
+	event_id = str2btrc_event_id_t(argv[2]);
+	type = str2btrc_notification_type_t(argv[3]);
+
+	switch (event_id) {
+	case BTRC_EVT_PLAY_STATUS_CHANGED:
+		reg.play_status = str2btrc_play_status_t(argv[4]);
+		break;
+
+	case BTRC_EVT_TRACK_CHANGE:
+		track = strtoull(argv[5], NULL, 10);
+		memcpy(reg.track, &track, sizeof(btrc_uid_t));
+		break;
+
+	case BTRC_EVT_TRACK_REACHED_END:
+	case BTRC_EVT_TRACK_REACHED_START:
+		break;
+
+	case BTRC_EVT_PLAY_POS_CHANGED:
+		song_pos = strtoul(argv[4], NULL, 10);
+		memcpy(&reg.song_pos, &song_pos, sizeof(uint32_t));
+		break;
+
+	case BTRC_EVT_APP_SETTINGS_CHANGED:
+		haltest_error("not supported");
+		return;
+	}
+
+	EXEC(if_rc->register_notification_rsp, event_id, type, &reg);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_rc);
+
+	EXECV(if_rc->cleanup);
+	if_rc = NULL;
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(get_play_status_rsp,
+					"<play_status> <song_len> <song_pos>"),
+	STD_METHODCH(get_element_attr_rsp, "<num_attr> <attrs_id> <value>"),
+	STD_METHODCH(set_player_app_value_rsp, "<rsp_status>"),
+	STD_METHODCH(set_volume, "<volume>"),
+	STD_METHODCH(register_notification_rsp,
+			"<event_id> <type> <respective_data...>\n"
+			"BTRC_EVT_PLAY_STATUS_CHANGED <type> <play_status>\n"
+			"BTRC_EVT_TRACK_CHANGE <type> <track>\n"
+			"BTRC_EVT_TRACK_REACHED_END <type>\n"
+			"BTRC_EVT_TRACK_REACHED_START <type>\n"
+			"BTRC_EVT_PLAY_POS_CHANGED <type> <song_pos>\n"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface rc_if = {
+	.name = "rc",
+	.methods = methods
+};
diff --git a/repo/android/client/if-sco.c b/repo/android/client/if-sco.c
new file mode 100644
index 0000000..5a68ed5
--- /dev/null
+++ b/repo/android/client/if-sco.c
@@ -0,0 +1,815 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <pthread.h>
+#include <unistd.h>
+#include <math.h>
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+audio_hw_device_t *if_audio_sco = NULL;
+static struct audio_stream_out *stream_out = NULL;
+static struct audio_stream_in *stream_in = NULL;
+
+static size_t buffer_size = 0;
+static size_t buffer_size_in = 0;
+static pthread_t play_thread = 0;
+static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+enum state {
+	STATE_STOPPED,
+	STATE_STOPPING,
+	STATE_PLAYING,
+	STATE_SUSPENDED,
+	STATE_MAX
+};
+
+SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)")
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_LOW_FREQUENCY),
+	DELEMENT(AUDIO_CHANNEL_OUT_BACK_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_BACK_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_BACK_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_SIDE_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_SIDE_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_MONO),
+	DELEMENT(AUDIO_CHANNEL_OUT_STEREO),
+	DELEMENT(AUDIO_CHANNEL_OUT_QUAD),
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+	DELEMENT(AUDIO_CHANNEL_OUT_SURROUND),
+#else
+	DELEMENT(AUDIO_CHANNEL_OUT_QUAD_BACK),
+	DELEMENT(AUDIO_CHANNEL_OUT_QUAD_SIDE),
+	DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_BACK),
+	DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_SIDE),
+#endif
+	DELEMENT(AUDIO_CHANNEL_OUT_5POINT1),
+	DELEMENT(AUDIO_CHANNEL_OUT_7POINT1),
+	DELEMENT(AUDIO_CHANNEL_OUT_ALL),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ENDMAP
+
+SINTMAP(audio_format_t, -1, "(AUDIO_FORMAT_INVALID)")
+	DELEMENT(AUDIO_FORMAT_DEFAULT),
+	DELEMENT(AUDIO_FORMAT_PCM),
+	DELEMENT(AUDIO_FORMAT_MP3),
+	DELEMENT(AUDIO_FORMAT_AMR_NB),
+	DELEMENT(AUDIO_FORMAT_AMR_WB),
+	DELEMENT(AUDIO_FORMAT_AAC),
+	DELEMENT(AUDIO_FORMAT_HE_AAC_V1),
+	DELEMENT(AUDIO_FORMAT_HE_AAC_V2),
+	DELEMENT(AUDIO_FORMAT_VORBIS),
+	DELEMENT(AUDIO_FORMAT_MAIN_MASK),
+	DELEMENT(AUDIO_FORMAT_SUB_MASK),
+	DELEMENT(AUDIO_FORMAT_PCM_16_BIT),
+	DELEMENT(AUDIO_FORMAT_PCM_8_BIT),
+	DELEMENT(AUDIO_FORMAT_PCM_32_BIT),
+	DELEMENT(AUDIO_FORMAT_PCM_8_24_BIT),
+ENDMAP
+
+static int current_state = STATE_STOPPED;
+
+#define SAMPLERATE 44100
+static short sample[SAMPLERATE];
+static uint16_t sample_pos;
+
+static void init_p(int argc, const char **argv)
+{
+	int err;
+	const hw_module_t *module;
+	audio_hw_device_t *device;
+
+	err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, "sco", &module);
+	if (err) {
+		haltest_error("hw_get_module_by_class returned %d\n", err);
+		return;
+	}
+
+	err = audio_hw_device_open(module, &device);
+	if (err) {
+		haltest_error("audio_hw_device_open returned %d\n", err);
+		return;
+	}
+
+	if_audio_sco = device;
+}
+
+static int feed_from_file(short *buffer, void *data)
+{
+	FILE *in = data;
+	return fread(buffer, buffer_size, 1, in);
+}
+
+static int feed_from_generator(short *buffer, void *data)
+{
+	size_t i = 0;
+	float volume = 0.5;
+	float *freq = data;
+	float f = 1;
+
+	if (freq)
+		f = *freq;
+
+	/* buffer_size is in bytes but we are using buffer of shorts (2 bytes)*/
+	for (i = 0; i < buffer_size / sizeof(*buffer) - 1;) {
+		if (sample_pos >= SAMPLERATE)
+			sample_pos = sample_pos % SAMPLERATE;
+
+		/* Use the same sample for both channels */
+		buffer[i++] = sample[sample_pos] * volume;
+		buffer[i++] = sample[sample_pos] * volume;
+
+		sample_pos += f;
+	}
+
+	return buffer_size;
+}
+
+static int feed_from_in(short *buffer, void *data)
+{
+	return stream_in->read(stream_in, buffer, buffer_size_in);
+}
+
+static void prepare_sample(void)
+{
+	int x;
+	double s;
+
+	haltest_info("Preparing audio sample...\n");
+
+	for (x = 0; x < SAMPLERATE; x++) {
+		/* prepare sinusoidal 1Hz sample */
+		s = (2.0 * 3.14159) * ((double)x / SAMPLERATE);
+		s = sin(s);
+
+		/* remap <-1, 1> to signed 16bit PCM range */
+		sample[x] = s * 32767;
+	}
+
+	sample_pos = 0;
+}
+
+static void mono_to_stereo_pcm16(const int16_t *in, int16_t *out, size_t samples)
+{
+	size_t i;
+
+	for (i = 0; i < samples; i++) {
+		out[2 * i] = in[i];
+		out[2 * i + 1] = in[i];
+	}
+}
+
+static void *playback_thread(void *data)
+{
+	int (*filbuff_cb) (short*, void*);
+	short buffer[buffer_size / sizeof(short)];
+	short buffer_in[buffer_size_in / sizeof(short)];
+	size_t len = 0;
+	ssize_t w_len = 0;
+	FILE *in = data;
+	void *cb_data = NULL;
+	float freq = 440.0;
+
+	/* Use file or fall back to generator */
+	if (in) {
+		if (data == stream_in)
+			filbuff_cb = feed_from_in;
+		else {
+			filbuff_cb = feed_from_file;
+			cb_data = in;
+		}
+	} else {
+		prepare_sample();
+		filbuff_cb = feed_from_generator;
+		cb_data = &freq;
+	}
+
+	pthread_mutex_lock(&state_mutex);
+	current_state = STATE_PLAYING;
+	pthread_mutex_unlock(&state_mutex);
+
+	do {
+		pthread_mutex_lock(&state_mutex);
+
+		if (current_state == STATE_STOPPING) {
+			haltest_info("Detected stopping\n");
+			pthread_mutex_unlock(&state_mutex);
+			break;
+		} else if (current_state == STATE_SUSPENDED) {
+			pthread_mutex_unlock(&state_mutex);
+			usleep(500);
+			continue;
+		}
+
+		pthread_mutex_unlock(&state_mutex);
+
+		if (data && data == stream_in) {
+			int chan_in = popcount(stream_in->common.get_channels(&stream_in->common));
+			int chan_out = popcount(stream_out->common.get_channels(&stream_out->common));
+
+			len = filbuff_cb(buffer_in, cb_data);
+
+			if (chan_in == 1 && chan_out == 2) {
+				mono_to_stereo_pcm16(buffer_in,
+							buffer,
+							buffer_size_in / 2);
+			}
+		} else
+			len = filbuff_cb(buffer, cb_data);
+
+		pthread_mutex_lock(&outstream_mutex);
+		if (!stream_out) {
+			pthread_mutex_unlock(&outstream_mutex);
+			break;
+		}
+
+		w_len = stream_out->write(stream_out, buffer, buffer_size);
+		pthread_mutex_unlock(&outstream_mutex);
+	} while (len && w_len > 0);
+
+	if (in && data != stream_in)
+		fclose(in);
+
+	pthread_mutex_lock(&state_mutex);
+	current_state = STATE_STOPPED;
+	pthread_mutex_unlock(&state_mutex);
+
+	haltest_info("Done playing.\n");
+
+	return NULL;
+}
+
+static void write_stereo_pcm16(const short *input, size_t len, FILE *out)
+{
+	short sample[2];
+	size_t i;
+
+	for (i = 0; i < len / 2; i++) {
+		sample[0] = input[i];
+		sample[1] = input[i];
+
+		fwrite(sample, sizeof(sample), 1, out);
+	}
+}
+
+static void *read_thread(void *data)
+{
+	int (*filbuff_cb) (short*, void*) = feed_from_in;
+	short buffer[buffer_size_in / sizeof(short)];
+	ssize_t len = 0;
+	void *cb_data = NULL;
+	FILE *out = data;
+
+	pthread_mutex_lock(&state_mutex);
+	current_state = STATE_PLAYING;
+	pthread_mutex_unlock(&state_mutex);
+
+	do {
+		pthread_mutex_lock(&state_mutex);
+
+		if (current_state == STATE_STOPPING) {
+			haltest_info("Detected stopping\n");
+			pthread_mutex_unlock(&state_mutex);
+			break;
+		} else if (current_state == STATE_SUSPENDED) {
+			pthread_mutex_unlock(&state_mutex);
+			usleep(500);
+			continue;
+		}
+
+		pthread_mutex_unlock(&state_mutex);
+
+		len = filbuff_cb(buffer, cb_data);
+		if (len < 0) {
+			haltest_error("Error receiving SCO data");
+			break;
+		}
+
+		haltest_info("Read %zd bytes\n", len);
+
+		if (out) {
+			write_stereo_pcm16(buffer, len, out);
+			haltest_info("Written %zd bytes\n", len * 2);
+		}
+	} while (len);
+
+	if (out)
+		fclose(out);
+
+	pthread_mutex_lock(&state_mutex);
+	current_state = STATE_STOPPED;
+	pthread_mutex_unlock(&state_mutex);
+
+	haltest_info("Done reading.\n");
+
+	return NULL;
+}
+
+static void play_p(int argc, const char **argv)
+{
+	const char *fname = NULL;
+	FILE *in = NULL;
+
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3) {
+		haltest_error("Invalid audio file path.\n");
+		haltest_info("Using sound generator.\n");
+	} else {
+		fname = argv[2];
+		in = fopen(fname, "r");
+
+		if (in == NULL) {
+			haltest_error("Cannot open file: %s\n", fname);
+			return;
+		}
+		haltest_info("Playing file: %s\n", fname);
+	}
+
+	if (buffer_size == 0) {
+		haltest_error("Invalid buffer size. Was stream_out opened?\n");
+		goto fail;
+	}
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_STOPPED) {
+		haltest_error("Already playing or stream suspended!\n");
+		pthread_mutex_unlock(&state_mutex);
+		goto fail;
+	}
+	pthread_mutex_unlock(&state_mutex);
+
+	if (pthread_create(&play_thread, NULL, playback_thread, in) != 0) {
+		haltest_error("Cannot create playback thread!\n");
+		goto fail;
+	}
+
+	return;
+fail:
+	if (in)
+		fclose(in);
+}
+
+static void loop_p(int argc, const char **argv)
+{
+	int chan_out, chan_in;
+
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+	RETURN_IF_NULL(stream_in);
+
+	chan_out = popcount(stream_out->common.get_channels(&stream_out->common));
+	chan_in = popcount(stream_in->common.get_channels(&stream_in->common));
+
+	if (!buffer_size || !buffer_size_in) {
+		haltest_error("Invalid buffer sizes. Streams opened\n");
+		return;
+	}
+
+	if (buffer_size / chan_out != buffer_size_in / chan_in) {
+		haltest_error("read/write buffers differ, not supported\n");
+		return;
+	}
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_STOPPED) {
+		haltest_error("Already playing or stream suspended!\n");
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+	pthread_mutex_unlock(&state_mutex);
+
+	if (pthread_create(&play_thread, NULL, playback_thread,
+							stream_in) != 0)
+		haltest_error("Cannot create playback thread!\n");
+}
+
+static void read_p(int argc, const char **argv)
+{
+	const char *fname = NULL;
+	FILE *out = NULL;
+
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_in);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_STOPPED) {
+		haltest_error("Already playing or stream suspended!\n");
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+	pthread_mutex_unlock(&state_mutex);
+
+	if (argc < 3) {
+		haltest_error("Invalid audio file path.\n");
+		haltest_info("Using read and through away\n");
+	} else {
+		fname = argv[2];
+		out = fopen(fname, "w");
+		if (!out) {
+			haltest_error("Cannot open file: %s\n", fname);
+			return;
+		}
+
+		haltest_info("Reading to file: %s\n", fname);
+	}
+
+	if (!buffer_size_in) {
+		haltest_error("Invalid buffer size.\n");
+		goto failed;
+	}
+
+	if (pthread_create(&play_thread, NULL, read_thread, out) != 0) {
+		haltest_error("Cannot create playback thread!\n");
+		goto failed;
+	}
+
+	return;
+failed:
+	if (out)
+		fclose(out);
+}
+
+static void stop_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(play_thread);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) {
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+
+	if (stream_out) {
+		pthread_mutex_lock(&outstream_mutex);
+		stream_out->common.standby(&stream_out->common);
+		pthread_mutex_unlock(&outstream_mutex);
+	}
+
+	current_state = STATE_STOPPING;
+	pthread_mutex_unlock(&state_mutex);
+
+	pthread_join(play_thread, NULL);
+	play_thread = 0;
+
+	haltest_info("Ended %s\n", __func__);
+}
+
+static void open_output_stream_p(int argc, const char **argv)
+{
+	struct audio_config *config;
+	int err;
+
+	RETURN_IF_NULL(if_audio_sco);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state == STATE_PLAYING) {
+		haltest_error("Already playing!\n");
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+	pthread_mutex_unlock(&state_mutex);
+
+	if (argc < 3) {
+		haltest_info("No sampling rate specified. Use default conf\n");
+		config = NULL;
+	} else {
+		config = calloc(1, sizeof(struct audio_config));
+		if (!config)
+			return;
+
+		config->sample_rate = atoi(argv[2]);
+		config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+		config->format = AUDIO_FORMAT_PCM_16_BIT;
+	}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	err = if_audio_sco->open_output_stream(if_audio_sco,
+						0,
+						AUDIO_DEVICE_OUT_ALL_SCO,
+						AUDIO_OUTPUT_FLAG_NONE,
+						config,
+						&stream_out, NULL);
+#else
+	err = if_audio_sco->open_output_stream(if_audio_sco,
+						0,
+						AUDIO_DEVICE_OUT_ALL_SCO,
+						AUDIO_OUTPUT_FLAG_NONE,
+						config,
+						&stream_out);
+#endif
+	if (err < 0) {
+		haltest_error("open output stream returned %d\n", err);
+		goto failed;
+	}
+
+	buffer_size = stream_out->common.get_buffer_size(&stream_out->common);
+	if (buffer_size == 0)
+		haltest_error("Invalid buffer size received!\n");
+	else
+		haltest_info("Using buffer size: %zu\n", buffer_size);
+failed:
+	if (config)
+		free(config);
+}
+
+static void close_output_stream_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	if (play_thread)
+		stop_p(argc, argv);
+
+	if_audio_sco->close_output_stream(if_audio_sco, stream_out);
+
+	stream_out = NULL;
+	buffer_size = 0;
+}
+
+static void open_input_stream_p(int argc, const char **argv)
+{
+	struct audio_config *config;
+	int err;
+
+	RETURN_IF_NULL(if_audio_sco);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state == STATE_PLAYING) {
+		haltest_error("Already playing!\n");
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+	pthread_mutex_unlock(&state_mutex);
+
+	if (argc < 3) {
+		haltest_info("No sampling rate specified. Use default conf\n");
+		config = NULL;
+	} else {
+		config = calloc(1, sizeof(struct audio_config));
+		if (!config)
+			return;
+
+		config->sample_rate = atoi(argv[2]);
+		config->channel_mask = AUDIO_CHANNEL_OUT_MONO;
+		config->format = AUDIO_FORMAT_PCM_16_BIT;
+	}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	err = if_audio_sco->open_input_stream(if_audio_sco,
+						0,
+						AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
+						config,
+						&stream_in, 0, NULL, 0);
+#else
+	err = if_audio_sco->open_input_stream(if_audio_sco,
+						0,
+						AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
+						config,
+						&stream_in);
+#endif
+	if (err < 0) {
+		haltest_error("open output stream returned %d\n", err);
+		goto failed;
+	}
+
+	buffer_size_in = stream_in->common.get_buffer_size(&stream_in->common);
+	if (buffer_size_in == 0)
+		haltest_error("Invalid buffer size received!\n");
+	else
+		haltest_info("Using buffer size: %zu\n", buffer_size_in);
+failed:
+	if (config)
+		free(config);
+}
+
+static void close_input_stream_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_in);
+
+	if (play_thread)
+		stop_p(argc, argv);
+
+	if_audio_sco->close_input_stream(if_audio_sco, stream_in);
+
+	stream_in = NULL;
+	buffer_size_in = 0;
+}
+
+static void cleanup_p(int argc, const char **argv)
+{
+	int err;
+
+	RETURN_IF_NULL(if_audio_sco);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_STOPPED) {
+		pthread_mutex_unlock(&state_mutex);
+		close_output_stream_p(0, NULL);
+	} else {
+		pthread_mutex_unlock(&state_mutex);
+	}
+
+	err = audio_hw_device_close(if_audio_sco);
+	if (err < 0) {
+		haltest_error("audio_hw_device_close returned %d\n", err);
+		return;
+	}
+
+	if_audio_sco = NULL;
+}
+
+static void suspend_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_PLAYING) {
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+	current_state = STATE_SUSPENDED;
+	pthread_mutex_unlock(&state_mutex);
+
+	pthread_mutex_lock(&outstream_mutex);
+	stream_out->common.standby(&stream_out->common);
+	pthread_mutex_unlock(&outstream_mutex);
+}
+
+static void resume_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state == STATE_SUSPENDED)
+		current_state = STATE_PLAYING;
+	pthread_mutex_unlock(&state_mutex);
+}
+
+static void get_latency_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	haltest_info("Output audio stream latency: %d\n",
+					stream_out->get_latency(stream_out));
+}
+
+static void get_buffer_size_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	haltest_info("Current output buffer size: %zu\n",
+		stream_out->common.get_buffer_size(&stream_out->common));
+}
+
+static void get_channels_p(int argc, const char **argv)
+{
+	audio_channel_mask_t channels;
+
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	channels = stream_out->common.get_channels(&stream_out->common);
+
+	haltest_info("Channels: %s\n", audio_channel_mask_t2str(channels));
+}
+
+static void get_format_p(int argc, const char **argv)
+{
+	audio_format_t format;
+
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	format = stream_out->common.get_format(&stream_out->common);
+
+	haltest_info("Format: %s\n", audio_format_t2str(format));
+}
+
+static void get_sample_rate_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	haltest_info("Current sample rate: %d\n",
+		stream_out->common.get_sample_rate(&stream_out->common));
+}
+
+static void get_parameters_p(int argc, const char **argv)
+{
+	const char *keystr;
+
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3) {
+		haltest_info("No keys given.\n");
+		keystr = "";
+	} else {
+		keystr = argv[2];
+	}
+
+	haltest_info("Current parameters: %s\n",
+			stream_out->common.get_parameters(&stream_out->common,
+								keystr));
+}
+
+static void set_parameters_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3) {
+		haltest_error("No key=value; pairs given.\n");
+		return;
+	}
+
+	stream_out->common.set_parameters(&stream_out->common, argv[2]);
+}
+
+static void set_sample_rate_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3)
+		return;
+
+	stream_out->common.set_sample_rate(&stream_out->common, atoi(argv[2]));
+}
+
+static void init_check_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+
+	haltest_info("Init check result: %d\n",
+					if_audio_sco->init_check(if_audio_sco));
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHOD(cleanup),
+	STD_METHODH(open_output_stream, "sample_rate"),
+	STD_METHOD(close_output_stream),
+	STD_METHODH(open_input_stream, "sampling rate"),
+	STD_METHOD(close_input_stream),
+	STD_METHODH(play, "<path to pcm file>"),
+	STD_METHOD(read),
+	STD_METHOD(loop),
+	STD_METHOD(stop),
+	STD_METHOD(suspend),
+	STD_METHOD(resume),
+	STD_METHOD(get_latency),
+	STD_METHOD(get_buffer_size),
+	STD_METHOD(get_channels),
+	STD_METHOD(get_format),
+	STD_METHOD(get_sample_rate),
+	STD_METHODH(get_parameters, "<closing>"),
+	STD_METHODH(set_parameters, "<closing=value>"),
+	STD_METHODH(set_sample_rate, "<sample rate>"),
+	STD_METHOD(init_check),
+	END_METHOD
+};
+
+const struct interface sco_if = {
+	.name = "sco",
+	.methods = methods
+};
diff --git a/repo/android/client/if-sock.c b/repo/android/client/if-sock.c
new file mode 100644
index 0000000..ee2c1e8
--- /dev/null
+++ b/repo/android/client/if-sock.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+const btsock_interface_t *if_sock = NULL;
+
+SINTMAP(btsock_type_t, -1, "(unknown)")
+	DELEMENT(BTSOCK_RFCOMM),
+	DELEMENT(BTSOCK_SCO),
+	DELEMENT(BTSOCK_L2CAP),
+ENDMAP
+
+#define MAX_LISTEN_FD 15
+static int listen_fd[MAX_LISTEN_FD];
+static int listen_fd_count;
+
+static const char * const uuids[] = {
+	"00001101", "00001105", "0000112f", NULL
+};
+
+/*
+ * This function reads data from file descriptor and
+ * prints it to the user
+ */
+static void receive_from_client(struct pollfd *pollfd)
+{
+	char buf[16];
+	/*
+	 * Buffer for lines:
+	 * 41 42 43 20 20 00 31 32 00 07 04 00 00 00 00 00 ABC  .12.....
+	 */
+	char outbuf[sizeof(buf) * 4 + 2];
+	int i;
+	int ret;
+
+	if (pollfd->revents & POLLHUP) {
+		haltest_error("Disconnected fd=%d\n", pollfd->fd);
+		poll_unregister_fd(pollfd->fd, receive_from_client);
+	} else if (pollfd->revents & POLLIN) {
+		haltest_info("receiving from client fd=%d\n", pollfd->fd);
+
+		do {
+			memset(outbuf, ' ', sizeof(outbuf));
+			outbuf[sizeof(outbuf) - 1] = 0;
+			ret = recv(pollfd->fd, buf, sizeof(buf), MSG_DONTWAIT);
+
+			for (i = 0; i < ret; ++i)
+				sprintf(outbuf + i * 3, "%02X ",
+							(unsigned) buf[i]);
+			outbuf[i * 3] = ' ';
+			for (i = 0; i < ret; ++i)
+				sprintf(outbuf + 48 + i, "%c",
+					(isprint(buf[i]) ? buf[i] : '.'));
+			if (ret > 0)
+				haltest_info("%s\n", outbuf);
+		} while (ret > 0);
+	} else {
+		/* For now disconnect on all other events */
+		haltest_error("Poll event %x\n", pollfd->revents);
+		poll_unregister_fd(pollfd->fd, receive_from_client);
+	}
+}
+
+/*
+ * This function read from fd socket information about
+ * connected socket
+ */
+static void receive_sock_connect_signal(struct pollfd *pollfd)
+{
+	sock_connect_signal_t cs;
+	char addr_str[MAX_ADDR_STR_LEN];
+
+	if (pollfd->revents & POLLIN) {
+		int ret;
+
+		poll_unregister_fd(pollfd->fd, receive_sock_connect_signal);
+		ret = read(pollfd->fd, &cs, sizeof(cs));
+		if (ret != sizeof(cs)) {
+			haltest_info("Read on connect return %d\n", ret);
+			return;
+		}
+
+		haltest_info("Connection to %s channel %d status=%d\n",
+				bt_bdaddr_t2str(&cs.bd_addr, addr_str),
+				cs.channel, cs.status);
+
+		if (cs.status == 0)
+			poll_register_fd(pollfd->fd, POLLIN,
+							receive_from_client);
+	}
+
+	if (pollfd->revents & POLLHUP) {
+		haltest_error("Disconnected fd=%d revents=0x%X\n", pollfd->fd,
+				pollfd->revents);
+		poll_unregister_fd(pollfd->fd, receive_sock_connect_signal);
+	}
+}
+
+/*
+ * This function read from fd socket information about
+ * incoming connection and starts monitoring new connection
+ * on file descriptor read from fd.
+ */
+static void read_accepted(int fd)
+{
+	int ret;
+	struct msghdr msg;
+	struct iovec iv;
+	char cmsgbuf[CMSG_SPACE(1)];
+	struct cmsghdr *cmsgptr;
+	sock_connect_signal_t cs;
+	int accepted_fd = -1;
+	char addr_str[MAX_ADDR_STR_LEN];
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&iv, 0, sizeof(iv));
+	memset(cmsgbuf, 0, sizeof(cmsgbuf));
+
+	iv.iov_base = &cs;
+	iv.iov_len = sizeof(cs);
+
+	msg.msg_iov = &iv;
+	msg.msg_iovlen = 1;
+	msg.msg_control = cmsgbuf;
+	msg.msg_controllen = sizeof(cmsgbuf);
+
+	do {
+		ret = recvmsg(fd, &msg, MSG_NOSIGNAL);
+	} while (ret < 0 && errno == EINTR);
+
+	if (ret < 16 ||
+		(msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0)
+		haltest_error("Failed to accept connection\n");
+
+	for (cmsgptr = CMSG_FIRSTHDR(&msg);
+		cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
+		int count;
+
+		if (cmsgptr->cmsg_level != SOL_SOCKET ||
+			cmsgptr->cmsg_type != SCM_RIGHTS)
+			continue;
+
+		memcpy(&accepted_fd, CMSG_DATA(cmsgptr), sizeof(accepted_fd));
+		count = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int));
+
+		if (count != 1)
+			haltest_error("Failed to accept descriptors count=%d\n",
+									count);
+
+		break;
+	}
+
+	haltest_info("Incoming connection from %s channel %d status=%d fd=%d\n",
+					bt_bdaddr_t2str(&cs.bd_addr, addr_str),
+					cs.channel, cs.status, accepted_fd);
+	poll_register_fd(accepted_fd, POLLIN, receive_from_client);
+}
+
+/* handles incoming connections on socket */
+static void client_connected(struct pollfd *pollfd)
+{
+	haltest_info("client connected %x\n", pollfd->revents);
+
+	if (pollfd->revents & POLLHUP)
+		poll_unregister_fd(pollfd->fd, client_connected);
+	else if (pollfd->revents & POLLIN)
+		read_accepted(pollfd->fd);
+}
+
+/* listen */
+
+static void listen_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(btsock_type_t);
+		*enum_func = enum_defines;
+	} else if (argc == 5) {
+		*user = (void *) uuids;
+		*enum_func = enum_strings;
+	}
+}
+
+static void listen_p(int argc, const char **argv)
+{
+	btsock_type_t type;
+	const char *service_name;
+	bt_uuid_t service_uuid;
+	int channel;
+	int sock_fd = -1;
+	int flags;
+
+	RETURN_IF_NULL(if_sock);
+
+	/* Socket type */
+	if (argc < 3) {
+		haltest_error("No socket type specified\n");
+		return;
+	}
+	type = str2btsock_type_t(argv[2]);
+	if ((int) type == -1)
+		type = atoi(argv[2]);
+
+	/* service name */
+	if (argc < 4) {
+		haltest_error("No service name specified\n");
+		return;
+	}
+	service_name = argv[3];
+
+	/* uuid */
+	if (argc < 5) {
+		haltest_error("No uuid specified\n");
+		return;
+	}
+	str2bt_uuid_t(argv[4], &service_uuid);
+
+	/* channel */
+	channel = argc > 5 ? atoi(argv[5]) : 0;
+
+	/* flags */
+	flags = argc > 6 ? atoi(argv[6]) : 0;
+
+	if (listen_fd_count >= MAX_LISTEN_FD) {
+		haltest_error("Max (%d) listening sockets exceeded\n",
+							listen_fd_count);
+		return;
+	}
+	EXEC(if_sock->listen, type, service_name,
+				&service_uuid.uu[0], channel, &sock_fd, flags);
+	if (sock_fd > 0) {
+		int channel = 0;
+		int ret = read(sock_fd, &channel, 4);
+		if (ret != 4)
+			haltest_info("Read channel failed\n");
+		haltest_info("Channel returned from first read %d\n", channel);
+		listen_fd[listen_fd_count++] = sock_fd;
+		poll_register_fd(sock_fd, POLLIN, client_connected);
+	}
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*enum_func = enum_devices;
+	} else if (argc == 4) {
+		*user = TYPE_ENUM(btsock_type_t);
+		*enum_func = enum_defines;
+	} else if (argc == 5) {
+		*user = (void *) uuids;
+		*enum_func = enum_strings;
+	}
+}
+
+static void connect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	btsock_type_t type;
+	bt_uuid_t uuid;
+	int channel;
+	int sock_fd = -1;
+	int flags;
+
+	/* Address */
+	if (argc <= 2) {
+		haltest_error("No address specified\n");
+		return;
+	}
+	str2bt_bdaddr_t(argv[2], &addr);
+
+	/* Socket type */
+	if (argc <= 3) {
+		haltest_error("No socket type specified\n");
+		return;
+	}
+	type = str2btsock_type_t(argv[3]);
+	if ((int) type == -1)
+		type = atoi(argv[3]);
+
+	/* uuid */
+	if (argc <= 4) {
+		haltest_error("No uuid specified\n");
+		return;
+	}
+	str2bt_uuid_t(argv[4], &uuid);
+
+	/* channel */
+	if (argc <= 5) {
+		haltest_error("No channel specified\n");
+		return;
+	}
+	channel = atoi(argv[5]);
+
+	/* flags */
+	flags = argc <= 6 ? 0 : atoi(argv[6]);
+
+	RETURN_IF_NULL(if_sock);
+
+	EXEC(if_sock->connect, &addr, type, &uuid.uu[0], channel, &sock_fd,
+									flags);
+	if (sock_fd > 0) {
+		int channel = 0;
+		int ret = read(sock_fd, &channel, 4);
+
+		if (ret != 4)
+			haltest_info("Read channel failed\n");
+		haltest_info("Channel returned from first read %d\n", channel);
+		listen_fd[listen_fd_count++] = sock_fd;
+		poll_register_fd(sock_fd, POLLIN, receive_sock_connect_signal);
+	}
+}
+
+/* Methods available in btsock_interface_t */
+static struct method methods[] = {
+	STD_METHODCH(listen,
+			"<sock_type> <srvc_name> <uuid> [<channel>] [<flags>]"),
+	STD_METHODCH(connect,
+			"<addr> <sock_type> <uuid> <channel> [<flags>]"),
+	END_METHOD
+};
+
+const struct interface sock_if = {
+	.name = "socket",
+	.methods = methods
+};
diff --git a/repo/android/client/pollhandler.c b/repo/android/client/pollhandler.c
new file mode 100644
index 0000000..6160921
--- /dev/null
+++ b/repo/android/client/pollhandler.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <poll.h>
+
+#include "pollhandler.h"
+
+/*
+ * Code that allows to poll multiply file descriptors for events
+ * File descriptors can be added and removed at runtime
+ *
+ * Call poll_register_fd function first to add file descriptors to monitor
+ * Then call poll_dispatch_loop that will poll all registered file descriptors
+ * as long as they are not unregistered.
+ *
+ * When event happen on given fd appropriate user supplied handler is called
+ */
+
+/* Maximum number of files to monitor */
+#define MAX_OPEN_FD 10
+
+/* Storage for pollfd structures for monitored file descriptors */
+static struct pollfd fds[MAX_OPEN_FD];
+static poll_handler fds_handler[MAX_OPEN_FD];
+/* Number of registered file descriptors */
+static int fds_count = 0;
+
+/*
+ * Function polls file descriptor in loop and calls appropriate handler
+ * on event. Function returns when there is no more file descriptor to
+ * monitor
+ */
+void poll_dispatch_loop(void)
+{
+	while (fds_count > 0) {
+		int i;
+		int cur_fds_count = fds_count;
+		int ready = poll(fds, fds_count, 1000);
+
+		for (i = 0; i < fds_count && ready > 0; ++i) {
+			if (fds[i].revents == 0)
+				continue;
+
+			fds_handler[i](fds + i);
+			ready--;
+			/*
+			 * If handler was remove from table
+			 * just skip the rest and poll again
+			 * This is due to reordering of tables in
+			 * register/unregister functions
+			 */
+			if (cur_fds_count != fds_count)
+				break;
+		}
+	}
+}
+
+/*
+ * Registers file descriptor to be monitored for events (see man poll(2))
+ * for events.
+ *
+ * return non negative value on success
+ * -EMFILE when there are to much descriptors
+ */
+int poll_register_fd(int fd, short events, poll_handler ph)
+{
+	if (fds_count >= MAX_OPEN_FD)
+		return -EMFILE;
+
+	fds_handler[fds_count] = ph;
+	fds[fds_count].fd = fd;
+	fds[fds_count].events = events;
+	fds_count++;
+
+	return fds_count;
+}
+
+/*
+ * Unregisters file descriptor
+ * Both fd and ph must match previously registered data
+ *
+ * return 0 if unregister succeeded
+ * -EBADF if arguments do not match any register handler
+ */
+int poll_unregister_fd(int fd, poll_handler ph)
+{
+	int i;
+
+	for (i = 0; i < fds_count; ++i) {
+		if (fds_handler[i] == ph && fds[i].fd == fd) {
+			fds_count--;
+			if (i < fds_count) {
+				fds[i].fd = fds[fds_count].fd;
+				fds[i].events = fds[fds_count].events;
+				fds_handler[i] = fds_handler[fds_count];
+			}
+			return 0;
+		}
+	}
+	return -EBADF;
+}
diff --git a/repo/android/client/pollhandler.h b/repo/android/client/pollhandler.h
new file mode 100644
index 0000000..e2f22df
--- /dev/null
+++ b/repo/android/client/pollhandler.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <poll.h>
+
+/* Function to be called when there are event for some descriptor */
+typedef void (*poll_handler)(struct pollfd *pollfd);
+
+int poll_register_fd(int fd, short events, poll_handler ph);
+int poll_unregister_fd(int fd, poll_handler ph);
+
+void poll_dispatch_loop(void);
diff --git a/repo/android/client/tabcompletion.c b/repo/android/client/tabcompletion.c
new file mode 100644
index 0000000..bcca5fa
--- /dev/null
+++ b/repo/android/client/tabcompletion.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "if-main.h"
+#include "terminal.h"
+
+/* how many times tab was hit */
+static int tab_hit_count;
+
+typedef struct split_arg {
+	struct split_arg *next; /* next argument in buffer */
+	const char *origin; /* pointer to original argument */
+	char ntcopy[1]; /* null terminated copy of argument */
+} split_arg_t;
+
+/* function returns method of given name or NULL if not found */
+const struct method *get_interface_method(const char *iname,
+							const char *mname)
+{
+	const struct interface *iface = get_interface(iname);
+
+	if (iface == NULL)
+		return NULL;
+
+	return get_method(iface->methods, mname);
+}
+
+/* prints matching elements */
+static void print_matches(enum_func f, void *user, const char *prefix, int len)
+{
+	int i;
+	const char *enum_name;
+
+	putchar('\n');
+	for (i = 0; NULL != (enum_name = f(user, i)); ++i) {
+		if (strncmp(enum_name, prefix, len) == 0)
+			printf("%s\t", enum_name);
+	}
+	putchar('\n');
+	terminal_draw_command_line();
+}
+
+/*
+ * This function splits command line into linked list of arguments.
+ * line_buffer - pointer to input command line
+ * size - size of command line to parse
+ * buf - output buffer to keep split arguments list
+ * buf_size_in_bytes - size of buf
+ */
+static int split_command(const char *line_buffer, int size, split_arg_t *buf,
+							int buf_size_in_bytes)
+{
+	split_arg_t *prev = NULL;
+	split_arg_t *arg = buf;
+	int argc = 0;
+	const char *p = line_buffer;
+	const char *e = p + (size > 0 ? size : (int) strlen(p));
+	int len;
+
+	do {
+		while (p < e && isspace(*p))
+			p++;
+		arg->origin = p;
+		arg->next = NULL;
+		while (p < e && !isspace(*p))
+			p++;
+		len = p - arg->origin;
+		if (&arg->ntcopy[0] + len + 1 >
+			(const char *) buf + buf_size_in_bytes)
+			break;
+		strncpy(arg->ntcopy, arg->origin, len);
+		arg->ntcopy[len] = 0;
+		if (prev != NULL)
+			prev->next = arg;
+		prev = arg;
+		arg += (2 * sizeof(*arg) + len) / sizeof(*arg);
+		argc++;
+	} while (p < e);
+
+	return argc;
+}
+
+/* Function to enumerate method names */
+static const char *methods_name(void *v, int i)
+{
+	const struct interface *iface = v;
+
+	return iface->methods[i].name[0] ? iface->methods[i].name : NULL;
+}
+
+struct command_completion_args;
+typedef void (*short_help)(struct command_completion_args *args);
+
+struct command_completion_args {
+	const split_arg_t *arg; /* list of arguments */
+	const char *typed; /* last typed element */
+	enum_func func; /* enumerating function */
+	void *user; /* argument to enumerating function */
+	short_help help; /* help function */
+	const char *user_help; /* additional data (used by short_help) */
+};
+
+/* complete command line */
+static void tab_completion(struct command_completion_args *args)
+{
+	const char *name = args->typed;
+	const int len = strlen(name);
+	int i;
+	int j;
+	char prefix[128] = {0};
+	int prefix_len = 0;
+	int count = 0;
+	const char *enum_name;
+
+	for (i = 0; NULL != (enum_name = args->func(args->user, i)); ++i) {
+		/* prefix does not match */
+		if (strncmp(enum_name, name, len) != 0)
+			continue;
+
+		/* prefix matches first time */
+		if (count++ == 0) {
+			strcpy(prefix, enum_name);
+			prefix_len = strlen(prefix);
+			continue;
+		}
+
+		/* Prefix matches next time reduce prefix to common part */
+		for (j = 0; prefix[j] != 0
+			&& prefix[j] == enum_name[j];)
+			++j;
+		prefix_len = j;
+		prefix[j] = 0;
+	}
+
+	if (count == 0) {
+		/* no matches */
+		if (args->help != NULL)
+			args->help(args);
+		tab_hit_count = 0;
+		return;
+	}
+
+	/* len == prefix_len => nothing new was added */
+	if (len == prefix_len) {
+		if (count != 1) {
+			if (tab_hit_count == 1) {
+				putchar('\a');
+			} else if (tab_hit_count == 2 ||
+					args->help == NULL) {
+				print_matches(args->func,
+						args->user, name, len);
+			} else {
+				args->help(args);
+				tab_hit_count = 1;
+			}
+		} else if (count == 1) {
+			/* nothing to add, exact match add space */
+			terminal_insert_into_command_line(" ");
+		}
+	} else {
+		/* new chars can be added from some interface name(s) */
+		if (count == 1) {
+			/* exact match, add space */
+			prefix[prefix_len++] = ' ';
+			prefix[prefix_len] = '\0';
+		}
+
+		terminal_insert_into_command_line(prefix + len);
+		tab_hit_count = 0;
+	}
+}
+
+/* interface completion */
+static void command_completion(split_arg_t *arg)
+{
+	struct command_completion_args args = {
+		.arg = arg,
+		.typed = arg->ntcopy,
+		.func = command_name
+	};
+
+	tab_completion(&args);
+}
+
+/* method completion */
+static void method_completion(const struct interface *iface, split_arg_t *arg)
+{
+	struct command_completion_args args = {
+		.arg = arg,
+		.typed = arg->next->ntcopy,
+		.func = methods_name,
+		.user = (void *) iface
+	};
+
+	if (iface == NULL)
+		return;
+
+	tab_completion(&args);
+}
+
+static const char *bold = "\x1b[1m";
+static const char *normal = "\x1b[0m";
+
+static bool find_nth_argument(const char *str, int n, const char **s,
+								const char **e)
+{
+	const char *p = str;
+	int argc = 0;
+	*e = NULL;
+
+	while (p != NULL && *p != 0) {
+
+		while (isspace(*p))
+			++p;
+
+		if (n == argc)
+			*s = p;
+
+		if (*p == '[') {
+			p = strchr(p, ']');
+			if (p != NULL)
+				*e = ++p;
+		} else if (*p == '<') {
+			p = strchr(p, '>');
+			if (p != NULL)
+				*e = ++p;
+		} else {
+			*e = strchr(p, ' ');
+			if (*e == NULL)
+				*e = p + strlen(p);
+			p = *e;
+		}
+
+		if (n == argc)
+			break;
+
+		argc++;
+		*e = NULL;
+	}
+	return *e != NULL;
+}
+
+/* prints short help on method for interface */
+static void method_help(struct command_completion_args *args)
+{
+	int argc;
+	const split_arg_t *arg = args->arg;
+	const char *sb = NULL;
+	const char *eb = NULL;
+	const char *arg1 = "";
+	int arg1_size = 0; /* size of method field (for methods > 0) */
+
+	if (args->user_help == NULL)
+		return;
+
+	for (argc = 0; arg != NULL; argc++)
+		arg = arg->next;
+
+	/* Check if this is method from interface */
+	if (get_command(args->arg->ntcopy) == NULL) {
+		/* if so help is missing interface and method name */
+		arg1 = args->arg->next->ntcopy;
+		arg1_size = strlen(arg1) + 1;
+	}
+
+	find_nth_argument(args->user_help, argc - (arg1_size ? 3 : 2),
+								&sb, &eb);
+
+	if (eb != NULL)
+		haltest_info("%s %-*s%.*s%s%.*s%s%s\n", args->arg->ntcopy,
+				arg1_size, arg1, (int) (sb - args->user_help),
+				args->user_help, bold, (int) (eb - sb),
+				sb, normal, eb);
+	else
+		haltest_info("%s %-*s%s\n", args->arg->ntcopy,
+			arg1_size, arg1, args->user_help);
+}
+
+/* So we have empty enumeration */
+static const char *return_null(void *user, int i)
+{
+	return NULL;
+}
+
+/*
+ * parameter completion function
+ * argc - number of elements in arg list
+ * arg - list of arguments
+ * method - method to get completion from (can be NULL)
+ */
+static void param_completion(int argc, const split_arg_t *arg,
+					const struct method *method, int hlpix)
+{
+	int i;
+	const char *argv[argc];
+	const split_arg_t *tmp = arg;
+	struct command_completion_args args = {
+		.arg = arg,
+		.func = return_null
+	};
+
+	/* prepare standard argv from arg */
+	for (i = 0; i < argc; ++i) {
+		argv[i] = tmp->ntcopy;
+		tmp = tmp->next;
+	}
+
+	if (method != NULL && method->complete != NULL) {
+		/* ask method for completion function */
+		method->complete(argc, argv, &args.func, &args.user);
+	}
+
+	/* If method provided enumeration function call try to complete */
+	if (args.func != NULL) {
+		args.typed = argv[argc - 1];
+		args.help = method_help;
+		args.user_help = method ? method->help : NULL;
+
+		tab_completion(&args);
+	}
+}
+
+/*
+ * This method gets called when user tapped tab key.
+ * line - points to command line
+ * len - size of line that should be used for completions. This should be
+ *   cursor position during tab hit.
+ */
+void process_tab(const char *line, int len)
+{
+	int argc;
+	static split_arg_t buf[(LINE_BUF_MAX * 2) / sizeof(split_arg_t)];
+	const struct method *method;
+
+	argc = split_command(line, len, buf, sizeof(buf));
+	tab_hit_count++;
+
+	if (argc == 0)
+		return;
+
+	if (argc == 1) {
+		command_completion(buf);
+		return;
+	}
+
+	method = get_command(buf[0].ntcopy);
+	if (method != NULL) {
+		param_completion(argc, buf, method, 1);
+	} else if (argc == 2) {
+		method_completion(get_interface(buf[0].ntcopy), buf);
+	} else {
+		/* Find method for <interface, name> pair */
+		method = get_interface_method(buf[0].ntcopy,
+							buf[0].next->ntcopy);
+		param_completion(argc, buf, method, 2);
+	}
+}
diff --git a/repo/android/client/terminal.c b/repo/android/client/terminal.c
new file mode 100644
index 0000000..f7b56de
--- /dev/null
+++ b/repo/android/client/terminal.c
@@ -0,0 +1,824 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <termios.h>
+#include <stdlib.h>
+
+#include "terminal.h"
+#include "history.h"
+
+/*
+ * Character sequences recognized by code in this file
+ * Leading ESC 0x1B is not included
+ */
+#define SEQ_INSERT "[2~"
+#define SEQ_DELETE "[3~"
+#define SEQ_HOME   "OH"
+#define SEQ_END    "OF"
+#define SEQ_PGUP   "[5~"
+#define SEQ_PGDOWN "[6~"
+#define SEQ_LEFT   "[D"
+#define SEQ_RIGHT  "[C"
+#define SEQ_UP     "[A"
+#define SEQ_DOWN   "[B"
+#define SEQ_STAB   "[Z"
+#define SEQ_M_n    "n"
+#define SEQ_M_p    "p"
+#define SEQ_CLEFT  "[1;5D"
+#define SEQ_CRIGHT "[1;5C"
+#define SEQ_CUP    "[1;5A"
+#define SEQ_CDOWN  "[1;5B"
+#define SEQ_SLEFT  "[1;2D"
+#define SEQ_SRIGHT "[1;2C"
+#define SEQ_SUP    "[1;2A"
+#define SEQ_SDOWN  "[1;2B"
+#define SEQ_MLEFT  "[1;3D"
+#define SEQ_MRIGHT "[1;3C"
+#define SEQ_MUP    "[1;3A"
+#define SEQ_MDOWN  "[1;3B"
+
+#define KEY_SEQUENCE(k) { KEY_##k, SEQ_##k }
+struct ansii_sequence {
+	int code;
+	const char *sequence;
+};
+
+/* Table connects single int key codes with character sequences */
+static const struct ansii_sequence ansii_sequnces[] = {
+	KEY_SEQUENCE(INSERT),
+	KEY_SEQUENCE(DELETE),
+	KEY_SEQUENCE(HOME),
+	KEY_SEQUENCE(END),
+	KEY_SEQUENCE(PGUP),
+	KEY_SEQUENCE(PGDOWN),
+	KEY_SEQUENCE(LEFT),
+	KEY_SEQUENCE(RIGHT),
+	KEY_SEQUENCE(UP),
+	KEY_SEQUENCE(DOWN),
+	KEY_SEQUENCE(CLEFT),
+	KEY_SEQUENCE(CRIGHT),
+	KEY_SEQUENCE(CUP),
+	KEY_SEQUENCE(CDOWN),
+	KEY_SEQUENCE(SLEFT),
+	KEY_SEQUENCE(SRIGHT),
+	KEY_SEQUENCE(SUP),
+	KEY_SEQUENCE(SDOWN),
+	KEY_SEQUENCE(MLEFT),
+	KEY_SEQUENCE(MRIGHT),
+	KEY_SEQUENCE(MUP),
+	KEY_SEQUENCE(MDOWN),
+	KEY_SEQUENCE(STAB),
+	KEY_SEQUENCE(M_p),
+	KEY_SEQUENCE(M_n),
+	{ 0, NULL }
+};
+
+#define KEY_SEQUNCE_NOT_FINISHED -1
+#define KEY_C_C 3
+#define KEY_C_D 4
+#define KEY_C_L 12
+
+#define isseqence(c) ((c) == 0x1B)
+
+/*
+ * Number of characters that consist of ANSI sequence
+ * Should not be less then longest string in ansi_sequences
+ */
+#define MAX_ASCII_SEQUENCE 10
+
+static char current_sequence[MAX_ASCII_SEQUENCE];
+static int current_sequence_len = -1;
+
+/* single line typed by user goes here */
+static char line_buf[LINE_BUF_MAX];
+/* index of cursor in input line */
+static int line_buf_ix = 0;
+/* current length of input line */
+static int line_len = 0;
+
+/* line index used for fetching lines from history */
+static int line_index = 0;
+
+static char prompt_buf[10] = "> ";
+static const char *const noprompt = "";
+static const char *current_prompt = prompt_buf;
+static const char *prompt = prompt_buf;
+/*
+ * Moves cursor to right or left
+ *
+ * n - positive - moves cursor right
+ * n - negative - moves cursor left
+ */
+static void terminal_move_cursor(int n)
+{
+	if (n < 0) {
+		for (; n < 0; n++)
+			putchar('\b');
+	} else if (n > 0) {
+		printf("%*s", n, line_buf + line_buf_ix);
+	}
+}
+
+/* Draw command line */
+void terminal_draw_command_line(void)
+{
+	/*
+	 * this needs to be checked here since line_buf is not cleared
+	 * before parsing event though line_len and line_buf_ix are
+	 */
+	if (line_len > 0)
+		printf("%s%s", prompt, line_buf);
+	else
+		printf("%s", prompt);
+
+	/* move cursor to it's place */
+	terminal_move_cursor(line_buf_ix - line_len);
+}
+
+/* inserts string into command line at cursor position */
+void terminal_insert_into_command_line(const char *p)
+{
+	int len = strlen(p);
+
+	if (line_len == line_buf_ix) {
+		strcat(line_buf, p);
+		printf("%s", p);
+		line_len = line_len + len;
+		line_buf_ix = line_len;
+	} else {
+		memmove(line_buf + line_buf_ix + len,
+			line_buf + line_buf_ix, line_len - line_buf_ix + 1);
+		memmove(line_buf + line_buf_ix, p, len);
+		printf("%s", line_buf + line_buf_ix);
+		line_buf_ix += len;
+		line_len += len;
+		terminal_move_cursor(line_buf_ix - line_len);
+	}
+}
+
+/* Prints string and redraws command line */
+int terminal_print(const char *format, ...)
+{
+	va_list args;
+	int ret;
+
+	va_start(args, format);
+
+	ret = terminal_vprint(format, args);
+
+	va_end(args);
+	return ret;
+}
+
+/* Prints string and redraws command line */
+int terminal_vprint(const char *format, va_list args)
+{
+	int ret;
+
+	printf("\r%*s\r", (int) line_len + 1, " ");
+
+	ret = vprintf(format, args);
+
+	terminal_draw_command_line();
+
+	fflush(stdout);
+
+	return ret;
+}
+
+/*
+ * Call this when text in line_buf was changed
+ * and line needs to be redrawn
+ */
+static void terminal_line_replaced(void)
+{
+	int len = strlen(line_buf);
+
+	/* line is shorter that previous */
+	if (len < line_len) {
+		/* if new line is shorter move cursor to end of new end */
+		while (line_buf_ix > len) {
+			putchar('\b');
+			line_buf_ix--;
+		}
+
+		/* If cursor was not at the end, move it to the end */
+		if (line_buf_ix < line_len)
+			printf("%.*s", line_len - line_buf_ix,
+					line_buf + line_buf_ix);
+		/* over write end of previous line */
+		while (line_len >= len++)
+			putchar(' ');
+	}
+
+	/* draw new line */
+	printf("\r%s%s", prompt, line_buf);
+	/* set up indexes to new line */
+	line_len = strlen(line_buf);
+	line_buf_ix = line_len;
+	fflush(stdout);
+}
+
+static void terminal_clear_line(void)
+{
+	line_buf[0] = '\0';
+	terminal_line_replaced();
+}
+
+static void terminal_clear_screen(void)
+{
+	line_buf[0] = '\0';
+	line_buf_ix = 0;
+	line_len = 0;
+
+	printf("\x1b[2J\x1b[1;1H%s", prompt);
+}
+
+static void terminal_delete_char(void)
+{
+	/* delete character under cursor if not at the very end */
+	if (line_buf_ix >= line_len)
+		return;
+	/*
+	 * Prepare buffer with one character missing
+	 * trailing 0 is moved
+	 */
+	line_len--;
+	memmove(line_buf + line_buf_ix, line_buf + line_buf_ix + 1,
+						line_len - line_buf_ix + 1);
+	/* print rest of line from current cursor position */
+	printf("%s \b", line_buf + line_buf_ix);
+	/* move back cursor */
+	terminal_move_cursor(line_buf_ix - line_len);
+}
+
+/*
+ * Function tries to replace current line with specified line in history
+ * new_line_index - new line to show, -1 to show oldest
+ */
+static void terminal_get_line_from_history(int new_line_index)
+{
+	new_line_index = history_get_line(new_line_index,
+						line_buf, LINE_BUF_MAX);
+
+	if (new_line_index >= 0) {
+		terminal_line_replaced();
+		line_index = new_line_index;
+	}
+}
+
+/*
+ * Function searches history back or forward for command line that starts
+ * with characters up to cursor position
+ *
+ * back - true - searches backward
+ * back - false - searches forward (more recent commands)
+ */
+static void terminal_match_hitory(bool back)
+{
+	char buf[line_buf_ix + 1];
+	int line;
+	int matching_line = -1;
+	int dir = back ? 1 : -1;
+
+	line = line_index + dir;
+	while (matching_line == -1 && line >= 0) {
+		int new_line_index;
+
+		new_line_index = history_get_line(line, buf, line_buf_ix + 1);
+		if (new_line_index < 0)
+			break;
+
+		if (0 == strncmp(line_buf, buf, line_buf_ix))
+			matching_line = line;
+		line += dir;
+	}
+
+	if (matching_line >= 0) {
+		int pos = line_buf_ix;
+		terminal_get_line_from_history(matching_line);
+		/* move back to cursor position to original place */
+		line_buf_ix = pos;
+		terminal_move_cursor(pos - line_len);
+	}
+}
+
+/*
+ * Converts terminal character sequences to single value representing
+ * keyboard keys
+ */
+static int terminal_convert_sequence(int c)
+{
+	int i;
+
+	/* Not in sequence yet? */
+	if (current_sequence_len == -1) {
+		/* Is ansi sequence detected by 0x1B ? */
+		if (isseqence(c)) {
+			current_sequence_len++;
+			return KEY_SEQUNCE_NOT_FINISHED;
+		}
+
+		return c;
+	}
+
+	/* Inside sequence */
+	current_sequence[current_sequence_len++] = c;
+	current_sequence[current_sequence_len] = '\0';
+	for (i = 0; ansii_sequnces[i].code; ++i) {
+		/* Matches so far? */
+		if (0 != strncmp(current_sequence, ansii_sequnces[i].sequence,
+							current_sequence_len))
+			continue;
+
+		/* Matches as a whole? */
+		if (ansii_sequnces[i].sequence[current_sequence_len] == 0) {
+			current_sequence_len = -1;
+			return ansii_sequnces[i].code;
+		}
+
+		/* partial match (not whole sequence yet) */
+		return KEY_SEQUNCE_NOT_FINISHED;
+	}
+
+	terminal_print("ansi char 0x%X %c\n", c);
+	/*
+	 * Sequence does not match
+	 * mark that no in sequence any more, return char
+	 */
+	current_sequence_len = -1;
+	return c;
+}
+
+typedef void (*terminal_action)(int c, line_callback process_line);
+
+#define TERMINAL_ACTION(n) \
+	static void n(int c, void (*process_line)(char *line))
+
+TERMINAL_ACTION(terminal_action_null)
+{
+}
+
+/* Mapping between keys and function */
+typedef struct {
+	int key;
+	terminal_action func;
+} KeyAction;
+
+int action_keys[] = {
+	KEY_SEQUNCE_NOT_FINISHED,
+	KEY_LEFT,
+	KEY_RIGHT,
+	KEY_HOME,
+	KEY_END,
+	KEY_DELETE,
+	KEY_CLEFT,
+	KEY_CRIGHT,
+	KEY_SUP,
+	KEY_SDOWN,
+	KEY_UP,
+	KEY_DOWN,
+	KEY_BACKSPACE,
+	KEY_INSERT,
+	KEY_PGUP,
+	KEY_PGDOWN,
+	KEY_CUP,
+	KEY_CDOWN,
+	KEY_SLEFT,
+	KEY_SRIGHT,
+	KEY_MLEFT,
+	KEY_MRIGHT,
+	KEY_MUP,
+	KEY_MDOWN,
+	KEY_STAB,
+	KEY_M_n,
+	KEY_M_p,
+	KEY_C_C,
+	KEY_C_D,
+	KEY_C_L,
+	'\t',
+	'\r',
+	'\n',
+};
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+/*
+ * current_actions holds all recognizable kes and actions for them
+ * additional element (index 0) is used for default action
+ */
+static KeyAction current_actions[NELEM(action_keys) + 1];
+
+/* KeyAction comparator by key, for qsort and bsearch */
+static int KeyActionKeyCompare(const void *a, const void *b)
+{
+	return ((const KeyAction *) a)->key - ((const KeyAction *) b)->key;
+}
+
+/* Find action by key, NULL if no action for this key */
+static KeyAction *terminal_get_action(int key)
+{
+	KeyAction a = { .key = key };
+
+	return bsearch(&a, current_actions + 1, NELEM(action_keys), sizeof(a),
+							KeyActionKeyCompare);
+}
+
+/* Sets new set of actions to use */
+static void terminal_set_actions(const KeyAction *actions)
+{
+	int i;
+
+	/* Make map with empty function for every key */
+	for (i = 0; i < NELEM(action_keys); ++i) {
+		/*
+		 * + 1 due to 0 index reserved for default action that is
+		 * called for non mapped key
+		 */
+		current_actions[i + 1].key = action_keys[i];
+		current_actions[i + 1].func = terminal_action_null;
+	}
+
+	/* Sort action from 1 (index 0 - default action) */
+	qsort(current_actions + 1, NELEM(action_keys), sizeof(KeyAction),
+							KeyActionKeyCompare);
+	/* Set default action (first in array) */
+	current_actions[0] = *actions++;
+
+	/* Copy rest of actions into their places */
+	for (; actions->key; ++actions) {
+		KeyAction *place = terminal_get_action(actions->key);
+
+		if (place)
+			place->func = actions->func;
+	}
+}
+
+TERMINAL_ACTION(terminal_action_left)
+{
+	/* if not at the beginning move to previous character */
+	if (line_buf_ix <= 0)
+		return;
+	line_buf_ix--;
+	terminal_move_cursor(-1);
+}
+
+TERMINAL_ACTION(terminal_action_right)
+{
+	/*
+	 * If not at the end, just print current character
+	 * and modify position
+	 */
+	if (line_buf_ix < line_len)
+		putchar(line_buf[line_buf_ix++]);
+}
+
+TERMINAL_ACTION(terminal_action_home)
+{
+	/* move to beginning of line and update position */
+	printf("\r%s", prompt);
+	line_buf_ix = 0;
+}
+
+TERMINAL_ACTION(terminal_action_end)
+{
+	/* if not at the end of line */
+	if (line_buf_ix < line_len) {
+		/* print everything from cursor */
+		printf("%s", line_buf + line_buf_ix);
+		/* just modify current position */
+		line_buf_ix = line_len;
+	}
+}
+
+TERMINAL_ACTION(terminal_action_del)
+{
+	terminal_delete_char();
+}
+
+TERMINAL_ACTION(terminal_action_word_left)
+{
+	int old_pos;
+	/*
+	 * Move by word left
+	 *
+	 * Are we at the beginning of line?
+	 */
+	if (line_buf_ix <= 0)
+		return;
+
+	old_pos = line_buf_ix;
+	line_buf_ix--;
+	/* skip spaces left */
+	while (line_buf_ix && isspace(line_buf[line_buf_ix]))
+		line_buf_ix--;
+
+	/* skip all non spaces to the left */
+	while (line_buf_ix > 0 &&
+			!isspace(line_buf[line_buf_ix - 1]))
+		line_buf_ix--;
+
+	/* move cursor to new position */
+	terminal_move_cursor(line_buf_ix - old_pos);
+}
+
+TERMINAL_ACTION(terminal_action_word_right)
+{
+	int old_pos;
+	/*
+	 * Move by word right
+	 *
+	 * are we at the end of line?
+	 */
+	if (line_buf_ix >= line_len)
+		return;
+
+	old_pos = line_buf_ix;
+	/* skip all spaces */
+	while (line_buf_ix < line_len && isspace(line_buf[line_buf_ix]))
+		line_buf_ix++;
+
+	/* skip all non spaces */
+	while (line_buf_ix < line_len && !isspace(line_buf[line_buf_ix]))
+		line_buf_ix++;
+	/*
+	 * Move cursor to right by printing text
+	 * between old cursor and new
+	 */
+	if (line_buf_ix > old_pos)
+		printf("%.*s", (int) (line_buf_ix - old_pos),
+							line_buf + old_pos);
+}
+
+TERMINAL_ACTION(terminal_action_history_begin)
+{
+	terminal_get_line_from_history(-1);
+}
+
+TERMINAL_ACTION(terminal_action_history_end)
+{
+	if (line_index > 0)
+		terminal_get_line_from_history(0);
+}
+
+TERMINAL_ACTION(terminal_action_history_up)
+{
+	terminal_get_line_from_history(line_index + 1);
+}
+
+TERMINAL_ACTION(terminal_action_history_down)
+{
+	if (line_index > 0)
+		terminal_get_line_from_history(line_index - 1);
+}
+
+TERMINAL_ACTION(terminal_action_tab)
+{
+	/* tab processing */
+	process_tab(line_buf, line_buf_ix);
+}
+
+
+TERMINAL_ACTION(terminal_action_backspace)
+{
+	if (line_buf_ix <= 0)
+		return;
+
+	if (line_buf_ix == line_len) {
+		printf("\b \b");
+		line_len = --line_buf_ix;
+		line_buf[line_len] = 0;
+	} else {
+		putchar('\b');
+		line_buf_ix--;
+		line_len--;
+		memmove(line_buf + line_buf_ix,
+				line_buf + line_buf_ix + 1,
+				line_len - line_buf_ix + 1);
+		printf("%s \b", line_buf + line_buf_ix);
+		terminal_move_cursor(line_buf_ix - line_len);
+	}
+}
+
+TERMINAL_ACTION(terminal_action_find_history_forward)
+{
+	/* Search history forward */
+	terminal_match_hitory(false);
+}
+
+TERMINAL_ACTION(terminal_action_find_history_backward)
+{
+	/* Search history forward */
+	terminal_match_hitory(true);
+}
+
+TERMINAL_ACTION(terminal_action_ctrl_c)
+{
+	terminal_clear_line();
+}
+
+TERMINAL_ACTION(terminal_action_ctrl_d)
+{
+	if (line_len > 0) {
+		terminal_delete_char();
+	} else  {
+		puts("");
+		exit(0);
+	}
+}
+
+TERMINAL_ACTION(terminal_action_clear_screen)
+{
+	terminal_clear_screen();
+}
+
+TERMINAL_ACTION(terminal_action_enter)
+{
+	/*
+	 * On new line add line to history
+	 * forget history position
+	 */
+	history_add_line(line_buf);
+	line_len = 0;
+	line_buf_ix = 0;
+	line_index = -1;
+	/* print new line */
+	putchar(c);
+	prompt = noprompt;
+	process_line(line_buf);
+	/* clear current line */
+	line_buf[0] = '\0';
+	prompt = current_prompt;
+	printf("%s", prompt);
+}
+
+TERMINAL_ACTION(terminal_action_default)
+{
+	char str[2] = { c, 0 };
+
+	if (!isprint(c))
+		/*
+		 * TODO: remove this print once all meaningful sequences
+		 * are identified
+		 */
+		printf("char-0x%02x\n", c);
+	else if (line_buf_ix < LINE_BUF_MAX - 1)
+		terminal_insert_into_command_line(str);
+}
+
+/* Callback to call when user hit enter during prompt for */
+static line_callback prompt_callback;
+
+static KeyAction normal_actions[] = {
+	{ 0, terminal_action_default },
+	{ KEY_LEFT, terminal_action_left },
+	{ KEY_RIGHT, terminal_action_right },
+	{ KEY_HOME, terminal_action_home },
+	{ KEY_END, terminal_action_end },
+	{ KEY_DELETE, terminal_action_del },
+	{ KEY_CLEFT, terminal_action_word_left },
+	{ KEY_CRIGHT, terminal_action_word_right },
+	{ KEY_SUP, terminal_action_history_begin },
+	{ KEY_SDOWN, terminal_action_history_end },
+	{ KEY_UP, terminal_action_history_up },
+	{ KEY_DOWN, terminal_action_history_down },
+	{ '\t', terminal_action_tab },
+	{ KEY_BACKSPACE, terminal_action_backspace },
+	{ KEY_M_n, terminal_action_find_history_forward },
+	{ KEY_M_p, terminal_action_find_history_backward },
+	{ KEY_C_C, terminal_action_ctrl_c },
+	{ KEY_C_D, terminal_action_ctrl_d },
+	{ KEY_C_L, terminal_action_clear_screen },
+	{ '\r', terminal_action_enter },
+	{ '\n', terminal_action_enter },
+	{ 0, NULL },
+};
+
+TERMINAL_ACTION(terminal_action_answer)
+{
+	putchar(c);
+
+	terminal_set_actions(normal_actions);
+	/* Restore default prompt */
+	current_prompt = prompt_buf;
+
+	/* No prompt for prints */
+	prompt = noprompt;
+	line_buf_ix = 0;
+	line_len = 0;
+	/* Call user function with what was typed */
+	prompt_callback(line_buf);
+
+	line_buf[0] = 0;
+	/* promot_callback could change current_prompt */
+	prompt = current_prompt;
+
+	printf("%s", prompt);
+}
+
+TERMINAL_ACTION(terminal_action_prompt_ctrl_c)
+{
+	printf("^C\n");
+	line_buf_ix = 0;
+	line_len = 0;
+	line_buf[0] = 0;
+
+	current_prompt = prompt_buf;
+	prompt = current_prompt;
+	terminal_set_actions(normal_actions);
+
+	printf("%s", prompt);
+}
+
+static KeyAction prompt_actions[] = {
+	{ 0, terminal_action_default },
+	{ KEY_LEFT, terminal_action_left },
+	{ KEY_RIGHT, terminal_action_right },
+	{ KEY_HOME, terminal_action_home },
+	{ KEY_END, terminal_action_end },
+	{ KEY_DELETE, terminal_action_del },
+	{ KEY_CLEFT, terminal_action_word_left },
+	{ KEY_CRIGHT, terminal_action_word_right },
+	{ KEY_BACKSPACE, terminal_action_backspace },
+	{ KEY_C_C, terminal_action_prompt_ctrl_c },
+	{ KEY_C_D, terminal_action_ctrl_d },
+	{ '\r', terminal_action_answer },
+	{ '\n', terminal_action_answer },
+	{ 0, NULL },
+};
+
+void terminal_process_char(int c, line_callback process_line)
+{
+	KeyAction *a;
+
+	c = terminal_convert_sequence(c);
+
+	/* Get action for this key */
+	a = terminal_get_action(c);
+
+	/* No action found, get default one */
+	if (a == NULL)
+		a = &current_actions[0];
+
+	a->func(c, process_line);
+	fflush(stdout);
+}
+
+void terminal_prompt_for(const char *s, line_callback process_line)
+{
+	current_prompt = s;
+	if (prompt != noprompt) {
+		prompt = s;
+		terminal_clear_line();
+	}
+	prompt_callback = process_line;
+	terminal_set_actions(prompt_actions);
+}
+
+static struct termios origianl_tios;
+
+static void terminal_cleanup(void)
+{
+	tcsetattr(0, TCSANOW, &origianl_tios);
+}
+
+void terminal_setup(void)
+{
+	struct termios tios;
+
+	terminal_set_actions(normal_actions);
+
+	tcgetattr(0, &origianl_tios);
+	tios = origianl_tios;
+
+	/*
+	 * Turn off echo since all editing is done by hand,
+	 * Ctrl-c handled internally
+	 */
+	tios.c_lflag &= ~(ICANON | ECHO | BRKINT | IGNBRK);
+	tcsetattr(0, TCSANOW, &tios);
+
+	/* Restore terminal at exit */
+	atexit(terminal_cleanup);
+
+	printf("%s", prompt);
+	fflush(stdout);
+}
diff --git a/repo/android/client/terminal.h b/repo/android/client/terminal.h
new file mode 100644
index 0000000..0e63936
--- /dev/null
+++ b/repo/android/client/terminal.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdarg.h>
+
+/* size of supported line */
+#define LINE_BUF_MAX 1024
+
+enum key_codes {
+	KEY_BACKSPACE = 0x7F,
+	KEY_INSERT = 1000, /* arbitrary value */
+	KEY_DELETE,
+	KEY_HOME,
+	KEY_END,
+	KEY_PGUP,
+	KEY_PGDOWN,
+	KEY_LEFT,
+	KEY_RIGHT,
+	KEY_UP,
+	KEY_DOWN,
+	KEY_CLEFT,
+	KEY_CRIGHT,
+	KEY_CUP,
+	KEY_CDOWN,
+	KEY_SLEFT,
+	KEY_SRIGHT,
+	KEY_SUP,
+	KEY_SDOWN,
+	KEY_MLEFT,
+	KEY_MRIGHT,
+	KEY_MUP,
+	KEY_MDOWN,
+	KEY_STAB,
+	KEY_M_p,
+	KEY_M_n
+};
+
+typedef void (*line_callback)(char *);
+
+void terminal_setup(void);
+int terminal_print(const char *format, ...);
+int terminal_vprint(const char *format, va_list args);
+void terminal_process_char(int c, line_callback process_line);
+void terminal_insert_into_command_line(const char *p);
+void terminal_draw_command_line(void);
+void terminal_prompt_for(const char *s, line_callback process_line);
+
+void process_tab(const char *line, int len);
diff --git a/repo/android/compat/readline/history.h b/repo/android/compat/readline/history.h
new file mode 100644
index 0000000..decc2f4
--- /dev/null
+++ b/repo/android/compat/readline/history.h
@@ -0,0 +1,31 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 1987-2011 Free Software Foundation, 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
+ *
+ */
+
+#ifndef _HISTORY_H_
+#define _HISTORY_H_
+
+static inline void add_history(const char *c)
+{
+}
+
+#endif
diff --git a/repo/android/compat/readline/readline.h b/repo/android/compat/readline/readline.h
new file mode 100644
index 0000000..aaf6f31
--- /dev/null
+++ b/repo/android/compat/readline/readline.h
@@ -0,0 +1,110 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 1987-2011 Free Software Foundation, 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
+ *
+ */
+
+#ifndef _READLINE_H_
+#define _READLINE_H_
+
+typedef void (*rl_vcpfunc_t)(char *c);
+typedef void (*rl_compdisp_func_t)(char **c, int i, int j);
+typedef char *(*rl_compentry_func_t)(const char *c, int i);
+typedef char **(*rl_completion_func_t)(const char *c, int i, int j);
+
+#define RL_STATE_DONE 0x1000000
+#define RL_ISSTATE(x) (rl_readline_state & (x))
+
+static int rl_end;
+static int rl_point;
+static int rl_readline_state;
+static int rl_erase_empty_line;
+static int rl_attempted_completion_over;
+static char *rl_prompt;
+static char *rl_line_buffer;
+static rl_compdisp_func_t rl_completion_display_matches_hook;
+static rl_completion_func_t rl_attempted_completion_function;
+
+static inline void rl_callback_handler_install(const char *c, rl_vcpfunc_t f)
+{
+	printf("readline not available\n");
+	exit(1);
+}
+
+static inline int rl_set_prompt(const char *c)
+{
+	return -1;
+}
+
+static inline void rl_replace_line(const char *c, int i)
+{
+}
+
+static inline void rl_redisplay(void)
+{
+}
+
+static inline char **rl_completion_matches(const char *c, rl_compentry_func_t f)
+{
+	return NULL;
+}
+
+static inline int rl_insert_text(const char *c)
+{
+	return -1;
+}
+
+static inline int rl_crlf(void)
+{
+	return -1;
+}
+
+static inline void rl_callback_read_char(void)
+{
+}
+
+static inline int rl_message(const char *c, ...)
+{
+	return -1;
+}
+
+static inline void rl_callback_handler_remove(void)
+{
+}
+
+static inline char *rl_copy_text(int i, int j)
+{
+	return NULL;
+}
+
+static inline void rl_save_prompt(void)
+{
+}
+
+static inline void rl_restore_prompt(void)
+{
+}
+
+static inline int rl_forced_update_display(void)
+{
+	return -1;
+}
+
+#endif
diff --git a/repo/android/compat/wordexp.h b/repo/android/compat/wordexp.h
new file mode 100644
index 0000000..ff1f21c
--- /dev/null
+++ b/repo/android/compat/wordexp.h
@@ -0,0 +1,44 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 1991-2013 Free Software Foundation, Inc.
+ *
+ *
+ *  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
+ *
+ */
+
+#ifndef _WORDEXP_H_
+#define _WORDEXP_H_
+
+#define WRDE_NOCMD 0
+
+typedef struct {
+	size_t we_wordc;
+	char **we_wordv;
+	size_t we_offs;
+} wordexp_t;
+
+static inline int wordexp(const char *c, wordexp_t *w, int _i)
+{
+	return -1;
+}
+
+static inline void wordfree(wordexp_t *__wordexp)
+{
+}
+
+#endif
diff --git a/repo/android/cts.txt b/repo/android/cts.txt
new file mode 100644
index 0000000..f679263
--- /dev/null
+++ b/repo/android/cts.txt
@@ -0,0 +1,58 @@
+Android Compatibility Test Suite results
+
+Tested: 27-Nov-2014
+Android version: 5.0
+Kernel Version: 3.18
+CTS version: 5.0 R1 (android-5.0.0_r7)
+
+Note:
+CTS reliable write GATT tests require using CTS from master branch
+or https://android-review.googlesource.com/99354 applied.
+
+(*) Tests are disabled due to CTS quality issues. Link for reference:
+https://android.googlesource.com/platform/cts/+/0a62e4a0a9910101ccf2ccc43f6%5E!/
+
+-------------------------------------------------------------------------------
+android.bluetooth.cts.BasicAdapterTest (automated tests)
+Test Name				Result	Notes
+-------------------------------------------------------------------------------
+testAndroidTestCaseSetupProperly	PASS
+test_checkBluetoothAddress		PASS
+test_enableDisable			PASS
+test_getAddress				PASS
+test_getBondedDevices			PASS
+test_getDefaultAdapter			PASS
+test_getName				PASS
+test_getRemoteDevice			PASS
+test_listenUsingRfcommWithServiceRecord	PASS
+-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
+com.android.cts.verifier (manual tests)
+Test Name				Result	Notes
+-------------------------------------------------------------------------------
+Toggle Bluetooth			PASS
+BLE Client Test:
+	connect				N/A	(*)
+	discover service		N/A	(*)
+	read/write characteristic	N/A	(*)
+	reliable write			N/A	(*)
+	notify characteristic		N/A	(*)
+	read/write descriptor		N/A	(*)
+	read RSSI			N/A	(*)
+	disconnect			N/A	(*)
+BLE Server Test:
+	add service			N/A	(*)
+	connection			N/A	(*)
+	read characteristic request	N/A	(*)
+	write characteristic request	N/A	(*)
+	read descriptor request		N/A	(*)
+	write descriptor request	N/A	(*)
+	reliable write			N/A	(*)
+	disconnection			N/A	(*)
+Insecure Client				PASS
+Insecure Server				PASS
+Secure Client				PASS
+Secure Server				PASS
+-------------------------------------------------------------------------------
diff --git a/repo/android/cutils/properties.h b/repo/android/cutils/properties.h
new file mode 100644
index 0000000..0163eb5
--- /dev/null
+++ b/repo/android/cutils/properties.h
@@ -0,0 +1,95 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013  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
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define PROPERTY_VALUE_MAX 32
+#define PROPERTY_KEY_MAX 32
+
+#define BLUETOOTH_MODE_PROPERTY_NAME "persist.sys.bluetooth.mode"
+#define BLUETOOTH_MODE_PROPERTY_HANDSFREE "persist.sys.bluetooth.handsfree"
+
+static inline int property_get(const char *key, char *value,
+						const char *default_value)
+{
+	const char *prop = NULL;
+
+	if (!strcmp(key, BLUETOOTH_MODE_PROPERTY_NAME))
+		prop = getenv("BLUETOOTH_MODE");
+
+	if (!strcmp(key, BLUETOOTH_MODE_PROPERTY_HANDSFREE))
+		prop = getenv("BLUETOOTH_HANDSFREE_MODE");
+
+	if (!prop)
+		prop = default_value;
+
+	if (prop) {
+		strncpy(value, prop, PROPERTY_VALUE_MAX);
+
+		value[PROPERTY_VALUE_MAX - 1] = '\0';
+
+		return strlen(value);
+	}
+
+	return 0;
+}
+
+/* property_set: returns 0 on success, < 0 on failure
+*/
+static inline int property_set(const char *key, const char *value)
+{
+	static const char SYSTEM_SOCKET_PATH[] = "\0android_system";
+
+	struct sockaddr_un addr;
+	char msg[256];
+	int fd, len;
+
+	fd = socket(PF_LOCAL, SOCK_DGRAM, 0);
+	if (fd < 0)
+		return -1;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH));
+
+	if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		close(fd);
+		return 0;
+	}
+
+	len = snprintf(msg, sizeof(msg), "%s=%s", key, value);
+
+	if (send(fd, msg, len + 1, 0) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+
+	return 0;
+}
diff --git a/repo/android/gatt.c b/repo/android/gatt.c
new file mode 100644
index 0000000..28635ed
--- /dev/null
+++ b/repo/android/gatt.c
@@ -0,0 +1,7193 @@
+/*
+ *
+ *  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <glib.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "ipc.h"
+#include "ipc-common.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+#include "bluetooth.h"
+#include "gatt.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "utils.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "btio/btio.h"
+
+/* set according to Android bt_gatt_client.h */
+#define GATT_MAX_ATTR_LEN 600
+
+#define GATT_SUCCESS	0x00000000
+#define GATT_FAILURE	0x00000101
+
+#define BASE_UUID16_OFFSET     12
+
+#define GATT_PERM_READ			0x00000001
+#define GATT_PERM_READ_ENCRYPTED	0x00000002
+#define GATT_PERM_READ_MITM		0x00000004
+#define GATT_PERM_READ_AUTHORIZATION	0x00000008
+#define GATT_PERM_WRITE			0x00000100
+#define GATT_PERM_WRITE_ENCRYPTED	0x00000200
+#define GATT_PERM_WRITE_MITM		0x00000400
+#define GATT_PERM_WRITE_AUTHORIZATION	0x00000800
+#define GATT_PERM_WRITE_SIGNED		0x00010000
+#define GATT_PERM_WRITE_SIGNED_MITM	0x00020000
+#define GATT_PERM_NONE			0x10000000
+
+#define GATT_PAIR_CONN_TIMEOUT 30
+#define GATT_CONN_TIMEOUT 2
+
+static const uint8_t BLUETOOTH_UUID[] = {
+	0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+	0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+typedef enum {
+	DEVICE_DISCONNECTED = 0,
+	DEVICE_CONNECT_INIT,		/* connection procedure initiated */
+	DEVICE_CONNECT_READY,		/* dev found during LE scan */
+	DEVICE_CONNECTED,		/* connection has been established */
+} gatt_device_state_t;
+
+static const char *device_state_str[] = {
+	"DISCONNECTED",
+	"CONNECT INIT",
+	"CONNECT READY",
+	"CONNECTED",
+};
+
+struct pending_trans_data {
+	unsigned int id;
+	uint8_t opcode;
+	struct gatt_db_attribute *attrib;
+	unsigned int serial_id;
+};
+
+struct gatt_app {
+	int32_t id;
+	uint8_t uuid[16];
+
+	gatt_type_t type;
+
+	/* Valid for client applications */
+	struct queue *notifications;
+
+	gatt_conn_cb_t func;
+};
+
+struct element_id {
+	bt_uuid_t uuid;
+	uint8_t instance;
+};
+
+struct descriptor {
+	struct element_id id;
+	uint16_t handle;
+};
+
+struct characteristic {
+	struct element_id id;
+	struct gatt_char ch;
+	uint16_t end_handle;
+
+	struct queue *descriptors;
+};
+
+struct service {
+	struct element_id id;
+	struct gatt_primary prim;
+	struct gatt_included incl;
+
+	bool primary;
+
+	struct queue *chars;
+	struct queue *included;	/* Valid only for primary services */
+	bool incl_search_done;
+};
+
+struct notification_data {
+	struct hal_gatt_srvc_id service;
+	struct hal_gatt_gatt_id ch;
+	struct app_connection *conn;
+	guint notif_id;
+	guint ind_id;
+	int ref;
+};
+
+struct gatt_device {
+	bdaddr_t bdaddr;
+
+	gatt_device_state_t state;
+
+	GAttrib *attrib;
+	GIOChannel *att_io;
+	struct queue *services;
+	bool partial_srvc_search;
+
+	guint watch_id;
+	guint server_id;
+	guint ind_id;
+
+	int ref;
+
+	struct queue *autoconnect_apps;
+
+	struct queue *pending_requests;
+};
+
+struct app_connection {
+	struct gatt_device *device;
+	struct gatt_app *app;
+	struct queue *transactions;
+	int32_t id;
+
+	guint timeout_id;
+
+	bool wait_execute_write;
+};
+
+struct service_sdp {
+	int32_t service_handle;
+	uint32_t sdp_handle;
+};
+
+static struct ipc *hal_ipc = NULL;
+static bdaddr_t adapter_addr;
+static bool scanning = false;
+static unsigned int advertising_cnt = 0;
+
+static struct queue *gatt_apps = NULL;
+static struct queue *gatt_devices = NULL;
+static struct queue *app_connections = NULL;
+
+static struct queue *services_sdp = NULL;
+
+static struct queue *listen_apps = NULL;
+static struct gatt_db *gatt_db = NULL;
+
+static struct gatt_db_attribute *service_changed_attrib = NULL;
+
+static GIOChannel *le_io = NULL;
+static GIOChannel *bredr_io = NULL;
+
+static uint32_t gatt_sdp_handle = 0;
+static uint32_t gap_sdp_handle = 0;
+static uint32_t dis_sdp_handle = 0;
+
+static struct bt_crypto *crypto = NULL;
+
+static int test_client_if = 0;
+static const uint8_t TEST_UUID[] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04
+};
+
+static bool is_bluetooth_uuid(const uint8_t *uuid)
+{
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		/* ignore minimal uuid (16) value */
+		if (i == 12 || i == 13)
+			continue;
+
+		if (uuid[i] != BLUETOOTH_UUID[i])
+			return false;
+	}
+
+	return true;
+}
+
+static void android2uuid(const uint8_t *uuid, bt_uuid_t *dst)
+{
+	if (is_bluetooth_uuid(uuid)) {
+		/* copy 16 bit uuid value from full android 128bit uuid */
+		dst->type = BT_UUID16;
+		dst->value.u16 = (uuid[13] << 8) + uuid[12];
+	} else {
+		int i;
+
+		dst->type = BT_UUID128;
+		for (i = 0; i < 16; i++)
+			dst->value.u128.data[i] = uuid[15 - i];
+	}
+}
+
+static void uuid2android(const bt_uuid_t *src, uint8_t *uuid)
+{
+	bt_uuid_t uu128;
+	uint8_t i;
+
+	if (src->type != BT_UUID128) {
+		bt_uuid_to_uuid128(src, &uu128);
+		src = &uu128;
+	}
+
+	for (i = 0; i < 16; i++)
+		uuid[15 - i] = src->value.u128.data[i];
+}
+
+static void hal_srvc_id_to_element_id(const struct hal_gatt_srvc_id *from,
+							struct element_id *to)
+{
+	to->instance = from->inst_id;
+	android2uuid(from->uuid, &to->uuid);
+}
+
+static void element_id_to_hal_srvc_id(const struct element_id *from,
+						uint8_t primary,
+						struct hal_gatt_srvc_id *to)
+{
+	to->is_primary = primary;
+	to->inst_id = from->instance;
+	uuid2android(&from->uuid, to->uuid);
+}
+
+static void hal_gatt_id_to_element_id(const struct hal_gatt_gatt_id *from,
+							struct element_id *to)
+{
+	to->instance = from->inst_id;
+	android2uuid(from->uuid, &to->uuid);
+}
+
+static void element_id_to_hal_gatt_id(const struct element_id *from,
+						struct hal_gatt_gatt_id *to)
+{
+	to->inst_id = from->instance;
+	uuid2android(&from->uuid, to->uuid);
+}
+
+static void destroy_characteristic(void *data)
+{
+	struct characteristic *chars = data;
+
+	if (!chars)
+		return;
+
+	queue_destroy(chars->descriptors, free);
+	free(chars);
+}
+
+static void destroy_service(void *data)
+{
+	struct service *srvc = data;
+
+	if (!srvc)
+		return;
+
+	queue_destroy(srvc->chars, destroy_characteristic);
+
+	/*
+	 * Included services we keep on two queues.
+	 * 1. On the same queue with primary services.
+	 * 2. On the queue inside primary service.
+	 * So we need to free service memory only once but we need to destroy
+	 * two queues
+	 */
+	queue_destroy(srvc->included, NULL);
+
+	free(srvc);
+}
+
+static bool match_app_by_uuid(const void *data, const void *user_data)
+{
+	const uint8_t *exp_uuid = user_data;
+	const struct gatt_app *client = data;
+
+	return !memcmp(exp_uuid, client->uuid, sizeof(client->uuid));
+}
+
+static bool match_app_by_id(const void *data, const void *user_data)
+{
+	int32_t exp_id = PTR_TO_INT(user_data);
+	const struct gatt_app *client = data;
+
+	return client->id == exp_id;
+}
+
+static struct gatt_app *find_app_by_id(int32_t id)
+{
+	return queue_find(gatt_apps, match_app_by_id, INT_TO_PTR(id));
+}
+
+static bool match_device_by_bdaddr(const void *data, const void *user_data)
+{
+	const struct gatt_device *dev = data;
+	const bdaddr_t *addr = user_data;
+
+	return !bacmp(&dev->bdaddr, addr);
+}
+
+static bool match_device_by_state(const void *data, const void *user_data)
+{
+	const struct gatt_device *dev = data;
+
+	if (dev->state != PTR_TO_UINT(user_data))
+		return false;
+
+	return true;
+}
+
+static bool match_pending_device(const void *data, const void *user_data)
+{
+	const struct gatt_device *dev = data;
+
+	if ((dev->state == DEVICE_CONNECT_INIT) ||
+					(dev->state == DEVICE_CONNECT_READY))
+		return true;
+
+	return false;
+}
+
+static bool match_connection_by_id(const void *data, const void *user_data)
+{
+	const struct app_connection *conn = data;
+	const int32_t id = PTR_TO_INT(user_data);
+
+	return conn->id == id;
+}
+
+static bool match_connection_by_device_and_app(const void *data,
+							const void *user_data)
+{
+	const struct app_connection *conn = data;
+	const struct app_connection *match = user_data;
+
+	return conn->device == match->device && conn->app == match->app;
+}
+
+static struct app_connection *find_connection_by_id(int32_t conn_id)
+{
+	struct app_connection *conn;
+
+	conn = queue_find(app_connections, match_connection_by_id,
+							INT_TO_PTR(conn_id));
+	if (conn && conn->device->state == DEVICE_CONNECTED)
+		return conn;
+
+	return NULL;
+}
+
+static bool match_connection_by_device(const void *data, const void *user_data)
+{
+	const struct app_connection *conn = data;
+	const struct gatt_device *dev = user_data;
+
+	return conn->device == dev;
+}
+
+static bool match_connection_by_app(const void *data, const void *user_data)
+{
+	const struct app_connection *conn = data;
+	const struct gatt_app *app = user_data;
+
+	return conn->app == app;
+}
+
+static struct gatt_device *find_device_by_addr(const bdaddr_t *addr)
+{
+	return queue_find(gatt_devices, match_device_by_bdaddr, addr);
+}
+
+static struct gatt_device *find_pending_device(void)
+{
+	return queue_find(gatt_devices, match_pending_device, NULL);
+}
+
+static struct gatt_device *find_device_by_state(uint32_t state)
+{
+	return queue_find(gatt_devices, match_device_by_state,
+							UINT_TO_PTR(state));
+}
+
+static bool match_srvc_by_element_id(const void *data, const void *user_data)
+{
+	const struct element_id *exp_id = user_data;
+	const struct service *service = data;
+
+	if (service->id.instance == exp_id->instance)
+		return !bt_uuid_cmp(&service->id.uuid, &exp_id->uuid);
+
+	return false;
+}
+
+static bool match_srvc_by_higher_inst_id(const void *data,
+							const void *user_data)
+{
+	const struct service *s = data;
+	uint8_t inst_id = PTR_TO_INT(user_data);
+
+	/* For now we match inst_id as it is unique */
+	return inst_id < s->id.instance;
+}
+
+static bool match_srvc_by_bt_uuid(const void *data, const void *user_data)
+{
+	const bt_uuid_t *exp_uuid = user_data;
+	const struct service *service = data;
+
+	return !bt_uuid_cmp(exp_uuid, &service->id.uuid);
+}
+
+static bool match_srvc_by_range(const void *data, const void *user_data)
+{
+	const struct service *srvc = data;
+	const struct att_range *range = user_data;
+
+	return !memcmp(&srvc->prim.range, range, sizeof(srvc->prim.range));
+}
+
+static bool match_char_by_higher_inst_id(const void *data,
+							const void *user_data)
+{
+	const struct characteristic *ch = data;
+	uint8_t inst_id = PTR_TO_INT(user_data);
+
+	/* For now we match inst_id as it is unique, we'll match uuids later */
+	return inst_id < ch->id.instance;
+}
+
+static bool match_descr_by_element_id(const void *data, const void *user_data)
+{
+	const struct element_id *exp_id = user_data;
+	const struct descriptor *descr = data;
+
+	if (exp_id->instance == descr->id.instance)
+		return !bt_uuid_cmp(&descr->id.uuid, &exp_id->uuid);
+
+	return false;
+}
+
+static bool match_descr_by_higher_inst_id(const void *data,
+							const void *user_data)
+{
+	const struct descriptor *descr = data;
+	uint8_t instance = PTR_TO_INT(user_data);
+
+	/* For now we match instance as it is unique */
+	return instance < descr->id.instance;
+}
+
+static bool match_notification(const void *a, const void *b)
+{
+	const struct notification_data *a1 = a;
+	const struct notification_data *b1 = b;
+
+	if (a1->conn != b1->conn)
+		return false;
+
+	if (memcmp(&a1->ch, &b1->ch, sizeof(a1->ch)))
+		return false;
+
+	if (memcmp(&a1->service, &b1->service, sizeof(a1->service)))
+		return false;
+
+	return true;
+}
+
+static bool match_char_by_element_id(const void *data, const void *user_data)
+{
+	const struct element_id *exp_id = user_data;
+	const struct characteristic *chars = data;
+
+	if (exp_id->instance == chars->id.instance)
+		return !bt_uuid_cmp(&chars->id.uuid, &exp_id->uuid);
+
+	return false;
+}
+
+static void destroy_notification(void *data)
+{
+	struct notification_data *notification = data;
+	struct gatt_app *app;
+
+	if (!notification)
+		return;
+
+	if (--notification->ref)
+		return;
+
+	app = notification->conn->app;
+	queue_remove_if(app->notifications, match_notification, notification);
+	free(notification);
+}
+
+static void unregister_notification(void *data)
+{
+	struct notification_data *notification = data;
+	struct gatt_device *dev = notification->conn->device;
+
+	/*
+	 * No device means it was already disconnected and client cleanup was
+	 * triggered afterwards, but once client unregisters, device stays if
+	 * used by others. Then just unregister single handle.
+	 */
+	if (!queue_find(gatt_devices, NULL, dev))
+		return;
+
+	if (notification->notif_id && dev)
+		g_attrib_unregister(dev->attrib, notification->notif_id);
+
+	if (notification->ind_id && dev)
+		g_attrib_unregister(dev->attrib, notification->ind_id);
+}
+
+static void device_set_state(struct gatt_device *dev, uint32_t state)
+{
+	char bda[18];
+
+	if (dev->state == state)
+		return;
+
+	ba2str(&dev->bdaddr, bda);
+	DBG("gatt: Device %s state changed %s -> %s", bda,
+			device_state_str[dev->state], device_state_str[state]);
+
+	dev->state = state;
+}
+
+static bool auto_connect_le(struct gatt_device *dev)
+{
+	/*  For LE devices use auto connect feature if possible */
+	if (bt_kernel_conn_control()) {
+		if (!bt_auto_connect_add(bt_get_id_addr(&dev->bdaddr, NULL)))
+			return false;
+	} else {
+		/* Trigger discovery if not already started */
+		if (!scanning && !bt_le_discovery_start()) {
+			error("gatt: Could not start scan");
+			return false;
+		}
+	}
+
+	device_set_state(dev, DEVICE_CONNECT_INIT);
+	return true;
+}
+
+static void connection_cleanup(struct gatt_device *device)
+{
+	if (device->watch_id) {
+		g_source_remove(device->watch_id);
+		device->watch_id = 0;
+	}
+
+	if (device->att_io) {
+		g_io_channel_shutdown(device->att_io, FALSE, NULL);
+		g_io_channel_unref(device->att_io);
+		device->att_io = NULL;
+	}
+
+	if (device->attrib) {
+		GAttrib *attrib = device->attrib;
+
+		if (device->server_id > 0)
+			g_attrib_unregister(device->attrib, device->server_id);
+
+		if (device->ind_id > 0)
+			g_attrib_unregister(device->attrib, device->ind_id);
+
+		device->attrib = NULL;
+		g_attrib_cancel_all(attrib);
+		g_attrib_unref(attrib);
+	}
+
+	/*
+	 * If device was in connection_pending or connectable state we
+	 * search device list if we should stop the scan.
+	 */
+	if (!scanning && (device->state == DEVICE_CONNECT_INIT ||
+				device->state == DEVICE_CONNECT_READY)) {
+		if (!find_pending_device())
+			bt_le_discovery_stop(NULL);
+	}
+
+	/* If device is not bonded service cache should be refreshed */
+	if (!bt_device_is_bonded(&device->bdaddr))
+		queue_remove_all(device->services, NULL, NULL, destroy_service);
+
+	device_set_state(device, DEVICE_DISCONNECTED);
+
+	if (!queue_isempty(device->autoconnect_apps))
+		auto_connect_le(device);
+	else
+		bt_auto_connect_remove(&device->bdaddr);
+}
+
+static void destroy_gatt_app(void *data)
+{
+	struct gatt_app *app = data;
+
+	if (!app)
+		return;
+
+	/*
+	 * First we want to get all notifications and unregister them.
+	 * We don't pass unregister_notification to queue_destroy,
+	 * because destroy notification performs operations on queue
+	 * too. So remove all elements and then destroy queue.
+	 */
+
+	if (app->type == GATT_CLIENT)
+		while (queue_peek_head(app->notifications)) {
+			struct notification_data *notification;
+
+			notification = queue_pop_head(app->notifications);
+			unregister_notification(notification);
+		}
+
+	queue_destroy(app->notifications, free);
+
+	free(app);
+}
+
+struct pending_request {
+	struct gatt_db_attribute *attrib;
+	int length;
+	uint8_t *value;
+	uint16_t offset;
+
+	uint8_t *filter_value;
+	uint16_t filter_vlen;
+
+	bool completed;
+	uint8_t error;
+};
+
+static void destroy_pending_request(void *data)
+{
+	struct pending_request *entry = data;
+
+	if (!entry)
+		return;
+
+	free(entry->value);
+	free(entry->filter_value);
+	free(entry);
+}
+
+static void destroy_device(void *data)
+{
+	struct gatt_device *dev = data;
+
+	if (!dev)
+		return;
+
+	queue_destroy(dev->services, destroy_service);
+	queue_destroy(dev->pending_requests, destroy_pending_request);
+	queue_destroy(dev->autoconnect_apps, NULL);
+
+	bt_auto_connect_remove(&dev->bdaddr);
+
+	free(dev);
+}
+
+static struct gatt_device *device_ref(struct gatt_device *device)
+{
+	if (!device)
+		return NULL;
+
+	device->ref++;
+
+	return device;
+}
+
+static void device_unref(struct gatt_device *device)
+{
+	if (!device)
+		return;
+
+	if (--device->ref)
+		return;
+
+	destroy_device(device);
+}
+
+static struct gatt_device *create_device(const bdaddr_t *addr)
+{
+	struct gatt_device *dev;
+
+	dev = new0(struct gatt_device, 1);
+
+	bacpy(&dev->bdaddr, addr);
+
+	dev->services = queue_new();
+	dev->autoconnect_apps = queue_new();
+	dev->pending_requests = queue_new();
+
+	queue_push_head(gatt_devices, dev);
+
+	return device_ref(dev);
+}
+
+static void send_client_connect_status_notify(struct app_connection *conn,
+								int32_t status)
+{
+	struct hal_ev_gatt_client_connect ev;
+
+	if (conn->app->func) {
+		conn->app->func(&conn->device->bdaddr,
+					status == GATT_SUCCESS ? 0 : -ENOTCONN,
+					conn->device->attrib);
+		return;
+	}
+
+	ev.client_if = conn->app->id;
+	ev.conn_id = conn->id;
+	ev.status = status;
+
+	bdaddr2android(&conn->device->bdaddr, &ev.bda);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_CONNECT,
+							sizeof(ev), &ev);
+}
+
+static void send_server_connection_state_notify(struct app_connection *conn,
+								bool connected)
+{
+	struct hal_ev_gatt_server_connection ev;
+
+	if (conn->app->func) {
+		conn->app->func(&conn->device->bdaddr,
+					connected ? 0 : -ENOTCONN,
+					conn->device->attrib);
+		return;
+	}
+
+	ev.server_if = conn->app->id;
+	ev.conn_id = conn->id;
+	ev.connected = connected;
+
+	bdaddr2android(&conn->device->bdaddr, &ev.bdaddr);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_EV_GATT_SERVER_CONNECTION, sizeof(ev), &ev);
+}
+
+static void send_client_disconnect_status_notify(struct app_connection *conn,
+								int32_t status)
+{
+	struct hal_ev_gatt_client_disconnect ev;
+
+	if (conn->app->func) {
+		conn->app->func(&conn->device->bdaddr, -ENOTCONN,
+						conn->device->attrib);
+		return;
+	}
+
+	ev.client_if = conn->app->id;
+	ev.conn_id = conn->id;
+	ev.status = status;
+
+	bdaddr2android(&conn->device->bdaddr, &ev.bda);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev);
+
+}
+
+static void notify_app_disconnect_status(struct app_connection *conn,
+								int32_t status)
+{
+	if (!conn->app)
+		return;
+
+	if (conn->app->type == GATT_CLIENT)
+		send_client_disconnect_status_notify(conn, status);
+	else
+		send_server_connection_state_notify(conn, !!status);
+}
+
+static void notify_app_connect_status(struct app_connection *conn,
+								int32_t status)
+{
+	if (!conn->app)
+		return;
+
+	if (conn->app->type == GATT_CLIENT)
+		send_client_connect_status_notify(conn, status);
+	else
+		send_server_connection_state_notify(conn, !status);
+}
+
+static void destroy_connection(void *data)
+{
+	struct app_connection *conn = data;
+
+	if (!conn)
+		return;
+
+	if (conn->timeout_id > 0)
+		g_source_remove(conn->timeout_id);
+
+	switch (conn->device->state) {
+	case DEVICE_CONNECTED:
+		notify_app_disconnect_status(conn, GATT_SUCCESS);
+		break;
+	case DEVICE_CONNECT_INIT:
+	case DEVICE_CONNECT_READY:
+		notify_app_connect_status(conn, GATT_FAILURE);
+		break;
+	case DEVICE_DISCONNECTED:
+		break;
+	}
+
+	if (!queue_find(app_connections, match_connection_by_device,
+							conn->device))
+		connection_cleanup(conn->device);
+
+	queue_destroy(conn->transactions, free);
+	device_unref(conn->device);
+	free(conn);
+}
+
+static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct gatt_device *dev = user_data;
+	int sock, err = 0;
+	socklen_t len;
+
+	sock = g_io_channel_unix_get_fd(io);
+	len = sizeof(err);
+	if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len))
+		DBG("%s (%d)", strerror(err), err);
+
+	queue_remove_all(app_connections, match_connection_by_device, dev,
+							destroy_connection);
+
+	return FALSE;
+}
+
+static bool get_local_mtu(struct gatt_device *dev, uint16_t *mtu)
+{
+	GIOChannel *io;
+	uint16_t imtu, omtu;
+
+	io = g_attrib_get_channel(dev->attrib);
+
+	if (!bt_io_get(io, NULL, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_OMTU, &omtu,
+							BT_IO_OPT_INVALID)) {
+		error("gatt: Failed to get local MTU");
+		return false;
+	}
+
+	/*
+	 * Limit MTU to  MIN(IMTU, OMTU). This is to avoid situation where
+	 * local OMTU < MIN(remote MTU, IMTU)
+	 */
+	if (mtu)
+		*mtu = MIN(imtu, omtu);
+
+	return true;
+}
+
+static void notify_client_mtu_change(struct app_connection *conn, bool success)
+{
+	struct hal_ev_gatt_client_configure_mtu ev;
+	size_t mtu;
+
+	g_attrib_get_buffer(conn->device->attrib, &mtu);
+
+	ev.conn_id = conn->id;
+	ev.status = success ? GATT_SUCCESS : GATT_FAILURE;
+	ev.mtu = mtu;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_CONFIGURE_MTU, sizeof(ev), &ev);
+}
+
+static void notify_server_mtu(struct app_connection *conn)
+{
+	struct hal_ev_gatt_server_mtu_changed ev;
+	size_t mtu;
+
+	g_attrib_get_buffer(conn->device->attrib, &mtu);
+
+	ev.conn_id = conn->id;
+	ev.mtu = mtu;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_MTU_CHANGED, sizeof(ev), &ev);
+}
+
+static void notify_mtu_change(void *data, void *user_data)
+{
+	struct gatt_device *device = user_data;
+	struct app_connection *conn = data;
+
+	if (conn->device != device)
+		return;
+
+	if (!conn->app) {
+		error("gatt: can't notify mtu - no app registered for conn");
+		return;
+	}
+
+	switch (conn->app->type) {
+	case GATT_CLIENT:
+		notify_client_mtu_change(conn, true);
+		break;
+	case GATT_SERVER:
+		notify_server_mtu(conn);
+		break;
+	default:
+		break;
+	}
+}
+
+static bool update_mtu(struct gatt_device *device, uint16_t rmtu)
+{
+	uint16_t mtu, lmtu;
+
+	if (!get_local_mtu(device, &lmtu))
+		return false;
+
+	DBG("remote_mtu:%d local_mtu:%d", rmtu, lmtu);
+
+	if (rmtu < ATT_DEFAULT_LE_MTU) {
+		error("gatt: remote MTU invalid (%u bytes)", rmtu);
+		return false;
+	}
+
+	mtu = MIN(lmtu, rmtu);
+
+	if (mtu == ATT_DEFAULT_LE_MTU)
+		return true;
+
+	if (!g_attrib_set_mtu(device->attrib, mtu)) {
+		error("gatt: Failed to set MTU");
+		return false;
+	}
+
+	queue_foreach(app_connections, notify_mtu_change, device);
+
+	return true;
+}
+
+static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data);
+
+static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	struct gatt_device *device = user_data;
+	uint16_t rmtu;
+
+	DBG("");
+
+	if (status) {
+		error("gatt: MTU exchange: %s", att_ecode2str(status));
+		goto failed;
+	}
+
+	if (!dec_mtu_resp(pdu, plen, &rmtu)) {
+		error("gatt: MTU exchange: protocol error");
+		goto failed;
+	}
+
+	update_mtu(device, rmtu);
+
+failed:
+	device_unref(device);
+}
+
+static void send_exchange_mtu_request(struct gatt_device *device)
+{
+	uint16_t mtu;
+
+	if (!get_local_mtu(device, &mtu))
+		return;
+
+	DBG("mtu %u", mtu);
+
+	if (!gatt_exchange_mtu(device->attrib, mtu, exchange_mtu_cb,
+							device_ref(device)))
+		device_unref(device);
+}
+
+static void ignore_confirmation_cb(guint8 status, const guint8 *pdu,
+					guint16 len, gpointer user_data)
+{
+	/* Ignored. */
+}
+
+static void notify_att_range_change(struct gatt_device *dev,
+							struct att_range *range)
+{
+	uint16_t handle;
+	uint16_t length = 0;
+	uint16_t ccc;
+	uint8_t *pdu;
+	size_t mtu;
+	GAttribResultFunc confirmation_cb = NULL;
+
+	handle = gatt_db_attribute_get_handle(service_changed_attrib);
+	if (!handle)
+		return;
+
+	ccc = bt_get_gatt_ccc(&dev->bdaddr);
+	if (!ccc)
+		return;
+
+	pdu = g_attrib_get_buffer(dev->attrib, &mtu);
+
+	switch (ccc) {
+	case 0x0001:
+		length = enc_notification(handle, (uint8_t *) range,
+						sizeof(*range), pdu, mtu);
+		break;
+	case 0x0002:
+		length = enc_indication(handle, (uint8_t *) range,
+						sizeof(*range), pdu, mtu);
+		confirmation_cb = ignore_confirmation_cb;
+		break;
+	default:
+		/* 0xfff4 reserved for future use */
+		break;
+	}
+
+	g_attrib_send(dev->attrib, 0, pdu, length, confirmation_cb, NULL, NULL);
+}
+
+static struct app_connection *create_connection(struct gatt_device *device,
+						struct gatt_app *app)
+{
+	struct app_connection *new_conn;
+	static int32_t last_conn_id = 1;
+
+	/* Check if already connected */
+	new_conn = new0(struct app_connection, 1);
+
+	/* Make connection id unique to connection record (app, device) pair */
+	new_conn->app = app;
+	new_conn->id = last_conn_id++;
+	new_conn->transactions = queue_new();
+
+	queue_push_head(app_connections, new_conn);
+
+	new_conn->device = device_ref(device);
+
+	return new_conn;
+}
+
+static struct service *create_service(uint8_t id, bool primary, char *uuid,
+								void *data)
+{
+	struct service *s;
+
+	s = new0(struct service, 1);
+
+	if (bt_string_to_uuid(&s->id.uuid, uuid) < 0) {
+		error("gatt: Cannot convert string to uuid");
+		free(s);
+		return NULL;
+	}
+
+	s->chars = queue_new();
+	s->included = queue_new();
+	s->id.instance = id;
+
+	/* Put primary service to our local list */
+	s->primary = primary;
+	if (s->primary)
+		memcpy(&s->prim, data, sizeof(s->prim));
+	else
+		memcpy(&s->incl, data, sizeof(s->incl));
+
+	return s;
+}
+
+static void send_client_primary_notify(void *data, void *user_data)
+{
+	struct hal_ev_gatt_client_search_result ev;
+	struct service *p = data;
+	int32_t conn_id = PTR_TO_INT(user_data);
+
+	/* In service queue we will have also included services */
+	if (!p->primary)
+		return;
+
+	ev.conn_id  = conn_id;
+	element_id_to_hal_srvc_id(&p->id, 1, &ev.srvc_id);
+
+	uuid2android(&p->id.uuid, ev.srvc_id.uuid);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_SEARCH_RESULT, sizeof(ev), &ev);
+}
+
+static void send_client_search_complete_notify(int32_t status, int32_t conn_id)
+{
+	struct hal_ev_gatt_client_search_complete ev;
+
+	ev.status = status;
+	ev.conn_id = conn_id;
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_SEARCH_COMPLETE, sizeof(ev), &ev);
+}
+
+struct discover_srvc_data {
+	bt_uuid_t uuid;
+	struct app_connection *conn;
+};
+
+static void discover_srvc_by_uuid_cb(uint8_t status, GSList *ranges,
+								void *user_data)
+{
+	struct discover_srvc_data *cb_data = user_data;
+	struct gatt_primary prim;
+	struct service *s;
+	int32_t gatt_status;
+	struct gatt_device *dev = cb_data->conn->device;
+	uint8_t instance_id = queue_length(dev->services);
+
+	DBG("Status %d", status);
+
+	if (status) {
+		error("gatt: Discover pri srvc filtered by uuid failed: %s",
+							att_ecode2str(status));
+		gatt_status = GATT_FAILURE;
+		goto reply;
+	}
+
+	if (!ranges) {
+		info("gatt: No primary services searched by uuid found");
+		gatt_status = GATT_SUCCESS;
+		goto reply;
+	}
+
+	bt_uuid_to_string(&cb_data->uuid, prim.uuid, sizeof(prim.uuid));
+
+	for (; ranges; ranges = ranges->next) {
+		memcpy(&prim.range, ranges->data, sizeof(prim.range));
+
+		s = create_service(instance_id++, true, prim.uuid, &prim);
+		if (!s) {
+			gatt_status = GATT_FAILURE;
+			goto reply;
+		}
+
+		queue_push_tail(dev->services, s);
+
+		send_client_primary_notify(s, INT_TO_PTR(cb_data->conn->id));
+
+		DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
+				prim.range.start, prim.range.end, prim.uuid);
+	}
+
+	/* Partial search service scanning was performed */
+	dev->partial_srvc_search = true;
+	gatt_status = GATT_SUCCESS;
+
+reply:
+	send_client_search_complete_notify(gatt_status, cb_data->conn->id);
+	free(cb_data);
+}
+
+static void discover_srvc_all_cb(uint8_t status, GSList *services,
+								void *user_data)
+{
+	struct discover_srvc_data *cb_data = user_data;
+	struct gatt_device *dev = cb_data->conn->device;
+	int32_t gatt_status;
+	GSList *l;
+	/*
+	 * There might be multiply services with same uuid. Therefore make sure
+	 * each primary service one has unique instance_id
+	 */
+	uint8_t instance_id = queue_length(dev->services);
+
+	DBG("Status %d", status);
+
+	if (status) {
+		error("gatt: Discover all primary services failed: %s",
+							att_ecode2str(status));
+		gatt_status = GATT_FAILURE;
+		goto reply;
+	}
+
+	if (!services) {
+		info("gatt: No primary services found");
+		gatt_status = GATT_SUCCESS;
+		goto reply;
+	}
+
+	for (l = services; l; l = l->next) {
+		struct gatt_primary *prim = l->data;
+		struct service *p;
+
+		if (queue_find(dev->services, match_srvc_by_range,
+								&prim->range))
+			continue;
+
+		p = create_service(instance_id++, true, prim->uuid, prim);
+		if (!p)
+			continue;
+
+		queue_push_tail(dev->services, p);
+
+		DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
+			prim->range.start, prim->range.end, prim->uuid);
+	}
+
+	/*
+	 * Send all found services notifications - first cache,
+	 * then send notifies
+	 */
+	queue_foreach(dev->services, send_client_primary_notify,
+						INT_TO_PTR(cb_data->conn->id));
+
+	/* Full search service scanning was performed */
+	dev->partial_srvc_search = false;
+	gatt_status = GATT_SUCCESS;
+
+reply:
+	send_client_search_complete_notify(gatt_status, cb_data->conn->id);
+	free(cb_data);
+}
+
+static gboolean connection_timeout(void *user_data)
+{
+	struct app_connection *conn = user_data;
+
+	conn->timeout_id = 0;
+
+	queue_remove(app_connections, conn);
+	destroy_connection(conn);
+
+	return FALSE;
+}
+
+static void discover_primary_cb(uint8_t status, GSList *services,
+								void *user_data)
+{
+	struct discover_srvc_data *cb_data = user_data;
+	struct app_connection *conn = cb_data->conn;
+	struct gatt_device *dev = conn->device;
+	GSList *l, *uuids = NULL;
+
+	DBG("Status %d", status);
+
+	if (status) {
+		error("gatt: Discover all primary services failed: %s",
+							att_ecode2str(status));
+		free(cb_data);
+
+		return;
+	}
+
+	if (!services) {
+		info("gatt: No primary services found");
+		free(cb_data);
+
+		return;
+	}
+
+	for (l = services; l; l = l->next) {
+		struct gatt_primary *prim = l->data;
+		uint8_t *new_uuid;
+		bt_uuid_t uuid, u128;
+
+		DBG("uuid: %s", prim->uuid);
+
+		if (bt_string_to_uuid(&uuid, prim->uuid) < 0) {
+			error("gatt: Cannot convert string to uuid");
+			continue;
+		}
+
+		bt_uuid_to_uuid128(&uuid, &u128);
+		new_uuid = g_memdup(&u128.value.u128, sizeof(u128.value.u128));
+
+		uuids = g_slist_prepend(uuids, new_uuid);
+	}
+
+	bt_device_set_uuids(&dev->bdaddr, uuids);
+
+	free(cb_data);
+
+	conn->timeout_id = g_timeout_add_seconds(GATT_CONN_TIMEOUT,
+						connection_timeout, conn);
+}
+
+static guint search_dev_for_srvc(struct app_connection *conn, bt_uuid_t *uuid)
+{
+	struct discover_srvc_data *cb_data;
+
+	cb_data = new0(struct discover_srvc_data, 1);
+	cb_data->conn = conn;
+
+	if (uuid) {
+		memcpy(&cb_data->uuid, uuid, sizeof(cb_data->uuid));
+		return gatt_discover_primary(conn->device->attrib, uuid,
+					discover_srvc_by_uuid_cb, cb_data);
+	}
+
+	if (conn->app)
+		return gatt_discover_primary(conn->device->attrib, NULL,
+						discover_srvc_all_cb, cb_data);
+
+	return gatt_discover_primary(conn->device->attrib, NULL,
+						discover_primary_cb, cb_data);
+}
+
+struct connect_data {
+	struct gatt_device *dev;
+	int32_t status;
+};
+
+static void notify_app_connect_status_by_device(void *data, void *user_data)
+{
+	struct app_connection *conn = data;
+	struct connect_data *con_data = user_data;
+
+	if (conn->device == con_data->dev)
+		notify_app_connect_status(conn, con_data->status);
+}
+
+static struct app_connection *find_conn_without_app(struct gatt_device *dev)
+{
+	struct app_connection conn_match;
+
+	conn_match.device = dev;
+	conn_match.app = NULL;
+
+	return queue_find(app_connections, match_connection_by_device_and_app,
+								&conn_match);
+}
+
+static struct app_connection *find_conn(const bdaddr_t *addr, int32_t app_id)
+{
+	struct app_connection conn_match;
+	struct gatt_device *dev;
+	struct gatt_app *app;
+
+	/* Check if app is registered */
+	app = find_app_by_id(app_id);
+	if (!app) {
+		error("gatt: Client id %d not found", app_id);
+		return NULL;
+	}
+
+	/* Check if device is known */
+	dev = find_device_by_addr(addr);
+	if (!dev) {
+		error("gatt: Client id %d not found", app_id);
+		return NULL;
+	}
+
+	conn_match.device = dev;
+	conn_match.app = app;
+
+	return queue_find(app_connections, match_connection_by_device_and_app,
+								&conn_match);
+}
+
+static void create_app_connection(void *data, void *user_data)
+{
+	struct gatt_device *dev = user_data;
+	struct gatt_app *app;
+
+	app = find_app_by_id(PTR_TO_INT(data));
+	if (!app)
+		return;
+
+	DBG("Autoconnect application id=%d", app->id);
+
+	if (!find_conn(&dev->bdaddr, PTR_TO_INT(data)))
+		create_connection(dev, app);
+}
+
+static void ind_handler(const uint8_t *cmd, uint16_t cmd_len,
+							gpointer user_data)
+{
+	struct gatt_device *dev = user_data;
+	uint16_t resp_length = 0;
+	size_t length;
+	uint8_t *opdu = g_attrib_get_buffer(dev->attrib, &length);
+
+	/*
+	 * We have to send confirmation here. If some client is
+	 * registered for this indication, event will be send in
+	 * handle_notification
+	 */
+
+	resp_length = enc_confirmation(opdu, length);
+	g_attrib_send(dev->attrib, 0, opdu, resp_length, NULL, NULL, NULL);
+}
+
+static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+	struct gatt_device *dev = user_data;
+	struct connect_data data;
+	struct att_range range;
+	uint32_t status;
+	GError *err = NULL;
+	GAttrib *attrib;
+	uint16_t mtu, cid;
+
+	if (dev->state != DEVICE_CONNECT_READY) {
+		error("gatt: Device not in a connecting state!?");
+		g_io_channel_shutdown(io, TRUE, NULL);
+		return;
+	}
+
+	if (dev->att_io) {
+		g_io_channel_unref(dev->att_io);
+		dev->att_io = NULL;
+	}
+
+	if (gerr) {
+		error("gatt: connection failed %s", gerr->message);
+		device_set_state(dev, DEVICE_DISCONNECTED);
+		status = GATT_FAILURE;
+		goto reply;
+	}
+
+	if (!bt_io_get(io, &err, BT_IO_OPT_IMTU, &mtu, BT_IO_OPT_CID, &cid,
+							BT_IO_OPT_INVALID)) {
+		error("gatt: Could not get imtu or cid: %s", err->message);
+		device_set_state(dev, DEVICE_DISCONNECTED);
+		status = GATT_FAILURE;
+		g_error_free(err);
+		goto reply;
+	}
+
+	/* on BR/EDR MTU must not be less then minimal allowed MTU */
+	if (cid != ATT_CID && mtu < ATT_DEFAULT_L2CAP_MTU) {
+		error("gatt: MTU too small (%u bytes)", mtu);
+		device_set_state(dev, DEVICE_DISCONNECTED);
+		status = GATT_FAILURE;
+		goto reply;
+	}
+
+	DBG("mtu %u cid %u", mtu, cid);
+
+	/* on LE we always start with default MTU */
+	if (cid == ATT_CID)
+		mtu = ATT_DEFAULT_LE_MTU;
+
+	attrib = g_attrib_new(io, mtu, true);
+	if (!attrib) {
+		error("gatt: unable to create new GAttrib instance");
+		device_set_state(dev, DEVICE_DISCONNECTED);
+		status = GATT_FAILURE;
+		goto reply;
+	}
+
+	dev->attrib = attrib;
+	dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+							disconnected_cb, dev);
+
+	dev->server_id = g_attrib_register(attrib, GATTRIB_ALL_REQS,
+						GATTRIB_ALL_HANDLES,
+						att_handler, dev, NULL);
+	dev->ind_id = g_attrib_register(attrib, ATT_OP_HANDLE_IND,
+						GATTRIB_ALL_HANDLES,
+						ind_handler, dev, NULL);
+	if ((dev->server_id && dev->ind_id) == 0)
+		error("gatt: Could not attach to server");
+
+	device_set_state(dev, DEVICE_CONNECTED);
+
+	/* Send exchange mtu request as we assume being client and server */
+	/* TODO: Dont exchange mtu if no client apps */
+
+	/* MTU exchange shall not be used on BR/EDR - Vol 3. Part G. 4.3.1 */
+	if (cid == ATT_CID)
+		send_exchange_mtu_request(dev);
+
+	/*
+	 * Service Changed Characteristic and CCC Descriptor handles
+	 * should not change if there are bonded devices. We have them
+	 * constant all the time, thus they should be excluded from
+	 * range indicating changes.
+	 */
+	range.start = gatt_db_attribute_get_handle(service_changed_attrib) + 2;
+	range.end = 0xffff;
+
+	/*
+	 * If there is ccc stored for that device we were acting as server for
+	 * it, and as we dont have last connect and last services (de)activation
+	 * timestamps we should always assume something has changed.
+	 */
+	notify_att_range_change(dev, &range);
+
+	status = GATT_SUCCESS;
+
+reply:
+	/*
+	 * Make sure there are app_connections for all apps interested in auto
+	 * connect to that device
+	 */
+	queue_foreach(dev->autoconnect_apps, create_app_connection, dev);
+
+	if (!queue_find(app_connections, match_connection_by_device, dev)) {
+		struct app_connection *conn;
+
+		if (!dev->attrib)
+			return;
+
+		conn = create_connection(dev, NULL);
+		if (!conn)
+			return;
+
+		if (bt_is_pairing(&dev->bdaddr))
+			/*
+			 * If there is bonding ongoing lets wait for paired
+			 * callback. Once we get that we can start search
+			 * services
+			 */
+			conn->timeout_id = g_timeout_add_seconds(
+						GATT_PAIR_CONN_TIMEOUT,
+						connection_timeout, conn);
+		else
+			/*
+			 * There is no ongoing bonding, lets search for primary
+			 * services
+			 */
+			search_dev_for_srvc(conn, NULL);
+	}
+
+	data.dev = dev;
+	data.status = status;
+	queue_foreach(app_connections, notify_app_connect_status_by_device,
+									&data);
+
+	/* For BR/EDR notify about MTU since it is not negotiable*/
+	if (cid != ATT_CID && status == GATT_SUCCESS)
+		queue_foreach(app_connections, notify_mtu_change, dev);
+
+	device_unref(dev);
+
+	/* Check if we should restart scan */
+	if (scanning)
+		bt_le_discovery_start();
+
+	/* FIXME: What to do if discovery won't start here. */
+}
+
+static int connect_le(struct gatt_device *dev)
+{
+	GIOChannel *io;
+	GError *gerr = NULL;
+	char addr[18];
+	const bdaddr_t *bdaddr;
+	uint8_t bdaddr_type;
+
+	ba2str(&dev->bdaddr, addr);
+
+	/* There is one connection attempt going on */
+	if (dev->att_io) {
+		info("gatt: connection to dev %s is ongoing", addr);
+		return -EALREADY;
+	}
+
+	DBG("Connection attempt to: %s", addr);
+
+	bdaddr = bt_get_id_addr(&dev->bdaddr, &bdaddr_type);
+
+	/*
+	 * This connection will help us catch any PDUs that comes before
+	 * pairing finishes
+	 */
+	io = bt_io_connect(connect_cb, device_ref(dev), NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+					BT_IO_OPT_DEST_BDADDR, bdaddr,
+					BT_IO_OPT_DEST_TYPE, bdaddr_type,
+					BT_IO_OPT_CID, ATT_CID,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+	if (!io) {
+		error("gatt: Failed bt_io_connect(%s): %s", addr,
+							gerr->message);
+		g_error_free(gerr);
+		return -EIO;
+	}
+
+	/* Keep this, so we can cancel the connection */
+	dev->att_io = io;
+
+	device_set_state(dev, DEVICE_CONNECT_READY);
+
+	return 0;
+}
+
+static int connect_next_dev(void)
+{
+	struct gatt_device *dev;
+
+	DBG("");
+
+	dev = find_device_by_state(DEVICE_CONNECT_READY);
+	if (!dev)
+		return -ENODEV;
+
+	return connect_le(dev);
+}
+
+static void bt_le_discovery_stop_cb(void)
+{
+	DBG("");
+
+	/* Check now if there is any device ready to connect */
+	if (connect_next_dev() < 0)
+		bt_le_discovery_start();
+}
+
+static void le_device_found_handler(const bdaddr_t *addr, int rssi,
+					uint16_t eir_len, const void *eir,
+					bool connectable, bool bonded)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
+	struct gatt_device *dev;
+	char bda[18];
+
+	if (!scanning)
+		goto done;
+
+	ba2str(addr, bda);
+	DBG("LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi, !!eir);
+
+	bdaddr2android(addr, ev->bda);
+	ev->rssi = rssi;
+	ev->len = eir_len;
+
+	memcpy(ev->adv_data, eir, ev->len);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+						HAL_EV_GATT_CLIENT_SCAN_RESULT,
+						sizeof(*ev) + ev->len, ev);
+
+done:
+	if (!connectable)
+		return;
+
+	/* We use auto connect feature from kernel if possible */
+	if (bt_kernel_conn_control())
+		return;
+
+	dev = find_device_by_addr(addr);
+	if (!dev) {
+		if (!bonded)
+			return;
+
+		dev = create_device(addr);
+	}
+
+	if (dev->state != DEVICE_CONNECT_INIT)
+		return;
+
+	device_set_state(dev, DEVICE_CONNECT_READY);
+
+	/*
+	 * We are ok to perform connect now. Stop discovery
+	 * and once it is stopped continue with creating ACL
+	 */
+	bt_le_discovery_stop(bt_le_discovery_stop_cb);
+}
+
+static struct gatt_app *register_app(const uint8_t *uuid, gatt_type_t type)
+{
+	static int32_t application_id = 1;
+	struct gatt_app *app;
+
+	if (queue_find(gatt_apps, match_app_by_uuid, uuid)) {
+		error("gatt: app uuid is already on list");
+		return NULL;
+	}
+
+	app = new0(struct gatt_app, 1);
+
+	app->type = type;
+
+	if (app->type == GATT_CLIENT)
+		app->notifications = queue_new();
+
+	memcpy(app->uuid, uuid, sizeof(app->uuid));
+
+	app->id = application_id++;
+
+	queue_push_head(gatt_apps, app);
+
+	if (app->type == GATT_SERVER)
+		queue_push_tail(listen_apps, INT_TO_PTR(app->id));
+
+	return app;
+}
+
+static void handle_client_register(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_register *cmd = buf;
+	struct hal_ev_gatt_client_register_client ev;
+	struct gatt_app *app;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	app = register_app(cmd->uuid, GATT_CLIENT);
+
+	if (app) {
+		ev.client_if = app->id;
+		ev.status = GATT_SUCCESS;
+	} else {
+		ev.status = GATT_FAILURE;
+	}
+
+	/* We should send notification with given in cmd UUID */
+	memcpy(ev.app_uuid, cmd->uuid, sizeof(ev.app_uuid));
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_REGISTER_CLIENT, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
+							HAL_STATUS_SUCCESS);
+}
+
+static void handle_client_scan(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_scan *cmd = buf;
+	uint8_t status;
+
+	DBG("new state %d", cmd->start);
+
+	if (cmd->client_if != 0) {
+		void *registered = find_app_by_id(cmd->client_if);
+
+		if (!registered) {
+			error("gatt: Client not registered");
+			status = HAL_STATUS_FAILED;
+			goto reply;
+		}
+	}
+
+	/* Turn off scan */
+	if (!cmd->start) {
+		DBG("Stopping LE SCAN");
+
+		if (scanning) {
+			bt_le_discovery_stop(NULL);
+			scanning = false;
+		}
+
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	/* Reply success if we already do scan */
+	if (scanning) {
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	/* Turn on scan */
+	if (!bt_le_discovery_start()) {
+		error("gatt: LE scan switch failed");
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	scanning = true;
+	status = HAL_STATUS_SUCCESS;
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN,
+									status);
+}
+
+static int connect_bredr(struct gatt_device *dev)
+{
+	BtIOSecLevel sec_level;
+	GIOChannel *io;
+	GError *gerr = NULL;
+	char addr[18];
+
+	ba2str(&dev->bdaddr, addr);
+
+	/* There is one connection attempt going on */
+	if (dev->att_io) {
+		info("gatt: connection to dev %s is ongoing", addr);
+		return -EALREADY;
+	}
+
+	DBG("Connection attempt to: %s", addr);
+
+	sec_level = bt_device_is_bonded(&dev->bdaddr) ? BT_IO_SEC_MEDIUM :
+								BT_IO_SEC_LOW;
+
+	io = bt_io_connect(connect_cb, device_ref(dev), NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_SOURCE_TYPE, BDADDR_BREDR,
+					BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+					BT_IO_OPT_DEST_TYPE, BDADDR_BREDR,
+					BT_IO_OPT_PSM, ATT_PSM,
+					BT_IO_OPT_SEC_LEVEL, sec_level,
+					BT_IO_OPT_INVALID);
+	if (!io) {
+		error("gatt: Failed bt_io_connect(%s): %s", addr,
+							gerr->message);
+		g_error_free(gerr);
+		return -EIO;
+	}
+
+	device_set_state(dev, DEVICE_CONNECT_READY);
+
+	/* Keep this, so we can cancel the connection */
+	dev->att_io = io;
+
+	return 0;
+}
+
+static bool trigger_connection(struct app_connection *conn, bool direct)
+{
+	switch (conn->device->state) {
+	case DEVICE_DISCONNECTED:
+		/*
+		 *  If device was last seen over BR/EDR connect over it.
+		 *  Note: Connection state is handled in connect_bredr() func
+		 */
+		if (bt_device_last_seen_bearer(&conn->device->bdaddr) ==
+								BDADDR_BREDR)
+			return connect_bredr(conn->device) == 0;
+
+		if (direct)
+			return connect_le(conn->device) == 0;
+
+		bt_gatt_add_autoconnect(conn->app->id, &conn->device->bdaddr);
+		return auto_connect_le(conn->device);
+	case DEVICE_CONNECTED:
+		notify_app_connect_status(conn, GATT_SUCCESS);
+		return true;
+	case DEVICE_CONNECT_READY:
+	case DEVICE_CONNECT_INIT:
+	default:
+		/* In those cases connection is already triggered. */
+		return true;
+	}
+}
+
+static void remove_autoconnect_device(struct gatt_device *dev)
+{
+	bt_auto_connect_remove(&dev->bdaddr);
+
+	if (dev->state == DEVICE_CONNECT_INIT)
+		device_set_state(dev, DEVICE_DISCONNECTED);
+
+	device_unref(dev);
+}
+
+static void clear_autoconnect_devices(void *data, void *user_data)
+{
+	struct gatt_device *dev = data;
+
+	if (queue_remove(dev->autoconnect_apps, user_data))
+		if (queue_isempty(dev->autoconnect_apps))
+			remove_autoconnect_device(dev);
+}
+
+static uint8_t unregister_app(int client_if)
+{
+	struct gatt_app *cl;
+
+	/*
+	 * Make sure that there is no devices in auto connect list for this
+	 * application
+	 */
+	queue_foreach(gatt_devices, clear_autoconnect_devices,
+							INT_TO_PTR(client_if));
+
+	cl = queue_remove_if(gatt_apps, match_app_by_id, INT_TO_PTR(client_if));
+	if (!cl) {
+		error("gatt: client_if=%d not found", client_if);
+
+		return HAL_STATUS_FAILED;
+	}
+
+	/* Destroy app connections with proper notifications for this app. */
+	queue_remove_all(app_connections, match_connection_by_app, cl,
+							destroy_connection);
+	destroy_gatt_app(cl);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void send_client_listen_notify(int32_t id, int32_t status)
+{
+	struct hal_ev_gatt_client_listen ev;
+
+	/* Server if because of typo in android headers */
+	ev.server_if = id;
+	ev.status = status;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_LISTEN,
+							sizeof(ev), &ev);
+}
+
+struct listen_data {
+	int32_t client_id;
+	bool start;
+};
+
+static struct listen_data *create_listen_data(int32_t client_id, bool start)
+{
+	struct listen_data *d;
+
+	d = new0(struct listen_data, 1);
+	d->client_id = client_id;
+	d->start = start;
+
+	return d;
+}
+
+static void set_advertising_cb(uint8_t status, void *user_data)
+{
+	struct listen_data *l = user_data;
+
+	send_client_listen_notify(l->client_id, status);
+
+	/* In case of success update advertising state*/
+	if (!status)
+		advertising_cnt = l->start ? 1 : 0;
+
+	/*
+	 * Let's remove client from the list in two cases
+	 * 1. Start failed
+	 * 2. Stop succeed
+	 */
+	if ((l->start && status) || (!l->start && !status))
+		queue_remove(listen_apps, INT_TO_PTR(l->client_id));
+
+	free(l);
+}
+
+static void handle_client_unregister(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_unregister *cmd = buf;
+	uint8_t status;
+	void *listening_client;
+	struct listen_data *data;
+
+	DBG("");
+
+	listening_client = queue_find(listen_apps, NULL,
+						INT_TO_PTR(cmd->client_if));
+
+	if (listening_client) {
+		advertising_cnt--;
+		queue_remove(listen_apps, INT_TO_PTR(cmd->client_if));
+	} else {
+		status = unregister_app(cmd->client_if);
+		goto reply;
+	}
+
+	if (!advertising_cnt) {
+		data = create_listen_data(cmd->client_if, false);
+
+		if (!bt_le_set_advertising(data->start, set_advertising_cb,
+								data)) {
+			error("gatt: Could not set advertising");
+			status = HAL_STATUS_FAILED;
+			free(data);
+			goto reply;
+		}
+	}
+
+	status = unregister_app(cmd->client_if);
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_UNREGISTER, status);
+}
+
+static uint8_t handle_connect(int32_t app_id, const bdaddr_t *addr, bool direct)
+{
+	struct app_connection conn_match;
+	struct app_connection *conn;
+	struct gatt_device *device;
+	struct gatt_app *app;
+
+	DBG("");
+
+	app = find_app_by_id(app_id);
+	if (!app)
+		return HAL_STATUS_FAILED;
+
+	device = find_device_by_addr(addr);
+	if (!device)
+		device = create_device(addr);
+
+	conn_match.device = device;
+	conn_match.app = app;
+
+	conn = queue_find(app_connections, match_connection_by_device_and_app,
+								&conn_match);
+	if (!conn) {
+		conn = create_connection(device, app);
+		if (!conn)
+			return HAL_STATUS_NOMEM;
+	}
+
+	if (!trigger_connection(conn, direct))
+		return HAL_STATUS_FAILED;
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void handle_client_connect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_connect *cmd = buf;
+	uint8_t status;
+	bdaddr_t addr;
+
+	DBG("is_direct:%u transport:%u", cmd->is_direct, cmd->transport);
+
+	android2bdaddr(&cmd->bdaddr, &addr);
+
+	/* TODO handle transport flag */
+
+	status = handle_connect(cmd->client_if, &addr, cmd->is_direct);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT,
+								status);
+}
+
+static void handle_client_disconnect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_disconnect *cmd = buf;
+	struct app_connection *conn;
+	uint8_t status;
+
+	DBG("");
+
+	/* TODO: should we care to match also bdaddr when conn_id is unique? */
+	conn = queue_remove_if(app_connections, match_connection_by_id,
+						INT_TO_PTR(cmd->conn_id));
+	destroy_connection(conn);
+
+	status = HAL_STATUS_SUCCESS;
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_DISCONNECT, status);
+}
+
+static void handle_client_listen(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_listen *cmd = buf;
+	uint8_t status;
+	struct listen_data *data;
+	bool req_sent = false;
+	void *listening_client;
+
+	DBG("");
+
+	if (!find_app_by_id(cmd->client_if)) {
+		error("gatt: Client not registered");
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	listening_client = queue_find(listen_apps, NULL,
+						INT_TO_PTR(cmd->client_if));
+	/* Start listening */
+	if (cmd->start) {
+		if (listening_client) {
+			status = HAL_STATUS_SUCCESS;
+			goto reply;
+		}
+
+		queue_push_tail(listen_apps, INT_TO_PTR(cmd->client_if));
+
+		/* If listen is already on just return success*/
+		if (advertising_cnt > 0) {
+			advertising_cnt++;
+			status = HAL_STATUS_SUCCESS;
+			goto reply;
+		}
+	} else {
+		/* Stop listening. Check if client was listening */
+		if (!listening_client) {
+			error("gatt: This client %d does not listen",
+							cmd->client_if);
+			status = HAL_STATUS_FAILED;
+			goto reply;
+		}
+
+		/*
+		 * In case there is more listening clients don't stop
+		 * advertising
+		 */
+		if (advertising_cnt > 1) {
+			advertising_cnt--;
+			queue_remove(listen_apps, INT_TO_PTR(cmd->client_if));
+			status = HAL_STATUS_SUCCESS;
+			goto reply;
+		}
+	}
+
+	data = create_listen_data(cmd->client_if, cmd->start);
+
+	if (!bt_le_set_advertising(cmd->start, set_advertising_cb, data)) {
+		error("gatt: Could not set advertising");
+		status = HAL_STATUS_FAILED;
+		free(data);
+		goto reply;
+	}
+
+	/*
+	 * Use this flag to keep in mind that we are waiting for callback with
+	 * result
+	 */
+	req_sent = true;
+
+	status = HAL_STATUS_SUCCESS;
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN,
+							status);
+
+	/* In case of early success or error, just send notification up */
+	if (!req_sent) {
+		int32_t gatt_status = status == HAL_STATUS_SUCCESS ?
+						GATT_SUCCESS : GATT_FAILURE;
+		send_client_listen_notify(cmd->client_if, gatt_status);
+	}
+}
+
+static void handle_client_refresh(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_refresh *cmd = buf;
+	struct gatt_device *dev;
+	uint8_t status;
+	bdaddr_t bda;
+
+	/*
+	 * This is Android's framework hidden API call. It seams that no
+	 * notification is expected and Bluedroid silently updates device's
+	 * cache under the hood. As we use lazy caching ,we can just clear the
+	 * cache and we're done.
+	 */
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &bda);
+	dev = find_device_by_addr(&bda);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	queue_remove_all(dev->services, NULL, NULL, destroy_service);
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH,
+									status);
+}
+
+static void handle_client_search_service(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_search_service *cmd = buf;
+	struct app_connection *conn;
+	uint8_t status;
+	struct service *s;
+	bt_uuid_t uuid;
+	guint srvc_search_success;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + (cmd->filtered ? 16 : 0)) {
+		error("Invalid search service size (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return;
+	}
+
+	conn = find_connection_by_id(cmd->conn_id);
+	if (!conn) {
+		error("gatt: dev with conn_id=%d not found", cmd->conn_id);
+
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	if (conn->device->state != DEVICE_CONNECTED) {
+		char bda[18];
+
+		ba2str(&conn->device->bdaddr, bda);
+		error("gatt: device %s not connected", bda);
+
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	if (cmd->filtered)
+		android2uuid(cmd->filter_uuid, &uuid);
+
+	/* Services not cached yet */
+	if (queue_isempty(conn->device->services)) {
+		if (cmd->filtered)
+			srvc_search_success = search_dev_for_srvc(conn, &uuid);
+		else
+			srvc_search_success = search_dev_for_srvc(conn, NULL);
+
+		if (!srvc_search_success) {
+			status = HAL_STATUS_FAILED;
+			goto reply;
+		}
+
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	/* Search in cached services for given service */
+	if (cmd->filtered) {
+		/* Search in cache for service by uuid */
+		s = queue_find(conn->device->services, match_srvc_by_bt_uuid,
+									&uuid);
+
+		if (s) {
+			send_client_primary_notify(s, INT_TO_PTR(conn->id));
+		} else {
+			if (!search_dev_for_srvc(conn, &uuid)) {
+				status = HAL_STATUS_FAILED;
+				goto reply;
+			}
+
+			status = HAL_STATUS_SUCCESS;
+			goto reply;
+		}
+	} else {
+		/* Refresh service cache if only partial search was performed */
+		if (conn->device->partial_srvc_search) {
+			srvc_search_success = search_dev_for_srvc(conn, NULL);
+			if (!srvc_search_success) {
+				status = HAL_STATUS_FAILED;
+				goto reply;
+			}
+		} else
+			queue_foreach(conn->device->services,
+						send_client_primary_notify,
+						INT_TO_PTR(cmd->conn_id));
+	}
+
+	send_client_search_complete_notify(GATT_SUCCESS, conn->id);
+
+	status = HAL_STATUS_SUCCESS;
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_SEARCH_SERVICE, status);
+}
+
+static void send_client_incl_service_notify(const struct element_id *srvc_id,
+						const struct service *incl,
+						int32_t conn_id)
+{
+	struct hal_ev_gatt_client_get_inc_service ev;
+
+	memset(&ev, 0, sizeof(ev));
+
+	ev.conn_id = conn_id;
+
+	element_id_to_hal_srvc_id(srvc_id, 1, &ev.srvc_id);
+
+	if (incl) {
+		element_id_to_hal_srvc_id(&incl->id, 0, &ev.incl_srvc_id);
+		ev.status = GATT_SUCCESS;
+	} else {
+		ev.status = GATT_FAILURE;
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT ,
+					HAL_EV_GATT_CLIENT_GET_INC_SERVICE,
+					sizeof(ev), &ev);
+}
+
+struct get_included_data {
+	struct service *prim;
+	struct app_connection *conn;
+};
+
+static int get_inst_id_of_prim_services(const struct gatt_device *dev)
+{
+	struct service *s = queue_peek_tail(dev->services);
+
+	if (s)
+		return s->id.instance;
+
+	return -1;
+}
+
+static void get_included_cb(uint8_t status, GSList *included, void *user_data)
+{
+	struct get_included_data *data = user_data;
+	struct app_connection *conn = data->conn;
+	struct service *service = data->prim;
+	struct service *incl = NULL;
+	int instance_id;
+
+	DBG("");
+
+	free(data);
+
+	if (status) {
+		error("gatt: no included services found");
+		goto failed;
+	}
+
+	/* Remember that we already search included services.*/
+	service->incl_search_done = true;
+
+	/*
+	 * There might be multiply services with same uuid. Therefore make sure
+	 * each service has unique instance id. Let's take the latest instance
+	 * id of primary service and start iterate included services from this
+	 * point.
+	 */
+	instance_id = get_inst_id_of_prim_services(conn->device);
+	if (instance_id < 0)
+		goto failed;
+
+	for (; included; included = included->next) {
+		struct gatt_included *included_service = included->data;
+
+		incl = create_service(++instance_id, false,
+							included_service->uuid,
+							included_service);
+		if (!incl)
+			continue;
+
+		/*
+		 * Lets keep included service on two queues.
+		 * 1. on services queue together with primary service
+		 * 2. on special queue inside primary service
+		 */
+		queue_push_tail(service->included, incl);
+		queue_push_tail(conn->device->services, incl);
+	}
+
+	/*
+	 * Notify upper layer about first included service.
+	 * Android framework will iterate for next one.
+	 */
+	incl = queue_peek_head(service->included);
+
+failed:
+	send_client_incl_service_notify(&service->id, incl, conn->id);
+}
+
+static void search_included_services(struct app_connection *conn,
+							struct service *service)
+{
+	struct get_included_data *data;
+	uint16_t start, end;
+
+	data = new0(struct get_included_data, 1);
+	data->prim = service;
+	data->conn = conn;
+
+	if (service->primary) {
+		start = service->prim.range.start;
+		end = service->prim.range.end;
+	} else {
+		start = service->incl.range.start;
+		end = service->incl.range.end;
+	}
+
+	gatt_find_included(conn->device->attrib, start, end, get_included_cb,
+									data);
+}
+
+static bool find_service(int32_t conn_id, struct element_id *service_id,
+					struct app_connection **connection,
+					struct service **service)
+{
+	struct service *srvc;
+	struct app_connection *conn;
+
+	conn = find_connection_by_id(conn_id);
+	if (!conn) {
+		error("gatt: conn_id=%d not found", conn_id);
+		return false;
+	}
+
+	srvc = queue_find(conn->device->services, match_srvc_by_element_id,
+								service_id);
+	if (!srvc) {
+		error("gatt: Service with inst_id: %d not found",
+							service_id->instance);
+		return false;
+	}
+
+	*connection = conn;
+	*service = srvc;
+
+	return true;
+}
+
+static void handle_client_get_included_service(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_get_included_service *cmd = buf;
+	struct app_connection *conn;
+	struct service *prim_service;
+	struct service *incl_service = NULL;
+	struct element_id match_id;
+	struct element_id srvc_id;
+	uint8_t status;
+
+	DBG("");
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+
+	if (len != sizeof(*cmd) +
+			(cmd->continuation ? sizeof(cmd->incl_srvc_id[0]) : 0)) {
+		error("Invalid get incl services size (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return;
+	}
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+	if (!find_service(cmd->conn_id, &match_id, &conn, &prim_service)) {
+		status = HAL_STATUS_FAILED;
+		goto notify;
+	}
+
+	if (!prim_service->incl_search_done) {
+		search_included_services(conn, prim_service);
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	/* Try to use cache here */
+	if (!cmd->continuation) {
+		incl_service = queue_peek_head(prim_service->included);
+	} else {
+		uint8_t inst_id = cmd->incl_srvc_id[0].inst_id;
+
+		incl_service = queue_find(prim_service->included,
+						match_srvc_by_higher_inst_id,
+						INT_TO_PTR(inst_id));
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+notify:
+	/*
+	 * In case of error in handling request we need to send event with
+	 * service id of cmd and gatt failure status.
+	 */
+	send_client_incl_service_notify(&srvc_id, incl_service, cmd->conn_id);
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE, status);
+}
+
+static void send_client_char_notify(const struct hal_gatt_srvc_id *service,
+					const struct hal_gatt_gatt_id *charac,
+					int32_t char_prop, int32_t conn_id)
+{
+	struct hal_ev_gatt_client_get_characteristic ev;
+
+	ev.conn_id = conn_id;
+
+	if (charac) {
+		memcpy(&ev.char_id, charac, sizeof(struct hal_gatt_gatt_id));
+		ev.char_prop = char_prop;
+		ev.status = GATT_SUCCESS;
+	} else {
+		memset(&ev.char_id, 0, sizeof(struct hal_gatt_gatt_id));
+		ev.char_prop = 0;
+		ev.status = GATT_FAILURE;
+	}
+
+	memcpy(&ev.srvc_id, service, sizeof(struct hal_gatt_srvc_id));
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC,
+					sizeof(ev), &ev);
+}
+
+static void convert_send_client_char_notify(const struct characteristic *ch,
+						int32_t conn_id,
+						const struct service *service)
+{
+	struct hal_gatt_srvc_id srvc;
+	struct hal_gatt_gatt_id charac;
+
+	element_id_to_hal_srvc_id(&service->id, service->primary, &srvc);
+
+	if (ch) {
+		element_id_to_hal_gatt_id(&ch->id, &charac);
+		send_client_char_notify(&srvc, &charac, ch->ch.properties,
+								conn_id);
+	} else {
+		send_client_char_notify(&srvc, NULL, 0, conn_id);
+	}
+}
+
+static void cache_all_srvc_chars(struct service *srvc, GSList *characteristics)
+{
+	uint16_t inst_id = 0;
+	bt_uuid_t uuid;
+
+	for (; characteristics; characteristics = characteristics->next) {
+		struct characteristic *ch;
+
+		ch = new0(struct characteristic, 1);
+		ch->descriptors = queue_new();
+
+		memcpy(&ch->ch, characteristics->data, sizeof(ch->ch));
+
+		bt_string_to_uuid(&uuid, ch->ch.uuid);
+		bt_uuid_to_uuid128(&uuid, &ch->id.uuid);
+
+		/*
+		 * For now we increment inst_id and use it as characteristic
+		 * handle
+		 */
+		ch->id.instance = ++inst_id;
+
+		/* Store end handle to use later for descriptors discovery */
+		if (characteristics->next) {
+			struct gatt_char *next = characteristics->next->data;
+
+			ch->end_handle = next->handle - 1;
+		} else {
+			ch->end_handle = srvc->primary ? srvc->prim.range.end :
+							srvc->incl.range.end;
+		}
+
+		DBG("attr handle = 0x%04x, end handle = 0x%04x uuid: %s",
+				ch->ch.handle, ch->end_handle, ch->ch.uuid);
+
+		queue_push_tail(srvc->chars, ch);
+	}
+}
+
+struct discover_char_data {
+	int32_t conn_id;
+	struct service *service;
+};
+
+static void discover_char_cb(uint8_t status, GSList *characteristics,
+								void *user_data)
+{
+	struct discover_char_data *data = user_data;
+	struct service *srvc = data->service;
+
+	if (status) {
+		error("gatt: Failed to get characteristics: %s",
+							att_ecode2str(status));
+		convert_send_client_char_notify(NULL, data->conn_id, srvc);
+		goto done;
+	}
+
+	if (queue_isempty(srvc->chars))
+		cache_all_srvc_chars(srvc, characteristics);
+
+	convert_send_client_char_notify(queue_peek_head(srvc->chars),
+							data->conn_id, srvc);
+
+done:
+	free(data);
+}
+
+static void handle_client_get_characteristic(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_get_characteristic *cmd = buf;
+	struct characteristic *ch;
+	struct element_id match_id;
+	struct app_connection *conn;
+	struct service *srvc;
+	uint8_t status;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + (cmd->continuation ? sizeof(cmd->char_id[0]) : 0)) {
+		error("Invalid get characteristic size (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return;
+	}
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+	if (!find_service(cmd->conn_id, &match_id, &conn, &srvc)) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	/* Discover all characteristics for services if not cached yet */
+	if (queue_isempty(srvc->chars)) {
+		struct discover_char_data *cb_data;
+		struct att_range range;
+
+		cb_data = new0(struct discover_char_data, 1);
+		cb_data->service = srvc;
+		cb_data->conn_id = conn->id;
+
+		range = srvc->primary ? srvc->prim.range : srvc->incl.range;
+
+		if (!gatt_discover_char(conn->device->attrib, range.start,
+						range.end, NULL,
+						discover_char_cb, cb_data)) {
+			free(cb_data);
+
+			status = HAL_STATUS_FAILED;
+			goto done;
+		}
+
+		status = HAL_STATUS_SUCCESS;
+		goto done;
+	}
+
+	if (cmd->continuation)
+		ch = queue_find(srvc->chars, match_char_by_higher_inst_id,
+					INT_TO_PTR(cmd->char_id[0].inst_id));
+	else
+		ch = queue_peek_head(srvc->chars);
+
+	convert_send_client_char_notify(ch, conn->id, srvc);
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	if (status != HAL_STATUS_SUCCESS)
+		send_client_char_notify(&cmd->srvc_id, NULL, 0, cmd->conn_id);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC, status);
+}
+
+static void send_client_descr_notify(int32_t status, int32_t conn_id,
+					bool primary,
+					const struct element_id *srvc,
+					const struct element_id *ch,
+					const struct element_id *opt_descr)
+{
+	struct hal_ev_gatt_client_get_descriptor ev;
+
+	memset(&ev, 0, sizeof(ev));
+
+	ev.status = status;
+	ev.conn_id = conn_id;
+
+	element_id_to_hal_srvc_id(srvc, primary, &ev.srvc_id);
+	element_id_to_hal_gatt_id(ch, &ev.char_id);
+
+	if (opt_descr)
+		element_id_to_hal_gatt_id(opt_descr, &ev.descr_id);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_GET_DESCRIPTOR, sizeof(ev), &ev);
+}
+
+struct discover_desc_data {
+	struct app_connection *conn;
+	struct service *srvc;
+	struct characteristic *ch;
+};
+
+static void gatt_discover_desc_cb(guint8 status, GSList *descs,
+							gpointer user_data)
+{
+	struct discover_desc_data *data = user_data;
+	struct app_connection *conn = data->conn;
+	struct service *srvc = data->srvc;
+	struct characteristic *ch = data->ch;
+	struct descriptor *descr;
+	int i = 0;
+
+	if (status != 0) {
+		error("Discover all characteristic descriptors failed [%s]: %s",
+					ch->ch.uuid, att_ecode2str(status));
+		goto reply;
+	}
+
+	for ( ; descs; descs = descs->next) {
+		struct gatt_desc *desc = descs->data;
+		bt_uuid_t uuid;
+
+		descr = new0(struct descriptor, 1);
+
+		bt_string_to_uuid(&uuid, desc->uuid);
+		bt_uuid_to_uuid128(&uuid, &descr->id.uuid);
+
+		descr->id.instance = ++i;
+		descr->handle = desc->handle;
+
+		DBG("attr handle = 0x%04x, uuid: %s", desc->handle, desc->uuid);
+
+		queue_push_tail(ch->descriptors, descr);
+	}
+
+reply:
+	descr = queue_peek_head(ch->descriptors);
+
+	send_client_descr_notify(status ? GATT_FAILURE : GATT_SUCCESS, conn->id,
+					srvc->primary, &srvc->id, &ch->id,
+					descr ? &descr->id : NULL);
+
+	free(data);
+}
+
+static bool build_descr_cache(struct app_connection *conn, struct service *srvc,
+						struct characteristic *ch)
+{
+	struct discover_desc_data *cb_data;
+	uint16_t start, end;
+
+	/* Clip range to given characteristic */
+	start = ch->ch.value_handle + 1;
+	end = ch->end_handle;
+
+	/* If there are no descriptors, notify with fail status. */
+	if (start > end)
+		return false;
+
+	cb_data = new0(struct discover_desc_data, 1);
+	cb_data->conn = conn;
+	cb_data->srvc = srvc;
+	cb_data->ch = ch;
+
+	if (!gatt_discover_desc(conn->device->attrib, start, end, NULL,
+					gatt_discover_desc_cb, cb_data)) {
+		free(cb_data);
+		return false;
+	}
+
+	return true;
+}
+
+static void handle_client_get_descriptor(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_get_descriptor *cmd = buf;
+	struct descriptor *descr = NULL;
+	struct characteristic *ch;
+	struct service *srvc;
+	struct element_id srvc_id;
+	struct element_id char_id;
+	struct app_connection *conn;
+	int32_t conn_id;
+	uint8_t primary;
+	uint8_t status;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) +
+			(cmd->continuation ? sizeof(cmd->descr_id[0]) : 0)) {
+		error("gatt: Invalid get descr command (%u bytes), terminating",
+									len);
+
+		raise(SIGTERM);
+		return;
+	}
+
+	conn_id = cmd->conn_id;
+	primary = cmd->srvc_id.is_primary;
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+	hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+
+	if (!find_service(conn_id, &srvc_id, &conn, &srvc)) {
+		error("gatt: Get descr. could not find service");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+	if (!ch) {
+		error("gatt: Get descr. could not find characteristic");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (queue_isempty(ch->descriptors)) {
+		if (build_descr_cache(conn, srvc, ch)) {
+			ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_DESCRIPTOR,
+					HAL_STATUS_SUCCESS);
+			return;
+		}
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+	/* Send from cache */
+	if (cmd->continuation)
+		descr = queue_find(ch->descriptors,
+					match_descr_by_higher_inst_id,
+					INT_TO_PTR(cmd->descr_id[0].inst_id));
+	else
+		descr = queue_peek_head(ch->descriptors);
+
+failed:
+	send_client_descr_notify(descr ? GATT_SUCCESS : GATT_FAILURE, conn_id,
+						primary, &srvc_id, &char_id,
+						&descr->id);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, status);
+}
+
+struct char_op_data {
+	int32_t conn_id;
+	const struct element_id *srvc_id;
+	const struct element_id *char_id;
+	uint8_t primary;
+};
+
+static struct char_op_data *create_char_op_data(int32_t conn_id,
+						const struct element_id *s_id,
+						const struct element_id *ch_id,
+						bool primary)
+{
+	struct char_op_data *d;
+
+	d = new0(struct char_op_data, 1);
+	d->conn_id = conn_id;
+	d->srvc_id = s_id;
+	d->char_id = ch_id;
+	d->primary = primary;
+
+	return d;
+}
+
+static void send_client_read_char_notify(int32_t status, const uint8_t *pdu,
+						uint16_t len, int32_t conn_id,
+						const struct element_id *s_id,
+						const struct element_id *ch_id,
+						uint8_t primary)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_gatt_client_read_characteristic *ev = (void *) buf;
+	ssize_t vlen;
+
+	memset(buf, 0, sizeof(buf));
+
+	ev->conn_id = conn_id;
+	ev->status = status;
+	ev->data.status = status;
+
+	element_id_to_hal_srvc_id(s_id, primary, &ev->data.srvc_id);
+	element_id_to_hal_gatt_id(ch_id, &ev->data.char_id);
+
+	if (status == 0 && pdu) {
+		vlen = dec_read_resp(pdu, len, ev->data.value, sizeof(buf));
+		if (vlen < 0) {
+			error("gatt: Protocol error");
+			ev->status = GATT_FAILURE;
+		} else {
+			ev->data.len = vlen;
+		}
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC,
+					sizeof(*ev) + ev->data.len, ev);
+}
+
+static void read_char_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct char_op_data *data = user_data;
+
+	send_client_read_char_notify(status, pdu, len, data->conn_id,
+						data->srvc_id, data->char_id,
+						data->primary);
+
+	free(data);
+}
+
+static int get_cid(struct gatt_device *dev)
+{
+	GIOChannel *io;
+	uint16_t cid;
+
+	io = g_attrib_get_channel(dev->attrib);
+
+	if (!bt_io_get(io, NULL, BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID)) {
+		error("gatt: Failed to get CID");
+		return -1;
+	}
+
+	return cid;
+}
+
+static int get_sec_level(struct gatt_device *dev)
+{
+	GIOChannel *io;
+	int sec_level;
+
+	io = g_attrib_get_channel(dev->attrib);
+
+	if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
+							BT_IO_OPT_INVALID)) {
+		error("gatt: Failed to get sec_level");
+		return -1;
+	}
+
+	return sec_level;
+}
+
+static bool set_security(struct gatt_device *device, int req_sec_level)
+{
+	int sec_level;
+	GError *gerr = NULL;
+	GIOChannel *io;
+
+	sec_level = get_sec_level(device);
+	if (sec_level < 0)
+		return false;
+
+	if (req_sec_level <= sec_level)
+		return true;
+
+	io = g_attrib_get_channel(device->attrib);
+	if (!io)
+		return false;
+
+	bt_io_set(io, &gerr, BT_IO_OPT_SEC_LEVEL, req_sec_level,
+							BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("gatt: Failed to set security level: %s", gerr->message);
+		g_error_free(gerr);
+		return false;
+	}
+
+	return true;
+}
+
+bool bt_gatt_set_security(const bdaddr_t *bdaddr, int sec_level)
+{
+	struct gatt_device *device;
+
+	device = find_device_by_addr(bdaddr);
+	if (!device)
+		return false;
+
+	return set_security(device, sec_level);
+}
+
+static bool set_auth_type(struct gatt_device *device, int auth_type)
+{
+	int sec_level;
+
+	switch (auth_type) {
+	case HAL_GATT_AUTHENTICATION_MITM:
+		sec_level = BT_SECURITY_HIGH;
+		break;
+	case HAL_GATT_AUTHENTICATION_NO_MITM:
+		sec_level = BT_SECURITY_MEDIUM;
+		break;
+	case HAL_GATT_AUTHENTICATION_NONE:
+		sec_level = BT_SECURITY_LOW;
+		break;
+	default:
+		error("gatt: Invalid auth_type value: %d", auth_type);
+		return false;
+	}
+
+	return set_security(device, sec_level);
+}
+
+static void handle_client_read_characteristic(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_read_characteristic *cmd = buf;
+	struct char_op_data *cb_data;
+	struct characteristic *ch;
+	struct app_connection *conn;
+	struct service *srvc;
+	struct element_id srvc_id;
+	struct element_id char_id;
+	uint8_t status;
+
+	DBG("");
+
+	/* TODO authorization needs to be handled */
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+	hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+
+	if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	/* search characteristics by element id */
+	ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+	if (!ch) {
+		error("gatt: Characteristic with inst_id: %d not found",
+							cmd->char_id.inst_id);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	cb_data = create_char_op_data(cmd->conn_id, &srvc->id, &ch->id,
+						cmd->srvc_id.is_primary);
+
+	if (!set_auth_type(conn->device, cmd->auth_req)) {
+		error("gatt: Failed to set security %d", cmd->auth_req);
+		status = HAL_STATUS_FAILED;
+		free(cb_data);
+		goto failed;
+	}
+
+	if (!gatt_read_char(conn->device->attrib, ch->ch.value_handle,
+						read_char_cb, cb_data)) {
+		error("gatt: Cannot read characteristic with inst_id: %d",
+							cmd->char_id.inst_id);
+		status = HAL_STATUS_FAILED;
+		free(cb_data);
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC, status);
+
+	/*
+	 * We should send notification with service, characteristic id in case
+	 * of errors.
+	 */
+	if (status != HAL_STATUS_SUCCESS)
+		send_client_read_char_notify(GATT_FAILURE, NULL, 0,
+						cmd->conn_id, &srvc_id,
+						&char_id,
+						cmd->srvc_id.is_primary);
+}
+
+static void send_client_write_char_notify(int32_t status, int32_t conn_id,
+					const struct element_id *srvc_id,
+					const struct element_id *char_id,
+					uint8_t primary)
+{
+	struct hal_ev_gatt_client_write_characteristic ev;
+
+	memset(&ev, 0, sizeof(ev));
+
+	ev.conn_id = conn_id;
+	ev.status = status;
+	ev.data.status = status;
+
+	element_id_to_hal_srvc_id(srvc_id, primary, &ev.data.srvc_id);
+	element_id_to_hal_gatt_id(char_id, &ev.data.char_id);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC,
+					sizeof(ev), &ev);
+}
+
+static void write_char_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct char_op_data *data = user_data;
+
+	send_client_write_char_notify(status, data->conn_id, data->srvc_id,
+						data->char_id, data->primary);
+
+	free(data);
+}
+
+static guint signed_write_cmd(struct gatt_device *dev, uint16_t handle,
+					const uint8_t *value, uint16_t vlen)
+{
+	uint8_t csrk[16];
+	uint32_t sign_cnt;
+	guint res;
+
+	memset(csrk, 0, 16);
+
+	if (!bt_get_csrk(&dev->bdaddr, true, csrk, &sign_cnt, NULL)) {
+		error("gatt: Could not get csrk key");
+		return 0;
+	}
+
+	res = gatt_signed_write_cmd(dev->attrib, handle, value, vlen, crypto,
+						csrk, sign_cnt, NULL, NULL);
+	if (!res) {
+		error("gatt: Signed write command failed");
+		return 0;
+	}
+
+	bt_update_sign_counter(&dev->bdaddr, true, ++sign_cnt);
+
+	return res;
+}
+
+static void handle_client_write_characteristic(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_write_characteristic *cmd = buf;
+	struct char_op_data *cb_data = NULL;
+	struct characteristic *ch;
+	struct app_connection *conn;
+	struct service *srvc;
+	struct element_id srvc_id;
+	struct element_id char_id;
+	uint8_t status;
+	guint res;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid write char size (%u bytes), terminating", len);
+		raise(SIGTERM);
+		return;
+	}
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+	hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+
+	if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	/* search characteristics by instance id */
+	ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+	if (!ch) {
+		error("gatt: Characteristic with inst_id: %d not found",
+							cmd->char_id.inst_id);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (cmd->write_type == GATT_WRITE_TYPE_PREPARE ||
+				cmd->write_type == GATT_WRITE_TYPE_DEFAULT) {
+		cb_data = create_char_op_data(cmd->conn_id, &srvc->id, &ch->id,
+						cmd->srvc_id.is_primary);
+	}
+
+	if (!set_auth_type(conn->device, cmd->auth_req)) {
+		error("gatt: Failed to set security %d", cmd->auth_req);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	switch (cmd->write_type) {
+	case GATT_WRITE_TYPE_NO_RESPONSE:
+		res = gatt_write_cmd(conn->device->attrib, ch->ch.value_handle,
+							cmd->value, cmd->len,
+							NULL, NULL);
+		break;
+	case GATT_WRITE_TYPE_PREPARE:
+		res = gatt_reliable_write_char(conn->device->attrib,
+							ch->ch.value_handle,
+							cmd->value, cmd->len,
+							write_char_cb, cb_data);
+		break;
+	case GATT_WRITE_TYPE_DEFAULT:
+		res = gatt_write_char(conn->device->attrib, ch->ch.value_handle,
+							cmd->value, cmd->len,
+							write_char_cb, cb_data);
+		break;
+	case GATT_WRITE_TYPE_SIGNED:
+		if (get_cid(conn->device) != ATT_CID) {
+			error("gatt: Cannot write signed on BR/EDR bearer");
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		if (get_sec_level(conn->device) > BT_SECURITY_LOW)
+			res = gatt_write_cmd(conn->device->attrib,
+						ch->ch.value_handle, cmd->value,
+						cmd->len, NULL, NULL);
+		else
+			res = signed_write_cmd(conn->device,
+						ch->ch.value_handle, cmd->value,
+						cmd->len);
+		break;
+	default:
+		error("gatt: Write type %d unsupported", cmd->write_type);
+		status = HAL_STATUS_UNSUPPORTED;
+		goto failed;
+	}
+
+	if (!res) {
+		error("gatt: Cannot write char. with inst_id: %d",
+							cmd->char_id.inst_id);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC, status);
+
+	/*
+	 * We should send notification with service, characteristic id in case
+	 * of error and write with no response
+	 */
+	if (status != HAL_STATUS_SUCCESS ||
+			cmd->write_type == GATT_WRITE_TYPE_NO_RESPONSE ||
+			cmd->write_type == GATT_WRITE_TYPE_SIGNED) {
+		int32_t gatt_status = (status == HAL_STATUS_SUCCESS) ?
+						GATT_SUCCESS : GATT_FAILURE;
+
+		send_client_write_char_notify(gatt_status, cmd->conn_id,
+						&srvc_id, &char_id,
+						cmd->srvc_id.is_primary);
+		free(cb_data);
+	}
+}
+
+static void send_client_descr_read_notify(int32_t status, const uint8_t *pdu,
+						guint16 len, int32_t conn_id,
+						const struct element_id *srvc,
+						const struct element_id *ch,
+						const struct element_id *descr,
+						uint8_t primary)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_gatt_client_read_descriptor *ev = (void *) buf;
+
+	memset(buf, 0, sizeof(buf));
+
+	ev->status = status;
+	ev->conn_id = conn_id;
+	ev->data.status = ev->status;
+
+	element_id_to_hal_srvc_id(srvc, primary, &ev->data.srvc_id);
+	element_id_to_hal_gatt_id(ch, &ev->data.char_id);
+	element_id_to_hal_gatt_id(descr, &ev->data.descr_id);
+
+	if (status == 0 && pdu) {
+		ssize_t ret;
+
+		ret = dec_read_resp(pdu, len, ev->data.value,
+							GATT_MAX_ATTR_LEN);
+		if (ret < 0) {
+			error("gatt: Protocol error");
+			ev->status = GATT_FAILURE;
+		} else {
+			ev->data.len = ret;
+		}
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_EV_GATT_CLIENT_READ_DESCRIPTOR,
+					sizeof(*ev) + ev->data.len, ev);
+}
+
+struct desc_data {
+	int32_t conn_id;
+	const struct element_id *srvc_id;
+	const struct element_id *char_id;
+	const struct element_id *descr_id;
+	uint8_t primary;
+};
+
+static void read_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct desc_data *cb_data = user_data;
+
+	if (status != 0)
+		error("gatt: Discover all char descriptors failed: %s",
+							att_ecode2str(status));
+
+	send_client_descr_read_notify(status, pdu, len, cb_data->conn_id,
+					cb_data->srvc_id, cb_data->char_id,
+					cb_data->descr_id, cb_data->primary);
+
+	free(cb_data);
+}
+
+static struct desc_data *create_desc_data(int32_t conn_id,
+						const struct element_id *s_id,
+						const struct element_id *ch_id,
+						const struct element_id *d_id,
+						uint8_t primary)
+{
+	struct desc_data *d;
+
+	d = new0(struct desc_data, 1);
+	d->conn_id = conn_id;
+	d->srvc_id = s_id;
+	d->char_id = ch_id;
+	d->descr_id = d_id;
+	d->primary = primary;
+
+	return d;
+}
+
+static void handle_client_read_descriptor(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_read_descriptor *cmd = buf;
+	struct desc_data *cb_data;
+	struct characteristic *ch;
+	struct descriptor *descr;
+	struct service *srvc;
+	struct element_id char_id;
+	struct element_id descr_id;
+	struct element_id srvc_id;
+	struct app_connection *conn;
+	int32_t conn_id = 0;
+	uint8_t primary;
+	uint8_t status;
+
+	DBG("");
+
+	conn_id = cmd->conn_id;
+	primary = cmd->srvc_id.is_primary;
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+	hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+	hal_gatt_id_to_element_id(&cmd->descr_id, &descr_id);
+
+	if (!find_service(conn_id, &srvc_id, &conn, &srvc)) {
+		error("gatt: Read descr. could not find service");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+	if (!ch) {
+		error("gatt: Read descr. could not find characteristic");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	descr = queue_find(ch->descriptors, match_descr_by_element_id,
+								&descr_id);
+	if (!descr) {
+		error("gatt: Read descr. could not find descriptor");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	cb_data = create_desc_data(conn_id, &srvc->id, &ch->id, &descr->id,
+								primary);
+
+	if (!set_auth_type(conn->device, cmd->auth_req)) {
+		error("gatt: Failed to set security %d", cmd->auth_req);
+		status = HAL_STATUS_FAILED;
+		free(cb_data);
+		goto failed;
+	}
+
+	if (!gatt_read_char(conn->device->attrib, descr->handle, read_desc_cb,
+								cb_data)) {
+		free(cb_data);
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	if (status != HAL_STATUS_SUCCESS)
+		send_client_descr_read_notify(GATT_FAILURE, NULL, 0, conn_id,
+						&srvc_id, &char_id, &descr_id,
+						primary);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, status);
+}
+
+static void send_client_descr_write_notify(int32_t status, int32_t conn_id,
+						const struct element_id *srvc,
+						const struct element_id *ch,
+						const struct element_id *descr,
+						uint8_t primary) {
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_gatt_client_write_descriptor *ev = (void *) buf;
+
+	memset(buf, 0, sizeof(buf));
+
+	ev->status = status;
+	ev->conn_id = conn_id;
+
+	element_id_to_hal_srvc_id(srvc, primary, &ev->data.srvc_id);
+	element_id_to_hal_gatt_id(ch, &ev->data.char_id);
+	element_id_to_hal_gatt_id(descr, &ev->data.descr_id);
+	ev->data.status = status;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR,
+					sizeof(*ev), ev);
+}
+
+static void write_descr_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct desc_data *cb_data = user_data;
+
+	if (status)
+		error("gatt: Write descriptors failed: %s",
+							att_ecode2str(status));
+
+	send_client_descr_write_notify(status, cb_data->conn_id,
+					cb_data->srvc_id, cb_data->char_id,
+					cb_data->descr_id, cb_data->primary);
+
+	free(cb_data);
+}
+
+static void handle_client_write_descriptor(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_write_descriptor *cmd = buf;
+	struct desc_data *cb_data = NULL;
+	struct characteristic *ch;
+	struct descriptor *descr;
+	struct service *srvc;
+	struct element_id srvc_id;
+	struct element_id char_id;
+	struct element_id descr_id;
+	struct app_connection *conn;
+	int32_t conn_id;
+	uint8_t primary;
+	uint8_t status;
+	guint res;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid write desriptor command (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return;
+	}
+
+	primary = cmd->srvc_id.is_primary;
+	conn_id = cmd->conn_id;
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+	hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+	hal_gatt_id_to_element_id(&cmd->descr_id, &descr_id);
+
+	if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) {
+		error("gatt: Write descr. could not find service");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+	if (!ch) {
+		error("gatt: Write descr. could not find characteristic");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	descr = queue_find(ch->descriptors, match_descr_by_element_id,
+								&descr_id);
+	if (!descr) {
+		error("gatt: Write descr. could not find descriptor");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (cmd->write_type != GATT_WRITE_TYPE_NO_RESPONSE)
+		cb_data = create_desc_data(conn_id, &srvc->id, &ch->id,
+							&descr->id, primary);
+
+	if (!set_auth_type(conn->device, cmd->auth_req)) {
+		error("gatt: Failed to set security %d", cmd->auth_req);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	switch (cmd->write_type) {
+	case GATT_WRITE_TYPE_NO_RESPONSE:
+		res = gatt_write_cmd(conn->device->attrib, descr->handle,
+					cmd->value, cmd->len, NULL , NULL);
+		break;
+	case GATT_WRITE_TYPE_PREPARE:
+		res = gatt_reliable_write_char(conn->device->attrib,
+						descr->handle, cmd->value,
+						cmd->len, write_descr_cb,
+						cb_data);
+		break;
+	case GATT_WRITE_TYPE_DEFAULT:
+		res = gatt_write_char(conn->device->attrib, descr->handle,
+						cmd->value, cmd->len,
+						write_descr_cb, cb_data);
+		break;
+	default:
+		error("gatt: Write type %d unsupported", cmd->write_type);
+		status = HAL_STATUS_UNSUPPORTED;
+		goto failed;
+	}
+
+	if (!res) {
+		error("gatt: Write desc, could not write desc");
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	if (status != HAL_STATUS_SUCCESS ||
+			cmd->write_type == GATT_WRITE_TYPE_NO_RESPONSE) {
+		int32_t gatt_status = (status == HAL_STATUS_SUCCESS) ?
+						GATT_SUCCESS : GATT_FAILURE;
+
+		send_client_descr_write_notify(gatt_status, conn_id, &srvc_id,
+						&char_id, &descr_id, primary);
+		free(cb_data);
+	}
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR, status);
+}
+
+static void send_client_write_execute_notify(int32_t id, int32_t status)
+{
+	struct hal_ev_gatt_client_exec_write ev;
+
+	ev.conn_id = id;
+	ev.status = status;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_EV_GATT_CLIENT_EXEC_WRITE,
+					sizeof(ev), &ev);
+}
+
+static void write_execute_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	send_client_write_execute_notify(PTR_TO_INT(user_data), status);
+}
+
+static void handle_client_execute_write(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_execute_write *cmd = buf;
+	struct app_connection *conn;
+	uint8_t status;
+	uint8_t flags;
+
+	DBG("");
+
+	conn = find_connection_by_id(cmd->conn_id);
+	if (!conn) {
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	flags = cmd->execute ? ATT_WRITE_ALL_PREP_WRITES :
+						ATT_CANCEL_ALL_PREP_WRITES;
+
+	if (!gatt_execute_write(conn->device->attrib, flags, write_execute_cb,
+						INT_TO_PTR(cmd->conn_id))) {
+		error("gatt: Could not send execute write");
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_EXECUTE_WRITE, status);
+
+	/* In case of early error send also notification.*/
+	if (status != HAL_STATUS_SUCCESS)
+		send_client_write_execute_notify(cmd->conn_id, GATT_FAILURE);
+}
+
+static void handle_notification(const uint8_t *pdu, uint16_t len,
+							gpointer user_data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_gatt_client_notify *ev = (void *) buf;
+	struct notification_data *notification = user_data;
+	uint8_t data_offset = sizeof(uint8_t) + sizeof(uint16_t);
+
+	if (len < data_offset)
+		return;
+
+	memcpy(&ev->char_id, &notification->ch, sizeof(ev->char_id));
+	memcpy(&ev->srvc_id, &notification->service, sizeof(ev->srvc_id));
+	bdaddr2android(&notification->conn->device->bdaddr, &ev->bda);
+	ev->conn_id = notification->conn->id;
+	ev->is_notify = pdu[0] == ATT_OP_HANDLE_NOTIFY;
+
+	/* We have to cut opcode and handle from data */
+	ev->len = len - data_offset;
+	memcpy(ev->value, pdu + data_offset, len - data_offset);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_NOTIFY,
+						sizeof(*ev) + ev->len, ev);
+}
+
+static void send_register_for_notification_ev(int32_t id, int32_t registered,
+					int32_t status,
+					const struct hal_gatt_srvc_id *srvc,
+					const struct hal_gatt_gatt_id *ch)
+{
+	struct hal_ev_gatt_client_reg_for_notif ev;
+
+	ev.conn_id = id;
+	ev.status = status;
+	ev.registered = registered;
+	memcpy(&ev.srvc_id, srvc, sizeof(ev.srvc_id));
+	memcpy(&ev.char_id, ch, sizeof(ev.char_id));
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF, sizeof(ev), &ev);
+}
+
+static void handle_client_register_for_notification(const void *buf,
+								uint16_t len)
+{
+	const struct hal_cmd_gatt_client_register_for_notification *cmd = buf;
+	struct notification_data *notification;
+	struct characteristic *c;
+	struct element_id match_id;
+	struct app_connection *conn;
+	int32_t conn_id = 0;
+	struct service *service;
+	uint8_t status;
+	int32_t gatt_status;
+	bdaddr_t addr;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &addr);
+
+	conn = find_conn(&addr, cmd->client_if);
+	if (!conn) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	conn_id = conn->id;
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+	service = queue_find(conn->device->services, match_srvc_by_element_id,
+								&match_id);
+	if (!service) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	hal_gatt_id_to_element_id(&cmd->char_id, &match_id);
+	c = queue_find(service->chars, match_char_by_element_id, &match_id);
+	if (!c) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	notification = new0(struct notification_data, 1);
+
+	memcpy(&notification->ch, &cmd->char_id, sizeof(notification->ch));
+	memcpy(&notification->service, &cmd->srvc_id,
+						sizeof(notification->service));
+	notification->conn = conn;
+
+	if (queue_find(conn->app->notifications, match_notification,
+								notification)) {
+		free(notification);
+		status = HAL_STATUS_SUCCESS;
+		goto failed;
+	}
+
+	notification->notif_id = g_attrib_register(conn->device->attrib,
+							ATT_OP_HANDLE_NOTIFY,
+							c->ch.value_handle,
+							handle_notification,
+							notification,
+							destroy_notification);
+	if (!notification->notif_id) {
+		free(notification);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	notification->ind_id = g_attrib_register(conn->device->attrib,
+							ATT_OP_HANDLE_IND,
+							c->ch.value_handle,
+							handle_notification,
+							notification,
+							destroy_notification);
+	if (!notification->ind_id) {
+		g_attrib_unregister(conn->device->attrib,
+							notification->notif_id);
+		free(notification);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	/*
+	 * Because same data - notification - is shared by two handlers, we
+	 * introduce ref counter to be sure that data can be freed with no risk.
+	 * Counter is decremented in destroy_notification.
+	 */
+	notification->ref = 2;
+
+	queue_push_tail(conn->app->notifications, notification);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	gatt_status = status ? GATT_FAILURE : GATT_SUCCESS;
+	send_register_for_notification_ev(conn_id, 1, gatt_status,
+						&cmd->srvc_id, &cmd->char_id);
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION, status);
+}
+
+static void handle_client_deregister_for_notification(const void *buf,
+								uint16_t len)
+{
+	const struct hal_cmd_gatt_client_deregister_for_notification *cmd = buf;
+	struct notification_data *notification, notif;
+	struct app_connection *conn;
+	int32_t conn_id = 0;
+	uint8_t status;
+	int32_t gatt_status;
+	bdaddr_t addr;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &addr);
+
+	conn = find_conn(&addr, cmd->client_if);
+	if (!conn) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	conn_id = conn->id;
+
+	memcpy(&notif.ch, &cmd->char_id, sizeof(notif.ch));
+	memcpy(&notif.service, &cmd->srvc_id, sizeof(notif.service));
+	notif.conn = conn;
+
+	notification = queue_find(conn->app->notifications,
+						match_notification, &notif);
+	if (!notification) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	unregister_notification(notification);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	gatt_status = status ? GATT_FAILURE : GATT_SUCCESS;
+	send_register_for_notification_ev(conn_id, 0, gatt_status,
+						&cmd->srvc_id, &cmd->char_id);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION, status);
+}
+
+static void send_client_remote_rssi_notify(int32_t client_if,
+						const bdaddr_t *addr,
+						int32_t rssi, int32_t status)
+{
+	struct hal_ev_gatt_client_read_remote_rssi ev;
+
+	ev.client_if = client_if;
+	bdaddr2android(addr, &ev.address);
+	ev.rssi = rssi;
+	ev.status = status;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI, sizeof(ev), &ev);
+}
+
+static void read_remote_rssi_cb(uint8_t status, const bdaddr_t *addr,
+						int8_t rssi, void *user_data)
+{
+	int32_t client_if = PTR_TO_INT(user_data);
+	int32_t gatt_status = status ? GATT_FAILURE : GATT_SUCCESS;
+
+	send_client_remote_rssi_notify(client_if, addr, rssi, gatt_status);
+}
+
+static void handle_client_read_remote_rssi(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_read_remote_rssi *cmd = buf;
+	uint8_t status;
+	bdaddr_t bdaddr;
+
+	DBG("");
+
+	if (!find_app_by_id(cmd->client_if)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+	if (!bt_read_device_rssi(&bdaddr, read_remote_rssi_cb,
+						INT_TO_PTR(cmd->client_if))) {
+		error("gatt: Could not read RSSI");
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI, status);
+
+	if (status != HAL_STATUS_SUCCESS)
+		send_client_remote_rssi_notify(cmd->client_if, &bdaddr, 0,
+								GATT_FAILURE);
+}
+
+static void handle_client_get_device_type(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_get_device_type *cmd = buf;
+	struct hal_rsp_gatt_client_get_device_type rsp;
+	bdaddr_t bdaddr;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	rsp.type = bt_get_device_android_type(&bdaddr);
+
+	ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE,
+					sizeof(rsp), &rsp, -1);
+}
+
+static void handle_client_set_adv_data(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_set_adv_data *cmd = buf;
+	uint8_t status;
+
+	if (len != sizeof(*cmd) + cmd->manufacturer_len) {
+		error("Invalid set adv data command (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return;
+	}
+
+	DBG("scan_rsp=%u name=%u tx=%u min=%d max=%d app=%d",
+		cmd->set_scan_rsp, cmd->include_name, cmd->include_txpower,
+		cmd->min_interval, cmd->max_interval, cmd->appearance);
+
+	DBG("manufacturer=%u service_data=%u service_uuid=%u",
+				cmd->manufacturer_len, cmd->service_data_len,
+				cmd->service_uuid_len);
+
+	/* TODO This should be implemented when kernel supports it */
+	if (cmd->manufacturer_len || cmd->service_data_len ||
+							cmd->service_uuid_len) {
+		error("gatt: Extra advertising data not supported");
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_SET_ADV_DATA, status);
+}
+
+static void test_command_result(guint8 status, const guint8 *pdu,
+					guint16 len, gpointer user_data)
+{
+	DBG("status: %d", status);
+}
+
+static uint8_t test_read_write(bdaddr_t *bdaddr, bt_uuid_t *uuid, uint16_t op,
+						uint16_t u2, uint16_t u3,
+						uint16_t u4, uint16_t u5)
+{
+	guint16 length = 0;
+	struct gatt_device *dev;
+	uint8_t *pdu;
+	size_t mtu;
+
+	dev = find_device_by_addr(bdaddr);
+	if (!dev || dev->state != DEVICE_CONNECTED)
+		return HAL_STATUS_FAILED;
+
+	pdu = g_attrib_get_buffer(dev->attrib, &mtu);
+	if (!pdu)
+		return HAL_STATUS_FAILED;
+
+	switch (op) {
+	case ATT_OP_READ_REQ:
+		length = enc_read_req(u2, pdu, mtu);
+		break;
+	case ATT_OP_READ_BY_TYPE_REQ:
+		length = enc_read_by_type_req(u2, u3, uuid, pdu, mtu);
+		break;
+	case ATT_OP_READ_BLOB_REQ:
+		length = enc_read_blob_req(u2, u3, pdu, mtu);
+		break;
+	case ATT_OP_READ_BY_GROUP_REQ:
+		length = enc_read_by_grp_req(u2, u3, uuid, pdu, mtu);
+		break;
+	case ATT_OP_READ_MULTI_REQ:
+		return HAL_STATUS_UNSUPPORTED;
+	case ATT_OP_WRITE_REQ:
+		length = enc_write_req(u2, (uint8_t *) &u3, sizeof(u3), pdu,
+									mtu);
+		break;
+	case ATT_OP_WRITE_CMD:
+		length = enc_write_cmd(u2, (uint8_t *) &u3, sizeof(u3), pdu,
+									mtu);
+		break;
+	case ATT_OP_PREP_WRITE_REQ:
+		length = enc_prep_write_req(u2, u3, (uint8_t *) &u4, sizeof(u4),
+								pdu, mtu);
+		break;
+	case ATT_OP_EXEC_WRITE_REQ:
+		length = enc_exec_write_req(u2, pdu, mtu);
+		break;
+	case ATT_OP_SIGNED_WRITE_CMD:
+		if (signed_write_cmd(dev, u2, (uint8_t *) &u3, sizeof(u3)))
+			return HAL_STATUS_SUCCESS;
+		else
+			return HAL_STATUS_FAILED;
+	default:
+		error("gatt: Unknown operation type");
+
+		return HAL_STATUS_UNSUPPORTED;
+	}
+
+	if (!g_attrib_send(dev->attrib, 0, pdu, length, test_command_result,
+								NULL, NULL))
+		return HAL_STATUS_FAILED;
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t test_increase_security(bdaddr_t *bdaddr, uint16_t u1)
+{
+	struct gatt_device *device;
+
+	device = find_device_by_addr(bdaddr);
+	if (!device)
+		return HAL_STATUS_FAILED;
+
+	if (!set_auth_type(device, u1))
+		return HAL_STATUS_FAILED;
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void handle_client_test_command(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_test_command *cmd = buf;
+	struct gatt_app *app;
+	bdaddr_t bdaddr;
+	bt_uuid_t uuid;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bda1, &bdaddr);
+	android2uuid(cmd->uuid1, &uuid);
+
+	switch (cmd->command) {
+	case GATT_CLIENT_TEST_CMD_ENABLE:
+		if (cmd->u1) {
+			if (!test_client_if) {
+				app = register_app(TEST_UUID, GATT_CLIENT);
+				if (app)
+					test_client_if = app->id;
+			}
+
+			if (test_client_if)
+				status = HAL_STATUS_SUCCESS;
+			else
+				status = HAL_STATUS_FAILED;
+		} else {
+			status = unregister_app(test_client_if);
+			test_client_if = 0;
+		}
+		break;
+	case GATT_CLIENT_TEST_CMD_CONNECT:
+		/* TODO u1 holds device type, for now assume BLE */
+		status = handle_connect(test_client_if, &bdaddr, false);
+		break;
+	case GATT_CLIENT_TEST_CMD_DISCONNECT:
+		app = queue_find(gatt_apps, match_app_by_id,
+						INT_TO_PTR(test_client_if));
+		queue_remove_all(app_connections, match_connection_by_app, app,
+							destroy_connection);
+
+		status = HAL_STATUS_SUCCESS;
+		break;
+	case GATT_CLIENT_TEST_CMD_DISCOVER:
+		status = HAL_STATUS_FAILED;
+		break;
+	case GATT_CLIENT_TEST_CMD_READ:
+	case GATT_CLIENT_TEST_CMD_WRITE:
+		status = test_read_write(&bdaddr, &uuid, cmd->u1, cmd->u2,
+						cmd->u3, cmd->u4, cmd->u5);
+		break;
+	case GATT_CLIENT_TEST_CMD_INCREASE_SECURITY:
+		status = test_increase_security(&bdaddr, cmd->u1);
+		break;
+	case GATT_CLIENT_TEST_CMD_PAIRING_CONFIG:
+	default:
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_TEST_COMMAND, status);
+}
+
+static void handle_server_register(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_register *cmd = buf;
+	struct hal_ev_gatt_server_register ev;
+	struct gatt_app *app;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	app = register_app(cmd->uuid, GATT_SERVER);
+
+	if (app) {
+		ev.server_if = app->id;
+		ev.status = GATT_SUCCESS;
+	} else {
+		ev.status = GATT_FAILURE;
+	}
+
+	memcpy(ev.uuid, cmd->uuid, sizeof(ev.uuid));
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_EV_GATT_SERVER_REGISTER, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER,
+							HAL_STATUS_SUCCESS);
+}
+
+static void handle_server_unregister(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_unregister *cmd = buf;
+	uint8_t status;
+
+	DBG("");
+
+	status = unregister_app(cmd->server_if);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_UNREGISTER, status);
+}
+
+static void handle_server_connect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_connect *cmd = buf;
+	uint8_t status;
+	bdaddr_t addr;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &addr);
+
+	/* TODO: Handle transport flag */
+
+	status = handle_connect(cmd->server_if, &addr, cmd->is_direct);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT,
+								status);
+}
+
+static void handle_server_disconnect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_disconnect *cmd = buf;
+	struct app_connection *conn;
+	uint8_t status;
+
+	DBG("");
+
+	/* TODO: should we care to match also bdaddr when conn_id is unique? */
+	conn = queue_remove_if(app_connections, match_connection_by_id,
+						INT_TO_PTR(cmd->conn_id));
+	destroy_connection(conn);
+
+	status = HAL_STATUS_SUCCESS;
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_DISCONNECT, status);
+}
+
+static void handle_server_add_service(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_add_service *cmd = buf;
+	struct hal_ev_gatt_server_service_added ev;
+	struct gatt_app *server;
+	struct gatt_db_attribute *service;
+	uint8_t status;
+	bt_uuid_t uuid;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	server = find_app_by_id(cmd->server_if);
+	if (!server) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	android2uuid(cmd->srvc_id.uuid, &uuid);
+
+	service = gatt_db_add_service(gatt_db, &uuid, cmd->srvc_id.is_primary,
+							cmd->num_handles);
+	if (!service) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ev.srvc_handle = gatt_db_attribute_get_handle(service);
+	if (!ev.srvc_handle) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+	ev.srvc_id = cmd->srvc_id;
+	ev.server_if = cmd->server_if;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_SERVICE_ADDED, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_ADD_SERVICE, status);
+}
+
+static void handle_server_add_included_service(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_add_inc_service *cmd = buf;
+	struct hal_ev_gatt_server_inc_srvc_added ev;
+	struct gatt_app *server;
+	struct gatt_db_attribute *service, *include;
+	uint8_t status;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	server = find_app_by_id(cmd->server_if);
+	if (!server) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	service = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+	if (!service) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	include = gatt_db_get_attribute(gatt_db, cmd->included_handle);
+	if (!include) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	service = gatt_db_service_add_included(service, include);
+	if (!service) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ev.incl_srvc_handle = gatt_db_attribute_get_handle(service);
+	status = HAL_STATUS_SUCCESS;
+failed:
+	ev.srvc_handle = cmd->service_handle;
+	ev.status = status;
+	ev.server_if = cmd->server_if;
+	ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_INC_SRVC_ADDED, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_SERVER_ADD_INC_SERVICE, status);
+}
+
+static bool is_service(const bt_uuid_t *type)
+{
+	bt_uuid_t uuid;
+
+	bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+	if (!bt_uuid_cmp(&uuid, type))
+		return true;
+
+	bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+	if (!bt_uuid_cmp(&uuid, type))
+		return true;
+
+	return false;
+}
+
+static bool match_pending_dev_request(const void *data, const void *user_data)
+{
+	const struct pending_request *pending_request = data;
+
+	return !pending_request->completed;
+}
+
+static void send_dev_complete_response(struct gatt_device *device,
+								uint8_t opcode)
+{
+	size_t mtu;
+	uint8_t *rsp = g_attrib_get_buffer(device->attrib, &mtu);
+	struct pending_request *val;
+	uint16_t len = 0;
+	uint8_t error = 0;
+
+	if (queue_isempty(device->pending_requests))
+		return;
+
+	if (queue_find(device->pending_requests, match_pending_dev_request,
+									NULL)) {
+		DBG("Still pending requests");
+		return;
+	}
+
+	val = queue_peek_head(device->pending_requests);
+	if (!val) {
+		error = ATT_ECODE_ATTR_NOT_FOUND;
+		goto done;
+	}
+
+	if (val->error) {
+		error = val->error;
+		goto done;
+	}
+
+	switch (opcode) {
+	case ATT_OP_READ_BY_TYPE_REQ: {
+		struct att_data_list *adl;
+		int iterator = 0;
+		int length;
+		struct queue *temp;
+
+		temp = queue_new();
+
+		val = queue_pop_head(device->pending_requests);
+		if (!val) {
+			queue_destroy(temp, NULL);
+			error = ATT_ECODE_ATTR_NOT_FOUND;
+			goto done;
+		}
+
+		if (val->error) {
+			queue_destroy(temp, NULL);
+			error = val->error;
+			destroy_pending_request(val);
+			goto done;
+		}
+
+		length = val->length;
+
+		while (val && val->length == length && val->error == 0) {
+			queue_push_tail(temp, val);
+			val = queue_pop_head(device->pending_requests);
+		}
+
+		adl = att_data_list_alloc(queue_length(temp),
+						sizeof(uint16_t) + length);
+
+		destroy_pending_request(val);
+
+		val = queue_pop_head(temp);
+		while (val) {
+			uint8_t *value = adl->data[iterator++];
+			uint16_t handle;
+
+			handle = gatt_db_attribute_get_handle(val->attrib);
+
+			put_le16(handle, value);
+			memcpy(&value[2], val->value, val->length);
+
+			destroy_pending_request(val);
+			val = queue_pop_head(temp);
+		}
+
+		len = enc_read_by_type_resp(adl, rsp, mtu);
+
+		att_data_list_free(adl);
+		queue_destroy(temp, destroy_pending_request);
+
+		break;
+	}
+	case ATT_OP_READ_BLOB_REQ:
+		len = enc_read_blob_resp(val->value, val->length, val->offset,
+								rsp, mtu);
+		break;
+	case ATT_OP_READ_REQ:
+		len = enc_read_resp(val->value, val->length, rsp, mtu);
+		break;
+	case ATT_OP_READ_BY_GROUP_REQ: {
+		struct att_data_list *adl;
+		int iterator = 0;
+		int length;
+		struct queue *temp;
+
+		temp = queue_new();
+
+		val = queue_pop_head(device->pending_requests);
+		if (!val) {
+			queue_destroy(temp, NULL);
+			error = ATT_ECODE_ATTR_NOT_FOUND;
+			goto done;
+		}
+
+		length = val->length;
+
+		while (val && val->length == length) {
+			queue_push_tail(temp, val);
+			val = queue_pop_head(device->pending_requests);
+		}
+
+		adl = att_data_list_alloc(queue_length(temp),
+						2 * sizeof(uint16_t) + length);
+
+		val = queue_pop_head(temp);
+		while (val) {
+			uint8_t *value = adl->data[iterator++];
+			uint16_t start_handle, end_handle;
+
+			gatt_db_attribute_get_service_handles(val->attrib,
+								&start_handle,
+								&end_handle);
+
+			put_le16(start_handle, value);
+			put_le16(end_handle, &value[2]);
+			memcpy(&value[4], val->value, val->length);
+
+			destroy_pending_request(val);
+			val = queue_pop_head(temp);
+		}
+
+		len = enc_read_by_grp_resp(adl, rsp, mtu);
+
+		att_data_list_free(adl);
+		queue_destroy(temp, destroy_pending_request);
+
+		break;
+	}
+	case ATT_OP_FIND_BY_TYPE_REQ: {
+		GSList *list = NULL;
+
+		val = queue_pop_head(device->pending_requests);
+		while (val) {
+			struct att_range *range;
+			const bt_uuid_t *type;
+
+			/* Its find by type and value - filter by value here */
+			if ((val->length != val->filter_vlen) ||
+				memcmp(val->value, val->filter_value,
+								val->length)) {
+
+				destroy_pending_request(val);
+				val = queue_pop_head(device->pending_requests);
+				continue;
+			}
+
+			range = new0(struct att_range, 1);
+			range->start = gatt_db_attribute_get_handle(
+								val->attrib);
+
+			type = gatt_db_attribute_get_type(val->attrib);
+			if (is_service(type))
+				gatt_db_attribute_get_service_handles(
+								val->attrib,
+								NULL,
+								&range->end);
+			else
+				range->end = range->start;
+
+			list = g_slist_append(list, range);
+
+			destroy_pending_request(val);
+			val = queue_pop_head(device->pending_requests);
+		}
+
+		if (list && !error)
+			len = enc_find_by_type_resp(list, rsp, mtu);
+		else
+			error = ATT_ECODE_ATTR_NOT_FOUND;
+
+		g_slist_free_full(list, free);
+
+		break;
+	}
+	case ATT_OP_EXEC_WRITE_REQ:
+		len = enc_exec_write_resp(rsp);
+		break;
+	case ATT_OP_WRITE_REQ:
+		len = enc_write_resp(rsp);
+		break;
+	case ATT_OP_PREP_WRITE_REQ: {
+		uint16_t handle;
+
+		handle = gatt_db_attribute_get_handle(val->attrib);
+		len = enc_prep_write_resp(handle, val->offset, val->value,
+							val->length, rsp, mtu);
+		break;
+	}
+	default:
+		break;
+	}
+
+done:
+	if (!len)
+		len = enc_error_resp(opcode, 0x0000, error, rsp, mtu);
+
+	g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL);
+
+	queue_remove_all(device->pending_requests, NULL, NULL,
+						destroy_pending_request);
+}
+
+struct request_processing_data {
+	uint8_t opcode;
+	struct gatt_device *device;
+};
+
+static uint8_t check_device_permissions(struct gatt_device *device,
+					uint8_t opcode, uint32_t permissions)
+{
+	GIOChannel *io;
+	int sec_level;
+
+	io = g_attrib_get_channel(device->attrib);
+
+	if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
+							BT_IO_OPT_INVALID))
+		return ATT_ECODE_UNLIKELY;
+
+	DBG("opcode 0x%02x permissions %u sec_level %u", opcode, permissions,
+								sec_level);
+
+	switch (opcode) {
+	case ATT_OP_SIGNED_WRITE_CMD:
+		if (!(permissions & GATT_PERM_WRITE_SIGNED))
+				return ATT_ECODE_WRITE_NOT_PERM;
+
+		if (permissions & GATT_PERM_WRITE_SIGNED_MITM) {
+			bool auth;
+
+			if (bt_get_csrk(&device->bdaddr, true, NULL, NULL,
+					&auth) && auth)
+				break;
+
+			return ATT_ECODE_AUTHENTICATION;
+		}
+		break;
+	case ATT_OP_READ_BY_TYPE_REQ:
+	case ATT_OP_READ_REQ:
+	case ATT_OP_READ_BLOB_REQ:
+	case ATT_OP_READ_MULTI_REQ:
+	case ATT_OP_READ_BY_GROUP_REQ:
+	case ATT_OP_FIND_BY_TYPE_REQ:
+	case ATT_OP_FIND_INFO_REQ:
+		if (!(permissions & GATT_PERM_READ))
+			return ATT_ECODE_READ_NOT_PERM;
+
+		if ((permissions & GATT_PERM_READ_MITM) &&
+						sec_level < BT_SECURITY_HIGH)
+			return ATT_ECODE_AUTHENTICATION;
+
+		if ((permissions & GATT_PERM_READ_ENCRYPTED) &&
+						sec_level < BT_SECURITY_MEDIUM)
+			return ATT_ECODE_INSUFF_ENC;
+
+		if (permissions & GATT_PERM_READ_AUTHORIZATION)
+			return ATT_ECODE_AUTHORIZATION;
+		break;
+	case ATT_OP_WRITE_REQ:
+	case ATT_OP_WRITE_CMD:
+	case ATT_OP_PREP_WRITE_REQ:
+	case ATT_OP_EXEC_WRITE_REQ:
+		if (!(permissions & GATT_PERM_WRITE))
+			return ATT_ECODE_WRITE_NOT_PERM;
+
+		if ((permissions & GATT_PERM_WRITE_MITM) &&
+						sec_level < BT_SECURITY_HIGH)
+			return ATT_ECODE_AUTHENTICATION;
+
+		if ((permissions & GATT_PERM_WRITE_ENCRYPTED) &&
+						sec_level < BT_SECURITY_MEDIUM)
+			return ATT_ECODE_INSUFF_ENC;
+
+		if (permissions & GATT_PERM_WRITE_AUTHORIZATION)
+			return ATT_ECODE_AUTHORIZATION;
+		break;
+	default:
+		return ATT_ECODE_UNLIKELY;
+	}
+
+	return 0;
+}
+
+static uint8_t err_to_att(int err)
+{
+	if (!err || (err > 0 && err < UINT8_MAX))
+		return err;
+
+	switch (err) {
+	case -ENOENT:
+		return ATT_ECODE_INVALID_HANDLE;
+	case -ENOMEM:
+		return ATT_ECODE_INSUFF_RESOURCES;
+	default:
+		return ATT_ECODE_UNLIKELY;
+	}
+}
+
+static void attribute_read_cb(struct gatt_db_attribute *attrib, int err,
+					const uint8_t *value, size_t length,
+					void *user_data)
+{
+	struct pending_request *resp_data = user_data;
+	uint8_t error = err_to_att(err);
+
+	resp_data->attrib = attrib;
+	resp_data->length = length;
+	resp_data->error = error;
+
+	resp_data->completed = true;
+
+	if (!length)
+		return;
+
+	resp_data->value = malloc0(length);
+	if (!resp_data->value) {
+		resp_data->error = ATT_ECODE_INSUFF_RESOURCES;
+
+		return;
+	}
+
+	memcpy(resp_data->value, value, length);
+}
+
+static void read_requested_attributes(void *data, void *user_data)
+{
+	struct pending_request *resp_data = data;
+	struct request_processing_data *process_data = user_data;
+	struct bt_att *att = g_attrib_get_att(process_data->device->attrib);
+	struct gatt_db_attribute *attrib;
+	uint32_t permissions;
+	uint8_t error;
+
+	attrib = resp_data->attrib;
+	if (!attrib) {
+		resp_data->error = ATT_ECODE_ATTR_NOT_FOUND;
+		resp_data->completed = true;
+		return;
+	}
+
+	permissions = gatt_db_attribute_get_permissions(attrib);
+
+	/*
+	 * Check if it is attribute we didn't declare permissions, like service
+	 * declaration or included service. Set permissions to read only
+	 */
+	if (permissions == 0)
+		permissions = GATT_PERM_READ;
+
+	error = check_device_permissions(process_data->device,
+							process_data->opcode,
+							permissions);
+	if (error != 0) {
+		resp_data->error = error;
+		resp_data->completed = true;
+		return;
+	}
+
+	gatt_db_attribute_read(attrib, resp_data->offset, process_data->opcode,
+					att, attribute_read_cb, resp_data);
+}
+
+static void process_dev_pending_requests(struct gatt_device *device,
+							uint8_t att_opcode)
+{
+	struct request_processing_data process_data;
+
+	if (queue_isempty(device->pending_requests))
+		return;
+
+	process_data.device = device;
+	process_data.opcode = att_opcode;
+
+	/* Process pending requests and prepare response */
+	queue_foreach(device->pending_requests, read_requested_attributes,
+								&process_data);
+
+	send_dev_complete_response(device, att_opcode);
+}
+
+static struct pending_trans_data *conn_add_transact(struct app_connection *conn,
+					uint8_t opcode,
+					struct gatt_db_attribute *attrib,
+					unsigned int serial_id)
+{
+	struct pending_trans_data *transaction;
+	static int32_t trans_id = 1;
+
+	transaction = new0(struct pending_trans_data, 1);
+	transaction->id = trans_id++;
+	transaction->opcode = opcode;
+	transaction->attrib = attrib;
+	transaction->serial_id = serial_id;
+
+	queue_push_tail(conn->transactions, transaction);
+
+	return transaction;
+}
+
+static bool get_dst_addr(struct bt_att *att, bdaddr_t *dst)
+{
+	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_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;
+}
+
+static void read_cb(struct gatt_db_attribute *attrib, unsigned int id,
+			uint16_t offset, uint8_t opcode, struct bt_att *att,
+			void *user_data)
+{
+	struct pending_trans_data *transaction;
+	struct hal_ev_gatt_server_request_read ev;
+	struct gatt_app *app;
+	struct app_connection *conn;
+	int32_t app_id = PTR_TO_INT(user_data);
+	bdaddr_t bdaddr;
+
+	DBG("id %u", id);
+
+	app = find_app_by_id(app_id);
+	if (!app) {
+		error("gatt: read_cb, cound not found app id");
+		goto failed;
+	}
+
+	if (!get_dst_addr(att, &bdaddr)) {
+		error("gatt: read_cb, could not obtain dst BDADDR");
+		goto failed;
+	}
+
+	conn = find_conn(&bdaddr, app->id);
+	if (!conn) {
+		error("gatt: read_cb, cound not found connection");
+		goto failed;
+	}
+
+	memset(&ev, 0, sizeof(ev));
+
+	/* Store the request data, complete callback and transaction id */
+	transaction = conn_add_transact(conn, opcode, attrib, id);
+
+	bdaddr2android(&bdaddr, ev.bdaddr);
+	ev.conn_id = conn->id;
+	ev.attr_handle = gatt_db_attribute_get_handle(attrib);
+	ev.offset = offset;
+	ev.is_long = opcode == ATT_OP_READ_BLOB_REQ;
+	ev.trans_id = transaction->id;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_EV_GATT_SERVER_REQUEST_READ,
+					sizeof(ev), &ev);
+
+	return;
+
+failed:
+	gatt_db_attribute_read_result(attrib, id, -ENOENT, NULL, 0);
+}
+
+static void 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)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_gatt_server_request_write *ev = (void *) buf;
+	struct pending_trans_data *transaction;
+	struct gatt_app *app;
+	int32_t app_id = PTR_TO_INT(user_data);
+	struct app_connection *conn;
+	bdaddr_t bdaddr;
+
+	DBG("id %u", id);
+
+	app = find_app_by_id(app_id);
+	if (!app) {
+		error("gatt: write_cb could not found app id");
+		goto failed;
+	}
+
+	if (!get_dst_addr(att, &bdaddr)) {
+		error("gatt: write_cb, could not obtain dst BDADDR");
+		goto failed;
+	}
+
+	conn = find_conn(&bdaddr, app->id);
+	if (!conn) {
+		error("gatt: write_cb could not found connection");
+		goto failed;
+	}
+
+	/*
+	 * Remember that this application has ongoing prep write
+	 * Need it later to find out where to send execute write
+	 */
+	if (opcode == ATT_OP_PREP_WRITE_REQ)
+		conn->wait_execute_write = true;
+
+	/* Store the request data, complete callback and transaction id */
+	transaction = conn_add_transact(conn, opcode, attrib, id);
+
+	memset(ev, 0, sizeof(*ev));
+
+	bdaddr2android(&bdaddr, &ev->bdaddr);
+	ev->attr_handle = gatt_db_attribute_get_handle(attrib);
+	ev->offset = offset;
+
+	ev->conn_id = conn->id;
+	ev->trans_id = transaction->id;
+
+	ev->is_prep = opcode == ATT_OP_PREP_WRITE_REQ;
+
+	if (opcode == ATT_OP_WRITE_REQ || opcode == ATT_OP_PREP_WRITE_REQ)
+		ev->need_rsp = 0x01;
+	else
+		gatt_db_attribute_write_result(attrib, id, 0);
+
+	ev->length = len;
+	memcpy(ev->value, value, len);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_REQUEST_WRITE,
+						sizeof(*ev) + ev->length , ev);
+	return;
+
+failed:
+	gatt_db_attribute_write_result(attrib, id, ATT_ECODE_UNLIKELY);
+}
+
+static uint32_t android_to_gatt_permissions(int32_t hal_permissions)
+{
+	uint32_t permissions = 0;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_READ)
+		permissions |= GATT_PERM_READ;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_READ_ENCRYPTED)
+		permissions |= GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM)
+		permissions |= GATT_PERM_READ_MITM | GATT_PERM_READ_ENCRYPTED |
+								GATT_PERM_READ;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_WRITE)
+		permissions |= GATT_PERM_WRITE;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_WRITE_ENCRYPTED)
+		permissions |= GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_WRITE_ENCRYPTED_MITM)
+		permissions |= GATT_PERM_WRITE_MITM |
+				GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_WRITE_SIGNED)
+		permissions |= GATT_PERM_WRITE_SIGNED;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_WRITE_SIGNED_MITM)
+		permissions |= GATT_PERM_WRITE_SIGNED_MITM |
+							GATT_PERM_WRITE_SIGNED;
+
+	return permissions;
+}
+
+static void handle_server_add_characteristic(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_add_characteristic *cmd = buf;
+	struct hal_ev_gatt_server_characteristic_added ev;
+	struct gatt_app *server;
+	struct gatt_db_attribute *attrib;
+	bt_uuid_t uuid;
+	uint8_t status;
+	uint32_t permissions;
+	int32_t app_id = cmd->server_if;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	server = find_app_by_id(app_id);
+	if (!server) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+	if (!attrib) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	android2uuid(cmd->uuid, &uuid);
+	permissions = android_to_gatt_permissions(cmd->permissions);
+
+	attrib = gatt_db_service_add_characteristic(attrib,
+							&uuid, permissions,
+							cmd->properties,
+							read_cb, write_cb,
+							INT_TO_PTR(app_id));
+	if (!attrib) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ev.char_handle = gatt_db_attribute_get_handle(attrib);
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ev.srvc_handle = cmd->service_handle;
+	ev.status = status;
+	ev.server_if = app_id;
+	ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+	memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid));
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_EV_GATT_SERVER_CHAR_ADDED, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC, status);
+}
+
+static void handle_server_add_descriptor(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_add_descriptor *cmd = buf;
+	struct hal_ev_gatt_server_descriptor_added ev;
+	struct gatt_app *server;
+	struct gatt_db_attribute *attrib;
+	bt_uuid_t uuid;
+	uint8_t status;
+	uint32_t permissions;
+	int32_t app_id = cmd->server_if;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	server = find_app_by_id(app_id);
+	if (!server) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	android2uuid(cmd->uuid, &uuid);
+	permissions = android_to_gatt_permissions(cmd->permissions);
+
+	attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+	if (!attrib) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	attrib = gatt_db_service_add_descriptor(attrib, &uuid, permissions,
+							read_cb, write_cb,
+							INT_TO_PTR(app_id));
+	if (!attrib) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ev.descr_handle = gatt_db_attribute_get_handle(attrib);
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ev.server_if = app_id;
+	ev.srvc_handle = cmd->service_handle;
+	memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid));
+	ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, status);
+}
+
+static void notify_service_change(void *data, void *user_data)
+{
+	struct att_range range;
+	struct gatt_db_attribute *attrib = user_data;
+
+	gatt_db_attribute_get_service_handles(attrib, &range.start, &range.end);
+
+	/* In case of db error */
+	if (!range.end)
+		return;
+
+	notify_att_range_change(data, &range);
+}
+
+static sdp_record_t *get_sdp_record(uuid_t *uuid, uint16_t start, uint16_t end,
+							const char *name)
+{
+	sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto;
+	uuid_t root_uuid, proto_uuid, l2cap;
+	sdp_record_t *record;
+	sdp_data_t *psm, *sh, *eh;
+	uint16_t lp = ATT_PSM;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+	sdp_list_free(root, NULL);
+
+	svclass_id = sdp_list_append(NULL, uuid);
+	sdp_set_service_classes(record, svclass_id);
+	sdp_list_free(svclass_id, NULL);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&proto_uuid, ATT_UUID);
+	proto[1] = sdp_list_append(NULL, &proto_uuid);
+	sh = sdp_data_alloc(SDP_UINT16, &start);
+	proto[1] = sdp_list_append(proto[1], sh);
+	eh = sdp_data_alloc(SDP_UINT16, &end);
+	proto[1] = sdp_list_append(proto[1], eh);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	if (name)
+		sdp_set_info_attr(record, name, "BlueZ for Android", NULL);
+
+	sdp_data_free(psm);
+	sdp_data_free(sh);
+	sdp_data_free(eh);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(aproto, NULL);
+
+	return record;
+}
+
+static uint32_t add_sdp_record(const bt_uuid_t *uuid, uint16_t start,
+						uint16_t end, const char *name)
+{
+	sdp_record_t *rec;
+	uuid_t u, u32;
+
+	switch (uuid->type) {
+	case BT_UUID16:
+		sdp_uuid16_create(&u, uuid->value.u16);
+		break;
+	case BT_UUID32:
+		sdp_uuid32_create(&u32, uuid->value.u32);
+		sdp_uuid32_to_uuid128(&u, &u32);
+		break;
+	case BT_UUID128:
+		sdp_uuid128_create(&u, &uuid->value.u128);
+		break;
+	case BT_UUID_UNSPEC:
+	default:
+		return 0;
+	}
+
+	rec = get_sdp_record(&u, start, end, name);
+	if (!rec)
+		return 0;
+
+	if (bt_adapter_add_record(rec, 0) < 0) {
+		error("gatt: Failed to register SDP record");
+		sdp_record_free(rec);
+		return 0;
+	}
+
+	return rec->handle;
+}
+
+static bool match_service_sdp(const void *data, const void *user_data)
+{
+	const struct service_sdp *s = data;
+
+	return s->service_handle == PTR_TO_INT(user_data);
+}
+
+static struct service_sdp *new_service_sdp_record(int32_t service_handle)
+{
+	bt_uuid_t uuid;
+	struct service_sdp *s;
+	struct gatt_db_attribute *attrib;
+	uint16_t end_handle;
+
+	attrib = gatt_db_get_attribute(gatt_db, service_handle);
+	if (!attrib)
+		return NULL;
+
+	gatt_db_attribute_get_service_handles(attrib, NULL, &end_handle);
+	if (!end_handle)
+		return NULL;
+
+	if (!gatt_db_attribute_get_service_uuid(attrib, &uuid))
+		return NULL;
+
+	s = new0(struct service_sdp, 1);
+	s->service_handle = service_handle;
+	s->sdp_handle = add_sdp_record(&uuid, service_handle, end_handle, NULL);
+	if (!s->sdp_handle) {
+		free(s);
+		return NULL;
+	}
+
+	return s;
+}
+
+static void free_service_sdp_record(void *data)
+{
+	struct service_sdp *s = data;
+
+	if (!s)
+		return;
+
+	bt_adapter_remove_record(s->sdp_handle);
+	free(s);
+}
+
+static bool add_service_sdp_record(int32_t service_handle)
+{
+	struct service_sdp *s;
+
+	s = queue_find(services_sdp, match_service_sdp,
+						INT_TO_PTR(service_handle));
+	if (s)
+		return true;
+
+	s = new_service_sdp_record(service_handle);
+	if (!s)
+		return false;
+
+	queue_push_tail(services_sdp, s);
+
+	return true;
+}
+
+static void remove_service_sdp_record(int32_t service_handle)
+{
+	struct service_sdp *s;
+
+	s = queue_remove_if(services_sdp, match_service_sdp,
+						INT_TO_PTR(service_handle));
+	if (!s)
+		return;
+
+	free_service_sdp_record(s);
+}
+
+static void handle_server_start_service(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_start_service *cmd = buf;
+	struct hal_ev_gatt_server_service_started ev;
+	struct gatt_app *server;
+	struct gatt_db_attribute *attrib;
+	uint8_t status;
+
+	DBG("transport 0x%02x", cmd->transport);
+
+	memset(&ev, 0, sizeof(ev));
+
+	if (cmd->transport == 0) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	server = find_app_by_id(cmd->server_if);
+	if (!server) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (cmd->transport & GATT_SERVER_TRANSPORT_BREDR_BIT) {
+		if (!add_service_sdp_record(cmd->service_handle)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+	}
+	/* TODO: Handle BREDR only */
+
+	attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+	if (!attrib) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (!gatt_db_service_set_active(attrib, true)) {
+		/*
+		 * no need to clean SDP since this can fail only if service
+		 * handle is invalid in which case add_sdp_record() also fails
+		 */
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	queue_foreach(gatt_devices, notify_service_change, attrib);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+	ev.server_if = cmd->server_if;
+	ev.srvc_handle = cmd->service_handle;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_SERVICE_STARTED, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_SERVER_START_SERVICE, status);
+}
+
+static void handle_server_stop_service(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_stop_service *cmd = buf;
+	struct hal_ev_gatt_server_service_stopped ev;
+	struct gatt_app *server;
+	struct gatt_db_attribute *attrib;
+	uint8_t status;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	server = find_app_by_id(cmd->server_if);
+	if (!server) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+	if (!attrib) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (!gatt_db_service_set_active(attrib, false)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	remove_service_sdp_record(cmd->service_handle);
+
+	status = HAL_STATUS_SUCCESS;
+
+	queue_foreach(gatt_devices, notify_service_change, attrib);
+
+failed:
+	ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+	ev.server_if = cmd->server_if;
+	ev.srvc_handle = cmd->service_handle;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_SERVICE_STOPPED, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_SERVER_STOP_SERVICE, status);
+}
+
+static void handle_server_delete_service(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_delete_service *cmd = buf;
+	struct hal_ev_gatt_server_service_deleted ev;
+	struct gatt_app *server;
+	struct gatt_db_attribute *attrib;
+	uint8_t status;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	server = find_app_by_id(cmd->server_if);
+	if (!server) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+	if (!attrib) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (!gatt_db_remove_service(gatt_db, attrib)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	remove_service_sdp_record(cmd->service_handle);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+	ev.srvc_handle = cmd->service_handle;
+	ev.server_if = cmd->server_if;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_SERVICE_DELETED, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_SERVER_DELETE_SERVICE, status);
+}
+
+static void indication_confirmation_cb(guint8 status, const guint8 *pdu,
+						guint16 len, gpointer user_data)
+{
+	struct hal_ev_gatt_server_indication_sent ev;
+
+	ev.status = status;
+	ev.conn_id = PTR_TO_UINT(user_data);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_INDICATION_SENT, sizeof(ev), &ev);
+}
+
+static void handle_server_send_indication(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_send_indication *cmd = buf;
+	struct app_connection *conn;
+	uint8_t status;
+	uint16_t length;
+	uint8_t *pdu;
+	size_t mtu;
+	GAttribResultFunc confirmation_cb = NULL;
+
+	DBG("");
+
+	conn = find_connection_by_id(cmd->conn_id);
+	if (!conn) {
+		error("gatt: Could not find connection");
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	pdu = g_attrib_get_buffer(conn->device->attrib, &mtu);
+
+	if (cmd->confirm) {
+		length = enc_indication(cmd->attribute_handle,
+					(uint8_t *) cmd->value, cmd->len, pdu,
+					mtu);
+		confirmation_cb = indication_confirmation_cb;
+	} else {
+		length = enc_notification(cmd->attribute_handle,
+						(uint8_t *) cmd->value,
+						cmd->len, pdu, mtu);
+	}
+
+	if (!g_attrib_send(conn->device->attrib, 0, pdu, length,
+				confirmation_cb, UINT_TO_PTR(conn->id), NULL)) {
+		error("gatt: Failed to send indication");
+		status = HAL_STATUS_FAILED;
+	} else {
+		status = HAL_STATUS_SUCCESS;
+	}
+
+	/* Here we confirm failed indications and all notifications */
+	if (status || !confirmation_cb)
+		indication_confirmation_cb(status, NULL, 0,
+							UINT_TO_PTR(conn->id));
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_SERVER_SEND_INDICATION, status);
+}
+
+static bool match_trans_id(const void *data, const void *user_data)
+{
+	const struct pending_trans_data *transaction = data;
+
+	return transaction->id == PTR_TO_UINT(user_data);
+}
+
+static bool find_conn_waiting_exec_write(const void *data,
+							const void *user_data)
+{
+	const struct app_connection *conn = data;
+
+	return conn->wait_execute_write;
+}
+
+static bool pending_execute_write(void)
+{
+	return queue_find(app_connections, find_conn_waiting_exec_write, NULL);
+}
+
+static void handle_server_send_response(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_send_response *cmd = buf;
+	struct pending_trans_data *transaction;
+	struct app_connection *conn;
+	uint8_t status;
+
+	DBG("");
+
+	conn = find_connection_by_id(cmd->conn_id);
+	if (!conn) {
+		error("gatt: could not found connection");
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	transaction = queue_remove_if(conn->transactions, match_trans_id,
+						UINT_TO_PTR(cmd->trans_id));
+	if (!transaction) {
+		error("gatt: transaction ID = %d not found", cmd->trans_id);
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	if (transaction->opcode == ATT_OP_EXEC_WRITE_REQ) {
+		struct pending_request *req;
+
+		conn->wait_execute_write = false;
+
+		/* Check for execute response from all server applications */
+		if (pending_execute_write())
+			goto done;
+
+		/*
+		 * This is usually done through db write callback but for
+		 * execute write we dont have the attribute or handle to call
+		 * gatt_db_attribute_write().
+		 */
+		req = queue_peek_head(conn->device->pending_requests);
+		if (!req)
+			goto done;
+
+		/* Cast status to uint8_t, due to (byte) cast in java layer. */
+		req->error = err_to_att((uint8_t) cmd->status);
+		req->completed = true;
+
+		/*
+		 * FIXME: Handle situation when not all server applications
+		 * respond with a success.
+		 */
+	}
+
+	/* Cast status to uint8_t, due to (byte) cast in java layer. */
+	if (transaction->opcode < ATT_OP_WRITE_REQ)
+		gatt_db_attribute_read_result(transaction->attrib,
+					transaction->serial_id,
+					err_to_att((uint8_t) cmd->status),
+					cmd->data, cmd->len);
+	else
+		gatt_db_attribute_write_result(transaction->attrib,
+					transaction->serial_id,
+					err_to_att((uint8_t) cmd->status));
+
+	send_dev_complete_response(conn->device, transaction->opcode);
+
+done:
+	/* Clean request data */
+	free(transaction);
+
+	status = HAL_STATUS_SUCCESS;
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_SEND_RESPONSE, status);
+}
+
+static void handle_client_scan_filter_setup(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_scan_filter_setup *cmd = buf;
+
+	DBG("client_if %u", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_scan_filter_add_remove(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_scan_filter_add_remove *cmd = buf;
+
+	DBG("client_if %u", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE,
+				HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_scan_filter_clear(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_scan_filter_clear *cmd = buf;
+
+	DBG("client_if %u", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_scan_filter_enable(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_scan_filter_enable *cmd = buf;
+
+	DBG("client_if %u", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_configure_mtu(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_configure_mtu *cmd = buf;
+	static struct app_connection *conn;
+	uint8_t status;
+
+	DBG("conn_id %u mtu %d", cmd->conn_id, cmd->mtu);
+
+	conn = find_connection_by_id(cmd->conn_id);
+	if (!conn) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	/*
+	 * currently MTU is always exchanged on connection, just report current
+	 * value
+	 *
+	 * TODO figure out when send failed status in notification
+	 * TODO should we fail for BR/EDR?
+	 */
+	notify_client_mtu_change(conn, false);
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_CONFIGURE_MTU,
+					status);
+}
+
+static void handle_client_conn_param_update(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_conn_param_update *cmd = buf;
+	char address[18];
+	bdaddr_t bdaddr;
+
+	android2bdaddr(cmd->address, &bdaddr);
+	ba2str(&bdaddr, address);
+
+	DBG("%s", address);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_set_scan_param(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_set_scan_param *cmd = buf;
+
+	DBG("interval %d window %d", cmd->interval, cmd->window);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SET_SCAN_PARAM,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_setup_multi_adv(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_setup_multi_adv *cmd = buf;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_update_multi_adv(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_update_multi_adv *cmd = buf;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_setup_multi_adv_inst(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_setup_multi_adv_inst *cmd = buf;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_disable_multi_adv_inst(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_disable_multi_adv_inst *cmd = buf;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST,
+				HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_configure_batchscan(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_configure_batchscan *cmd = buf;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_enable_batchscan(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_enable_batchscan *cmd = buf;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_disable_batchscan(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_disable_batchscan *cmd = buf;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_read_batchscan_reports(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_read_batchscan_reports *cmd = buf;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS,
+				HAL_STATUS_UNSUPPORTED);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_GATT_CLIENT_REGISTER */
+	{ handle_client_register, false,
+		sizeof(struct hal_cmd_gatt_client_register) },
+	/* HAL_OP_GATT_CLIENT_UNREGISTER */
+	{ handle_client_unregister, false,
+		sizeof(struct hal_cmd_gatt_client_unregister) },
+	/* HAL_OP_GATT_CLIENT_SCAN */
+	{ handle_client_scan, false,
+		sizeof(struct hal_cmd_gatt_client_scan) },
+	/* HAL_OP_GATT_CLIENT_CONNECT */
+	{ handle_client_connect, false,
+		sizeof(struct hal_cmd_gatt_client_connect) },
+	/* HAL_OP_GATT_CLIENT_DISCONNECT */
+	{ handle_client_disconnect, false,
+		sizeof(struct hal_cmd_gatt_client_disconnect) },
+	/* HAL_OP_GATT_CLIENT_LISTEN */
+	{ handle_client_listen, false,
+		sizeof(struct hal_cmd_gatt_client_listen) },
+	/* HAL_OP_GATT_CLIENT_REFRESH */
+	{ handle_client_refresh, false,
+		sizeof(struct hal_cmd_gatt_client_refresh) },
+	/* HAL_OP_GATT_CLIENT_SEARCH_SERVICE */
+	{ handle_client_search_service, true,
+		sizeof(struct hal_cmd_gatt_client_search_service) },
+	/* HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE */
+	{ handle_client_get_included_service, true,
+		sizeof(struct hal_cmd_gatt_client_get_included_service) },
+	/* HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC */
+	{ handle_client_get_characteristic, true,
+		sizeof(struct hal_cmd_gatt_client_get_characteristic) },
+	/* HAL_OP_GATT_CLIENT_GET_DESCRIPTOR */
+	{ handle_client_get_descriptor, true,
+		sizeof(struct hal_cmd_gatt_client_get_descriptor) },
+	/* HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC */
+	{ handle_client_read_characteristic, false,
+		sizeof(struct hal_cmd_gatt_client_read_characteristic) },
+	/* HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC */
+	{ handle_client_write_characteristic, true,
+		sizeof(struct hal_cmd_gatt_client_write_characteristic) },
+	/* HAL_OP_GATT_CLIENT_READ_DESCRIPTOR */
+	{ handle_client_read_descriptor, false,
+		sizeof(struct hal_cmd_gatt_client_read_descriptor) },
+	/* HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR */
+	{ handle_client_write_descriptor, true,
+		sizeof(struct hal_cmd_gatt_client_write_descriptor) },
+	/* HAL_OP_GATT_CLIENT_EXECUTE_WRITE */
+	{ handle_client_execute_write, false,
+		sizeof(struct hal_cmd_gatt_client_execute_write)},
+	/* HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION */
+	{ handle_client_register_for_notification, false,
+		sizeof(struct hal_cmd_gatt_client_register_for_notification) },
+	/* HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION */
+	{ handle_client_deregister_for_notification, false,
+		sizeof(struct hal_cmd_gatt_client_deregister_for_notification) },
+	/* HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI */
+	{ handle_client_read_remote_rssi, false,
+		sizeof(struct hal_cmd_gatt_client_read_remote_rssi) },
+	/* HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE */
+	{ handle_client_get_device_type, false,
+		sizeof(struct hal_cmd_gatt_client_get_device_type) },
+	/* HAL_OP_GATT_CLIENT_SET_ADV_DATA */
+	{ handle_client_set_adv_data, true,
+		sizeof(struct hal_cmd_gatt_client_set_adv_data) },
+	/* HAL_OP_GATT_CLIENT_TEST_COMMAND */
+	{ handle_client_test_command, false,
+		sizeof(struct hal_cmd_gatt_client_test_command) },
+	/* HAL_OP_GATT_SERVER_REGISTER */
+	{ handle_server_register, false,
+		sizeof(struct hal_cmd_gatt_server_register) },
+	/* HAL_OP_GATT_SERVER_UNREGISTER */
+	{ handle_server_unregister, false,
+		sizeof(struct hal_cmd_gatt_server_unregister) },
+	/* HAL_OP_GATT_SERVER_CONNECT */
+	{ handle_server_connect, false,
+		sizeof(struct hal_cmd_gatt_server_connect) },
+	/* HAL_OP_GATT_SERVER_DISCONNECT */
+	{ handle_server_disconnect, false,
+		sizeof(struct hal_cmd_gatt_server_disconnect) },
+	/* HAL_OP_GATT_SERVER_ADD_SERVICE */
+	{ handle_server_add_service, false,
+		sizeof(struct hal_cmd_gatt_server_add_service) },
+	/* HAL_OP_GATT_SERVER_ADD_INC_SERVICE */
+	{ handle_server_add_included_service, false,
+		sizeof(struct hal_cmd_gatt_server_add_inc_service) },
+	/* HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC */
+	{ handle_server_add_characteristic, false,
+		sizeof(struct hal_cmd_gatt_server_add_characteristic) },
+	/* HAL_OP_GATT_SERVER_ADD_DESCRIPTOR */
+	{ handle_server_add_descriptor, false,
+		sizeof(struct hal_cmd_gatt_server_add_descriptor) },
+	/* HAL_OP_GATT_SERVER_START_SERVICE */
+	{ handle_server_start_service, false,
+		sizeof(struct hal_cmd_gatt_server_start_service) },
+	/* HAL_OP_GATT_SERVER_STOP_SERVICE */
+	{ handle_server_stop_service, false,
+		sizeof(struct hal_cmd_gatt_server_stop_service) },
+	/* HAL_OP_GATT_SERVER_DELETE_SERVICE */
+	{ handle_server_delete_service, false,
+		sizeof(struct hal_cmd_gatt_server_delete_service) },
+	/* HAL_OP_GATT_SERVER_SEND_INDICATION */
+	{ handle_server_send_indication, true,
+		sizeof(struct hal_cmd_gatt_server_send_indication) },
+	/* HAL_OP_GATT_SERVER_SEND_RESPONSE */
+	{ handle_server_send_response, true,
+		sizeof(struct hal_cmd_gatt_server_send_response) },
+	/* HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP */
+	{ handle_client_scan_filter_setup, false,
+		sizeof(struct hal_cmd_gatt_client_scan_filter_setup) },
+	/* HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE */
+	{ handle_client_scan_filter_add_remove, true,
+		sizeof(struct hal_cmd_gatt_client_scan_filter_add_remove) },
+	/* HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR */
+	{ handle_client_scan_filter_clear, false,
+		sizeof(struct hal_cmd_gatt_client_scan_filter_clear) },
+	/* HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE */
+	{ handle_client_scan_filter_enable, false,
+		sizeof(struct hal_cmd_gatt_client_scan_filter_enable) },
+	/* HAL_OP_GATT_CLIENT_CONFIGURE_MTU */
+	{ handle_client_configure_mtu, false,
+		sizeof(struct hal_cmd_gatt_client_configure_mtu) },
+	/* HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE */
+	{ handle_client_conn_param_update, false,
+		sizeof(struct hal_cmd_gatt_client_conn_param_update) },
+	/* HAL_OP_GATT_CLIENT_SET_SCAN_PARAM */
+	{ handle_client_set_scan_param, false,
+		sizeof(struct hal_cmd_gatt_client_set_scan_param) },
+	/* HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV */
+	{ handle_client_setup_multi_adv, false,
+		sizeof(struct hal_cmd_gatt_client_setup_multi_adv) },
+	/* HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV */
+	{ handle_client_update_multi_adv, false,
+		sizeof(struct hal_cmd_gatt_client_update_multi_adv) },
+	/* HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST */
+	{ handle_client_setup_multi_adv_inst, false,
+		sizeof(struct hal_cmd_gatt_client_setup_multi_adv_inst) },
+	/* HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST */
+	{ handle_client_disable_multi_adv_inst, false,
+		sizeof(struct hal_cmd_gatt_client_disable_multi_adv_inst) },
+	/* HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN */
+	{ handle_client_configure_batchscan, false,
+		sizeof(struct hal_cmd_gatt_client_configure_batchscan) },
+	/* HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN */
+	{ handle_client_enable_batchscan, false,
+		sizeof(struct hal_cmd_gatt_client_enable_batchscan) },
+	/* HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN */
+	{ handle_client_disable_batchscan, false,
+		sizeof(struct hal_cmd_gatt_client_disable_batchscan) },
+	/* HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS */
+	{ handle_client_read_batchscan_reports, false,
+		sizeof(struct hal_cmd_gatt_client_read_batchscan_reports) },
+};
+
+static uint8_t read_by_type(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *device)
+{
+	uint16_t start, end;
+	uint16_t len = 0;
+	bt_uuid_t uuid;
+	struct queue *q;
+
+	DBG("");
+
+	switch (cmd[0]) {
+	case ATT_OP_READ_BY_TYPE_REQ:
+		len = dec_read_by_type_req(cmd, cmd_len, &start, &end, &uuid);
+		break;
+	case ATT_OP_READ_BY_GROUP_REQ:
+		len = dec_read_by_grp_req(cmd, cmd_len, &start, &end, &uuid);
+		break;
+	default:
+		break;
+	}
+
+	if (!len)
+		return ATT_ECODE_INVALID_PDU;
+
+	if (start > end || start == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
+	q = queue_new();
+
+	switch (cmd[0]) {
+	case ATT_OP_READ_BY_TYPE_REQ:
+		gatt_db_read_by_type(gatt_db, start, end, uuid, q);
+		break;
+	case ATT_OP_READ_BY_GROUP_REQ:
+		gatt_db_read_by_group_type(gatt_db, start, end, uuid, q);
+		break;
+	default:
+		break;
+	}
+
+	if (queue_isempty(q)) {
+		queue_destroy(q, NULL);
+		return ATT_ECODE_ATTR_NOT_FOUND;
+	}
+
+	while (queue_peek_head(q)) {
+		struct pending_request *data;
+		struct gatt_db_attribute *attrib = queue_pop_head(q);
+
+		data = new0(struct pending_request, 1);
+		data->attrib = attrib;
+		queue_push_tail(device->pending_requests, data);
+	}
+
+	queue_destroy(q, NULL);
+	process_dev_pending_requests(device, cmd[0]);
+
+	return 0;
+}
+
+static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len,
+							struct gatt_device *dev)
+{
+	struct gatt_db_attribute *attrib;
+	uint16_t handle;
+	uint16_t len;
+	uint16_t offset;
+	struct pending_request *data;
+
+	DBG("");
+
+	switch (cmd[0]) {
+	case ATT_OP_READ_BLOB_REQ:
+		len = dec_read_blob_req(cmd, cmd_len, &handle, &offset);
+		if (!len)
+			return ATT_ECODE_INVALID_PDU;
+		break;
+	case ATT_OP_READ_REQ:
+		len = dec_read_req(cmd, cmd_len, &handle);
+		if (!len)
+			return ATT_ECODE_INVALID_PDU;
+		offset = 0;
+		break;
+	default:
+		error("gatt: Unexpected read type 0x%02x", cmd[0]);
+		return ATT_ECODE_REQ_NOT_SUPP;
+	}
+
+	attrib = gatt_db_get_attribute(gatt_db, handle);
+	if (attrib == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
+	data = new0(struct pending_request, 1);
+	data->offset = offset;
+	data->attrib = attrib;
+	queue_push_tail(dev->pending_requests, data);
+
+	process_dev_pending_requests(dev, cmd[0]);
+
+	return 0;
+}
+
+static uint8_t mtu_att_handle(const uint8_t *cmd, uint16_t cmd_len,
+							struct gatt_device *dev)
+{
+	uint16_t rmtu, mtu, len;
+	size_t length;
+	uint8_t *rsp;
+
+	DBG("");
+
+	len = dec_mtu_req(cmd, cmd_len, &rmtu);
+	if (!len)
+		return ATT_ECODE_INVALID_PDU;
+
+	/* MTU exchange shall not be used on BR/EDR - Vol 3. Part G. 4.3.1 */
+	if (get_cid(dev) != ATT_CID)
+		return ATT_ECODE_UNLIKELY;
+
+	if (!get_local_mtu(dev, &mtu))
+		return ATT_ECODE_UNLIKELY;
+
+	if (!update_mtu(dev, rmtu))
+		return ATT_ECODE_UNLIKELY;
+
+	rsp = g_attrib_get_buffer(dev->attrib, &length);
+
+	/* Respond with our MTU */
+	len = enc_mtu_resp(mtu, rsp, length);
+	if (!g_attrib_send(dev->attrib, 0, rsp, len, NULL, NULL, NULL))
+		return ATT_ECODE_UNLIKELY;
+
+	return 0;
+}
+
+static uint8_t find_info_handle(const uint8_t *cmd, uint16_t cmd_len,
+				uint8_t *rsp, size_t rsp_size, uint16_t *length)
+{
+	struct gatt_db_attribute *attrib;
+	struct queue *q, *temp;
+	struct att_data_list *adl;
+	int iterator = 0;
+	uint16_t start, end;
+	uint16_t len, queue_len;
+	uint8_t format;
+	uint8_t ret = 0;
+
+	DBG("");
+
+	len = dec_find_info_req(cmd, cmd_len, &start, &end);
+	if (!len)
+		return ATT_ECODE_INVALID_PDU;
+
+	if (start > end || start == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
+	q = queue_new();
+
+	gatt_db_find_information(gatt_db, start, end, q);
+
+	if (queue_isempty(q)) {
+		queue_destroy(q, NULL);
+		return ATT_ECODE_ATTR_NOT_FOUND;
+	}
+
+	temp = queue_new();
+
+	attrib = queue_peek_head(q);
+	/* UUIDS can be only 128 bit and 16 bit */
+	len = bt_uuid_len(gatt_db_attribute_get_type(attrib));
+	if (len != 2 && len != 16) {
+		queue_destroy(q, NULL);
+		queue_destroy(temp, NULL);
+		return ATT_ECODE_UNLIKELY;
+	}
+
+	while (attrib) {
+		const bt_uuid_t *type;
+
+		type = gatt_db_attribute_get_type(attrib);
+		if (bt_uuid_len(type) != len)
+			break;
+
+		queue_push_tail(temp, queue_pop_head(q));
+		attrib = queue_peek_head(q);
+	}
+
+	queue_destroy(q, NULL);
+
+	queue_len = queue_length(temp);
+	adl = att_data_list_alloc(queue_len, len + sizeof(uint16_t));
+	if (!adl) {
+		queue_destroy(temp, NULL);
+		return ATT_ECODE_INSUFF_RESOURCES;
+	}
+
+	while (queue_peek_head(temp)) {
+		uint8_t *value;
+		const bt_uuid_t *type;
+		struct gatt_db_attribute *attrib = queue_pop_head(temp);
+		uint16_t handle;
+
+		type = gatt_db_attribute_get_type(attrib);
+		if (!type)
+			break;
+
+		value = adl->data[iterator++];
+
+		handle = gatt_db_attribute_get_handle(attrib);
+		put_le16(handle, value);
+		memcpy(&value[2], &type->value, len);
+	}
+
+	if (len == 2)
+		format = ATT_FIND_INFO_RESP_FMT_16BIT;
+	else
+		format = ATT_FIND_INFO_RESP_FMT_128BIT;
+
+	len = enc_find_info_resp(format, adl, rsp, rsp_size);
+	if (!len)
+		ret = ATT_ECODE_UNLIKELY;
+
+	*length = len;
+	att_data_list_free(adl);
+	queue_destroy(temp, NULL);
+
+	return ret;
+}
+
+struct find_by_type_request_data {
+	struct gatt_device *device;
+	uint8_t *search_value;
+	size_t search_vlen;
+	uint8_t error;
+};
+
+static void find_by_type_request_cb(struct gatt_db_attribute *attrib,
+								void *user_data)
+{
+	struct find_by_type_request_data *find_data = user_data;
+	struct pending_request *request_data;
+
+	if (find_data->error)
+		return;
+
+	request_data = new0(struct pending_request, 1);
+	request_data->filter_value = malloc0(find_data->search_vlen);
+	if (!request_data->filter_value) {
+		destroy_pending_request(request_data);
+		find_data->error = ATT_ECODE_INSUFF_RESOURCES;
+		return;
+	}
+
+	request_data->attrib = attrib;
+	request_data->filter_vlen = find_data->search_vlen;
+	memcpy(request_data->filter_value, find_data->search_value,
+							find_data->search_vlen);
+
+	queue_push_tail(find_data->device->pending_requests, request_data);
+}
+
+static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *device)
+{
+	uint8_t search_value[cmd_len];
+	size_t search_vlen;
+	uint16_t start, end;
+	bt_uuid_t uuid;
+	uint16_t len;
+	struct find_by_type_request_data data;
+
+	DBG("");
+
+	len = dec_find_by_type_req(cmd, cmd_len, &start, &end, &uuid,
+						search_value, &search_vlen);
+	if (!len)
+		return ATT_ECODE_INVALID_PDU;
+
+	if (start > end || start == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
+	data.error = 0;
+	data.search_vlen = search_vlen;
+	data.search_value = search_value;
+	data.device = device;
+
+	if (gatt_db_find_by_type(gatt_db, start, end, &uuid,
+					find_by_type_request_cb, &data) == 0) {
+		size_t mtu;
+		uint8_t *rsp = g_attrib_get_buffer(device->attrib, &mtu);
+
+		len = enc_error_resp(ATT_OP_FIND_BY_TYPE_REQ, start,
+					ATT_ECODE_ATTR_NOT_FOUND, rsp, mtu);
+		g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL);
+		return 0;
+	}
+
+	if (!data.error)
+		process_dev_pending_requests(device, ATT_OP_FIND_BY_TYPE_REQ);
+
+	return data.error;
+}
+
+static void write_confirm(struct gatt_db_attribute *attrib,
+						int err, void *user_data)
+{
+	if (!err)
+		return;
+
+	error("Error writting attribute %p", attrib);
+}
+
+static void write_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *dev)
+{
+	uint8_t value[cmd_len];
+	struct gatt_db_attribute *attrib;
+	uint32_t permissions;
+	uint16_t handle;
+	uint16_t len;
+	size_t vlen;
+
+	len = dec_write_cmd(cmd, cmd_len, &handle, value, &vlen);
+	if (!len)
+		return;
+
+	if (handle == 0)
+		return;
+
+	attrib = gatt_db_get_attribute(gatt_db, handle);
+	if (!attrib)
+		return;
+
+	permissions = gatt_db_attribute_get_permissions(attrib);
+
+	if (check_device_permissions(dev, cmd[0], permissions))
+		return;
+
+	gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
+						g_attrib_get_att(dev->attrib),
+						write_confirm, NULL);
+}
+
+static void write_signed_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *dev)
+{
+	uint8_t value[cmd_len];
+	uint8_t s[ATT_SIGNATURE_LEN];
+	struct gatt_db_attribute *attrib;
+	uint32_t permissions;
+	uint16_t handle;
+	uint16_t len;
+	size_t vlen;
+	uint8_t csrk[16];
+	uint32_t sign_cnt;
+
+	if (get_cid(dev) != ATT_CID) {
+		error("gatt: Remote tries write signed on BR/EDR bearer");
+		connection_cleanup(dev);
+		return;
+	}
+
+	if (get_sec_level(dev) != BT_SECURITY_LOW) {
+		error("gatt: Remote tries write signed on encrypted link");
+		connection_cleanup(dev);
+		return;
+	}
+
+	if (!bt_get_csrk(&dev->bdaddr, false, csrk, &sign_cnt, NULL)) {
+		error("gatt: No valid csrk from remote device");
+		return;
+	}
+
+	len = dec_signed_write_cmd(cmd, cmd_len, &handle, value, &vlen, s);
+
+	if (handle == 0)
+		return;
+
+	attrib = gatt_db_get_attribute(gatt_db, handle);
+	if (!attrib)
+		return;
+
+	permissions = gatt_db_attribute_get_permissions(attrib);
+
+	if (check_device_permissions(dev, cmd[0], permissions))
+		return;
+
+	if (len) {
+		uint8_t t[ATT_SIGNATURE_LEN];
+		uint32_t r_sign_cnt = get_le32(s);
+
+		if (r_sign_cnt < sign_cnt) {
+			error("gatt: Invalid sign counter (%d<%d)",
+							r_sign_cnt, sign_cnt);
+			return;
+		}
+
+		/* Generate signature and verify it */
+		if (!bt_crypto_sign_att(crypto, csrk, cmd,
+						cmd_len - ATT_SIGNATURE_LEN,
+						r_sign_cnt, t)) {
+			error("gatt: Error when generating att signature");
+			return;
+		}
+
+		if (memcmp(t, s, ATT_SIGNATURE_LEN)) {
+			error("gatt: signature does not match");
+			return;
+		}
+		/* Signature OK, proceed with write */
+		bt_update_sign_counter(&dev->bdaddr, false, r_sign_cnt);
+		gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
+						g_attrib_get_att(dev->attrib),
+						write_confirm, NULL);
+	}
+}
+
+static void attribute_write_cb(struct gatt_db_attribute *attrib, int err,
+								void *user_data)
+{
+	struct pending_request *data = user_data;
+	uint8_t error = err_to_att(err);
+
+	DBG("");
+
+	data->attrib = attrib;
+	data->error = error;
+	data->completed = true;
+}
+
+static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *dev)
+{
+	uint8_t value[cmd_len];
+	struct pending_request *data;
+	struct gatt_db_attribute *attrib;
+	uint32_t permissions;
+	uint16_t handle;
+	uint16_t len;
+	uint8_t error;
+	size_t vlen;
+
+	len = dec_write_req(cmd, cmd_len, &handle, value, &vlen);
+	if (!len)
+		return ATT_ECODE_INVALID_PDU;
+
+	if (handle == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
+	attrib = gatt_db_get_attribute(gatt_db, handle);
+	if (!attrib)
+		return ATT_ECODE_ATTR_NOT_FOUND;
+
+	permissions = gatt_db_attribute_get_permissions(attrib);
+
+	error = check_device_permissions(dev, cmd[0], permissions);
+	if (error)
+		return error;
+
+	data = new0(struct pending_request, 1);
+	data->attrib = attrib;
+
+	queue_push_tail(dev->pending_requests, data);
+
+	if (!gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
+						g_attrib_get_att(dev->attrib),
+						attribute_write_cb, data)) {
+		queue_remove(dev->pending_requests, data);
+		free(data);
+		return ATT_ECODE_UNLIKELY;
+	}
+
+	send_dev_complete_response(dev, cmd[0]);
+
+	return 0;
+}
+
+static uint8_t write_prep_request(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *dev)
+{
+	uint8_t value[cmd_len];
+	struct pending_request *data;
+	struct gatt_db_attribute *attrib;
+	uint32_t permissions;
+	uint16_t handle;
+	uint16_t offset;
+	uint8_t error;
+	uint16_t len;
+	size_t vlen;
+
+	len = dec_prep_write_req(cmd, cmd_len, &handle, &offset,
+						value, &vlen);
+	if (!len)
+		return ATT_ECODE_INVALID_PDU;
+
+	if (handle == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
+	attrib = gatt_db_get_attribute(gatt_db, handle);
+	if (!attrib)
+		return ATT_ECODE_ATTR_NOT_FOUND;
+
+	permissions = gatt_db_attribute_get_permissions(attrib);
+
+	error = check_device_permissions(dev, cmd[0], permissions);
+	if (error)
+		return error;
+
+	data = new0(struct pending_request, 1);
+	data->attrib = attrib;
+	data->offset = offset;
+
+	queue_push_tail(dev->pending_requests, data);
+
+	data->value = g_memdup(value, vlen);
+	data->length = vlen;
+
+	if (!gatt_db_attribute_write(attrib, offset, value, vlen, cmd[0],
+						g_attrib_get_att(dev->attrib),
+						attribute_write_cb, data)) {
+		queue_remove(dev->pending_requests, data);
+		g_free(data->value);
+		free(data);
+
+		return ATT_ECODE_UNLIKELY;
+	}
+
+	send_dev_complete_response(dev, cmd[0]);
+
+	return 0;
+}
+
+static void send_server_write_execute_notify(void *data, void *user_data)
+{
+	struct hal_ev_gatt_server_request_exec_write *ev = user_data;
+	struct pending_trans_data *transaction;
+	struct app_connection *conn = data;
+
+	if (!conn->wait_execute_write)
+		return;
+
+	ev->conn_id = conn->id;
+
+	transaction = conn_add_transact(conn, ATT_OP_EXEC_WRITE_REQ, NULL, 0);
+
+	ev->trans_id = transaction->id;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE, sizeof(*ev), ev);
+}
+
+static uint8_t write_execute_request(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *dev)
+{
+	struct hal_ev_gatt_server_request_exec_write ev;
+	uint8_t value;
+	struct pending_request *data;
+
+	/*
+	 * Check if there was any write prep before.
+	 * TODO: Try to find better error code if possible
+	 */
+	if (!pending_execute_write())
+		return ATT_ECODE_UNLIKELY;
+
+	if (!dec_exec_write_req(cmd, cmd_len, &value))
+		return ATT_ECODE_INVALID_PDU;
+
+	memset(&ev, 0, sizeof(ev));
+	bdaddr2android(&dev->bdaddr, &ev.bdaddr);
+	ev.exec_write = value;
+
+	data = new0(struct pending_request, 1);
+
+	queue_push_tail(dev->pending_requests, data);
+
+	queue_foreach(app_connections, send_server_write_execute_notify, &ev);
+	send_dev_complete_response(dev, cmd[0]);
+
+	return 0;
+}
+
+static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
+{
+	struct gatt_device *dev = user_data;
+	uint8_t status;
+	uint16_t resp_length = 0;
+	size_t length;
+	uint8_t *opdu = g_attrib_get_buffer(dev->attrib, &length);
+
+	DBG("op 0x%02x", ipdu[0]);
+
+	if (len > length) {
+		error("gatt: Too much data on ATT socket %p", opdu);
+		status = ATT_ECODE_INVALID_PDU;
+		goto done;
+	}
+
+	switch (ipdu[0]) {
+	case ATT_OP_READ_BY_GROUP_REQ:
+	case ATT_OP_READ_BY_TYPE_REQ:
+		status = read_by_type(ipdu, len, dev);
+		break;
+	case ATT_OP_READ_REQ:
+	case ATT_OP_READ_BLOB_REQ:
+		status = read_request(ipdu, len, dev);
+		break;
+	case ATT_OP_MTU_REQ:
+		status = mtu_att_handle(ipdu, len, dev);
+		break;
+	case ATT_OP_FIND_INFO_REQ:
+		status = find_info_handle(ipdu, len, opdu, length,
+								&resp_length);
+		break;
+	case ATT_OP_WRITE_REQ:
+		status = write_req_request(ipdu, len, dev);
+		break;
+	case ATT_OP_WRITE_CMD:
+		write_cmd_request(ipdu, len, dev);
+		/* No response on write cmd */
+		return;
+	case ATT_OP_SIGNED_WRITE_CMD:
+		write_signed_cmd_request(ipdu, len, dev);
+		/* No response on write signed cmd */
+		return;
+	case ATT_OP_PREP_WRITE_REQ:
+		status = write_prep_request(ipdu, len, dev);
+		break;
+	case ATT_OP_FIND_BY_TYPE_REQ:
+		status = find_by_type_request(ipdu, len, dev);
+		break;
+	case ATT_OP_EXEC_WRITE_REQ:
+		status = write_execute_request(ipdu, len, dev);
+		break;
+	case ATT_OP_READ_MULTI_REQ:
+	default:
+		DBG("Unsupported request 0x%02x", ipdu[0]);
+		status = ATT_ECODE_REQ_NOT_SUPP;
+		break;
+	}
+
+done:
+	if (status)
+		resp_length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
+									length);
+
+	g_attrib_send(dev->attrib, 0, opdu, resp_length, NULL, NULL, NULL);
+}
+
+static void connect_confirm(GIOChannel *io, void *user_data)
+{
+	struct gatt_device *dev;
+	bdaddr_t dst;
+	GError *gerr = NULL;
+
+	DBG("");
+
+	bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("gatt: bt_io_get: %s", gerr->message);
+		g_error_free(gerr);
+		return;
+	}
+
+	/* TODO Handle collision */
+	dev = find_device_by_addr(&dst);
+	if (!dev) {
+		dev = create_device(&dst);
+	} else {
+		if ((dev->state != DEVICE_DISCONNECTED) &&
+					!(dev->state == DEVICE_CONNECT_INIT &&
+					bt_kernel_conn_control())) {
+			char addr[18];
+
+			ba2str(&dst, addr);
+			info("gatt: Rejecting incoming connection from %s",
+									addr);
+			goto drop;
+		}
+	}
+
+	if (!bt_io_accept(io, connect_cb, device_ref(dev), NULL, NULL)) {
+		error("gatt: failed to accept connection");
+		device_unref(dev);
+		goto drop;
+	}
+
+	queue_foreach(listen_apps, create_app_connection, dev);
+	device_set_state(dev, DEVICE_CONNECT_READY);
+
+	return;
+
+drop:
+	g_io_channel_shutdown(io, TRUE, NULL);
+}
+
+struct gap_srvc_handles {
+	struct gatt_db_attribute *srvc;
+
+	/* Characteristics */
+	struct gatt_db_attribute *dev_name;
+	struct gatt_db_attribute *appear;
+	struct gatt_db_attribute *priv;
+};
+
+static struct gap_srvc_handles gap_srvc_data;
+
+#define APPEARANCE_GENERIC_PHONE 0x0040
+#define PERIPHERAL_PRIVACY_DISABLE 0x00
+
+static void device_name_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	const char *name = bt_get_adapter_name();
+
+	gatt_db_attribute_read_result(attrib, id, 0, (void *) name,
+								strlen(name));
+}
+
+static void register_gap_service(void)
+{
+	uint16_t start, end;
+	bt_uuid_t uuid;
+
+	/* GAP UUID */
+	bt_uuid16_create(&uuid, 0x1800);
+	gap_srvc_data.srvc = gatt_db_add_service(gatt_db, &uuid, true, 7);
+
+	/* Device name characteristic */
+	bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
+	gap_srvc_data.dev_name =
+			gatt_db_service_add_characteristic(gap_srvc_data.srvc,
+							&uuid, GATT_PERM_READ,
+							GATT_CHR_PROP_READ,
+							device_name_read_cb,
+							NULL, NULL);
+
+	/* Appearance */
+	bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+
+	gap_srvc_data.appear =
+			gatt_db_service_add_characteristic(gap_srvc_data.srvc,
+							&uuid, GATT_PERM_READ,
+							GATT_CHR_PROP_READ,
+							NULL, NULL, NULL);
+	if (gap_srvc_data.appear) {
+		uint16_t value;
+		/* Store appearance into db */
+		value = cpu_to_le16(APPEARANCE_GENERIC_PHONE);
+		gatt_db_attribute_write(gap_srvc_data.appear, 0,
+						(void *) &value, sizeof(value),
+						ATT_OP_WRITE_REQ, NULL,
+						write_confirm, NULL);
+	}
+
+	/* Pripheral privacy flag */
+	bt_uuid16_create(&uuid, GATT_CHARAC_PERIPHERAL_PRIV_FLAG);
+	gap_srvc_data.priv =
+			gatt_db_service_add_characteristic(gap_srvc_data.srvc,
+							&uuid, GATT_PERM_READ,
+							GATT_CHR_PROP_READ,
+							NULL, NULL, NULL);
+	if (gap_srvc_data.priv) {
+		uint8_t value;
+
+		/* Store privacy into db */
+		value = PERIPHERAL_PRIVACY_DISABLE;
+		gatt_db_attribute_write(gap_srvc_data.priv, 0,
+						&value, sizeof(value),
+						ATT_OP_WRITE_REQ, NULL,
+						write_confirm, NULL);
+	}
+
+	gatt_db_service_set_active(gap_srvc_data.srvc , true);
+
+	/* SDP */
+	bt_uuid16_create(&uuid, 0x1800);
+	gatt_db_attribute_get_service_handles(gap_srvc_data.srvc, &start, &end);
+	gap_sdp_handle = add_sdp_record(&uuid, start, end,
+						"Generic Access Profile");
+	if (!gap_sdp_handle)
+		error("gatt: Failed to register GAP SDP record");
+}
+
+static void device_info_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	char *buf = user_data;
+
+	gatt_db_attribute_read_result(attrib, id, 0, user_data, strlen(buf));
+}
+
+static void device_info_read_system_id_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	uint8_t pdu[8];
+
+	put_le64(bt_config_get_system_id(), pdu);
+
+	gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu));
+}
+
+static void device_info_read_pnp_id_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	uint8_t pdu[7];
+
+	pdu[0] = bt_config_get_pnp_source();
+	put_le16(bt_config_get_pnp_vendor(), &pdu[1]);
+	put_le16(bt_config_get_pnp_product(), &pdu[3]);
+	put_le16(bt_config_get_pnp_version(), &pdu[5]);
+
+	gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu));
+}
+
+static void register_device_info_service(void)
+{
+	bt_uuid_t uuid;
+	struct gatt_db_attribute *service;
+	uint16_t start_handle, end_handle;
+	const char *data;
+	uint32_t enc_perm = GATT_PERM_READ | GATT_PERM_READ_ENCRYPTED;
+
+	DBG("");
+
+	/* Device Information Service */
+	bt_uuid16_create(&uuid, 0x180a);
+	service = gatt_db_add_service(gatt_db, &uuid, true, 17);
+
+	/* User data are not const hence (void *) cast is used */
+	data = bt_config_get_name();
+	if (data) {
+		bt_uuid16_create(&uuid, GATT_CHARAC_MODEL_NUMBER_STRING);
+		gatt_db_service_add_characteristic(service, &uuid,
+						GATT_PERM_READ,
+						GATT_CHR_PROP_READ,
+						device_info_read_cb, NULL,
+						(void *) data);
+	}
+
+	data = bt_config_get_serial();
+	if (data) {
+		bt_uuid16_create(&uuid, GATT_CHARAC_SERIAL_NUMBER_STRING);
+		gatt_db_service_add_characteristic(service, &uuid,
+						enc_perm, GATT_CHR_PROP_READ,
+						device_info_read_cb, NULL,
+						(void *) data);
+	}
+
+	if (bt_config_get_system_id()) {
+		bt_uuid16_create(&uuid, GATT_CHARAC_SYSTEM_ID);
+		gatt_db_service_add_characteristic(service, &uuid,
+						enc_perm, GATT_CHR_PROP_READ,
+						device_info_read_system_id_cb,
+						NULL, NULL);
+	}
+
+	data = bt_config_get_fw_rev();
+	if (data) {
+		bt_uuid16_create(&uuid, GATT_CHARAC_FIRMWARE_REVISION_STRING);
+		gatt_db_service_add_characteristic(service, &uuid,
+						GATT_PERM_READ,
+						GATT_CHR_PROP_READ,
+						device_info_read_cb, NULL,
+						(void *) data);
+	}
+
+	data = bt_config_get_hw_rev();
+	if (data) {
+		bt_uuid16_create(&uuid, GATT_CHARAC_HARDWARE_REVISION_STRING);
+		gatt_db_service_add_characteristic(service, &uuid,
+						GATT_PERM_READ,
+						GATT_CHR_PROP_READ,
+						device_info_read_cb, NULL,
+						(void *) data);
+	}
+
+	bt_uuid16_create(&uuid, GATT_CHARAC_SOFTWARE_REVISION_STRING);
+	gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_READ,
+					GATT_CHR_PROP_READ, device_info_read_cb,
+					NULL, VERSION);
+
+	data = bt_config_get_vendor();
+	if (data) {
+		bt_uuid16_create(&uuid, GATT_CHARAC_MANUFACTURER_NAME_STRING);
+		gatt_db_service_add_characteristic(service, &uuid,
+						GATT_PERM_READ,
+						GATT_CHR_PROP_READ,
+						device_info_read_cb, NULL,
+						(void *) data);
+	}
+
+	if (bt_config_get_pnp_source()) {
+		bt_uuid16_create(&uuid, GATT_CHARAC_PNP_ID);
+		gatt_db_service_add_characteristic(service, &uuid,
+						GATT_PERM_READ,
+						GATT_CHR_PROP_READ,
+						device_info_read_pnp_id_cb,
+						NULL, NULL);
+	}
+
+	gatt_db_service_set_active(service, true);
+
+	/* SDP */
+	bt_uuid16_create(&uuid, 0x180a);
+	gatt_db_attribute_get_service_handles(service, &start_handle,
+								&end_handle);
+	dis_sdp_handle = add_sdp_record(&uuid, start_handle, end_handle,
+						"Device Information Service");
+	if (!dis_sdp_handle)
+		error("gatt: Failed to register DIS SDP record");
+}
+
+static void gatt_srvc_change_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 gatt_device *dev;
+	bdaddr_t bdaddr;
+
+	if (!get_dst_addr(att, &bdaddr)) {
+		error("gatt: srvc_change_write_cb, could not obtain BDADDR");
+		return;
+	}
+
+	dev = find_device_by_addr(&bdaddr);
+	if (!dev) {
+		error("gatt: Could not find device ?!");
+		return;
+	}
+
+	if (!bt_device_is_bonded(&bdaddr)) {
+		gatt_db_attribute_write_result(attrib, id,
+						ATT_ECODE_AUTHORIZATION);
+		return;
+	}
+
+	/* 2 octets are expected as CCC value */
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+						ATT_ECODE_INVAL_ATTR_VALUE_LEN);
+		return;
+	}
+
+	/* Set services changed indication value */
+	bt_store_gatt_ccc(&bdaddr, get_le16(value));
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void gatt_srvc_change_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct gatt_device *dev;
+	uint8_t pdu[2];
+	bdaddr_t bdaddr;
+
+	if (!get_dst_addr(att, &bdaddr)) {
+		error("gatt: srvc_change_read_cb, could not obtain BDADDR");
+		return;
+	}
+
+	dev = find_device_by_addr(&bdaddr);
+	if (!dev) {
+		error("gatt: Could not find device ?!");
+		return;
+	}
+
+	put_le16(bt_get_gatt_ccc(&dev->bdaddr), pdu);
+
+	gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu));
+}
+
+static void register_gatt_service(void)
+{
+	struct gatt_db_attribute *service;
+	uint16_t start_handle, end_handle;
+	bt_uuid_t uuid;
+
+	DBG("");
+
+	bt_uuid16_create(&uuid, 0x1801);
+	service = gatt_db_add_service(gatt_db, &uuid, true, 4);
+
+	bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
+	service_changed_attrib = gatt_db_service_add_characteristic(service,
+							&uuid, GATT_PERM_NONE,
+							GATT_CHR_PROP_INDICATE,
+							NULL, NULL, NULL);
+
+	bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+	gatt_db_service_add_descriptor(service, &uuid,
+					GATT_PERM_READ | GATT_PERM_WRITE,
+					gatt_srvc_change_read_cb,
+					gatt_srvc_change_write_cb, NULL);
+
+	gatt_db_service_set_active(service, true);
+
+	/* SDP */
+	bt_uuid16_create(&uuid, 0x1801);
+	gatt_db_attribute_get_service_handles(service, &start_handle,
+								&end_handle);
+	gatt_sdp_handle = add_sdp_record(&uuid, start_handle, end_handle,
+						"Generic Attribute Profile");
+
+	if (!gatt_sdp_handle)
+		error("gatt: Failed to register GATT SDP record");
+}
+
+static bool start_listening(void)
+{
+	/* BR/EDR socket */
+	bredr_io = bt_io_listen(NULL, connect_confirm, NULL, NULL, NULL,
+					BT_IO_OPT_SOURCE_TYPE, BDADDR_BREDR,
+					BT_IO_OPT_PSM, ATT_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+
+	/* LE socket */
+	le_io = bt_io_listen(NULL, connect_confirm, NULL, NULL, NULL,
+					BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+					BT_IO_OPT_CID, ATT_CID,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+
+	if (!le_io && !bredr_io) {
+		error("gatt: Failed to start listening IO");
+		return false;
+	}
+
+	return true;
+}
+
+static void gatt_paired_cb(const bdaddr_t *addr)
+{
+	struct gatt_device *dev;
+	char address[18];
+	struct app_connection *conn;
+
+	dev = find_device_by_addr(addr);
+	if (!dev)
+		return;
+
+	ba2str(addr, address);
+	DBG("Paired device %s", address);
+
+	/* conn without app is internal one used for search primary services */
+	conn = find_conn_without_app(dev);
+	if (!conn)
+		return;
+
+	if (conn->timeout_id > 0) {
+		g_source_remove(conn->timeout_id);
+		conn->timeout_id = 0;
+	}
+
+	search_dev_for_srvc(conn, NULL);
+}
+
+static void gatt_unpaired_cb(const bdaddr_t *addr)
+{
+	struct gatt_device *dev;
+	char address[18];
+
+	dev = find_device_by_addr(addr);
+	if (!dev)
+		return;
+
+	ba2str(addr, address);
+	DBG("Unpaired device %s", address);
+
+	queue_remove(gatt_devices, dev);
+	destroy_device(dev);
+}
+
+bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
+{
+	DBG("");
+
+	if (!bt_paired_register(gatt_paired_cb)) {
+		error("gatt: Could not register paired callback");
+		return false;
+	}
+
+	if (!bt_unpaired_register(gatt_unpaired_cb)) {
+		error("gatt: Could not register unpaired callback");
+		return false;
+	}
+
+	if (!start_listening())
+		return false;
+
+	crypto = bt_crypto_new();
+	if (!crypto) {
+		error("gatt: Failed to setup crypto");
+		goto failed;
+	}
+
+	gatt_devices = queue_new();
+	gatt_apps = queue_new();
+	app_connections = queue_new();
+	listen_apps = queue_new();
+	services_sdp = queue_new();
+	gatt_db = gatt_db_new();
+
+	if (!gatt_db) {
+		error("gatt: Failed to allocate memory for database");
+		goto failed;
+	}
+
+	if (!bt_le_register(le_device_found_handler)) {
+		error("gatt: bt_le_register failed");
+		goto failed;
+	}
+
+	bacpy(&adapter_addr, addr);
+
+	hal_ipc = ipc;
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_GATT, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	register_gap_service();
+	register_device_info_service();
+	register_gatt_service();
+
+	info("gatt: LE: %s BR/EDR: %s", le_io ? "enabled" : "disabled",
+					bredr_io ? "enabled" : "disabled");
+
+	return true;
+
+failed:
+
+	bt_paired_unregister(gatt_paired_cb);
+	bt_unpaired_unregister(gatt_unpaired_cb);
+
+	queue_destroy(gatt_apps, NULL);
+	gatt_apps = NULL;
+
+	queue_destroy(gatt_devices, NULL);
+	gatt_devices = NULL;
+
+	queue_destroy(app_connections, NULL);
+	app_connections = NULL;
+
+	queue_destroy(listen_apps, NULL);
+	listen_apps = NULL;
+
+	queue_destroy(services_sdp, NULL);
+	services_sdp = NULL;
+
+	gatt_db_unref(gatt_db);
+	gatt_db = NULL;
+
+	bt_crypto_unref(crypto);
+	crypto = NULL;
+
+	if (le_io) {
+		g_io_channel_unref(le_io);
+		le_io = NULL;
+	}
+
+	if (bredr_io) {
+		g_io_channel_unref(bredr_io);
+		bredr_io = NULL;
+	}
+
+	return false;
+}
+
+void bt_gatt_unregister(void)
+{
+	DBG("");
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_GATT);
+	hal_ipc = NULL;
+
+	queue_destroy(app_connections, destroy_connection);
+	app_connections = NULL;
+
+	queue_destroy(gatt_apps, destroy_gatt_app);
+	gatt_apps = NULL;
+
+	queue_destroy(gatt_devices, destroy_device);
+	gatt_devices = NULL;
+
+	queue_destroy(services_sdp, free_service_sdp_record);
+	services_sdp = NULL;
+
+	queue_destroy(listen_apps, NULL);
+	listen_apps = NULL;
+
+	gatt_db_unref(gatt_db);
+	gatt_db = NULL;
+
+	if (le_io) {
+		g_io_channel_unref(le_io);
+		le_io = NULL;
+	}
+
+	if (bredr_io) {
+		g_io_channel_unref(bredr_io);
+		bredr_io = NULL;
+	}
+
+	if (gap_sdp_handle) {
+		bt_adapter_remove_record(gap_sdp_handle);
+		gap_sdp_handle = 0;
+	}
+
+	if (gatt_sdp_handle) {
+		bt_adapter_remove_record(gatt_sdp_handle);
+		gatt_sdp_handle = 0;
+	}
+
+	if (dis_sdp_handle) {
+		bt_adapter_remove_record(dis_sdp_handle);
+		dis_sdp_handle = 0;
+	}
+
+	bt_crypto_unref(crypto);
+	crypto = NULL;
+
+	bt_le_unregister();
+	bt_unpaired_unregister(gatt_unpaired_cb);
+}
+
+unsigned int bt_gatt_register_app(const char *uuid, gatt_type_t type,
+							gatt_conn_cb_t func)
+{
+	struct gatt_app *app;
+	bt_uuid_t u, u128;
+
+	bt_string_to_uuid(&u, uuid);
+	bt_uuid_to_uuid128(&u, &u128);
+	app = register_app((void *) &u128.value.u128, type);
+	if (!app)
+		return 0;
+
+	app->func = func;
+
+	return app->id;
+}
+
+bool bt_gatt_unregister_app(unsigned int id)
+{
+	uint8_t status;
+
+	status = unregister_app(id);
+
+	return status != HAL_STATUS_FAILED;
+}
+
+bool bt_gatt_connect_app(unsigned int id, const bdaddr_t *addr)
+{
+	uint8_t status;
+
+	status = handle_connect(id, addr, false);
+
+	return status != HAL_STATUS_FAILED;
+}
+
+bool bt_gatt_disconnect_app(unsigned int id, const bdaddr_t *addr)
+{
+	struct app_connection match;
+	struct app_connection *conn;
+	struct gatt_device *device;
+	struct gatt_app *app;
+
+	app = find_app_by_id(id);
+	if (!app)
+		return false;
+
+	device = find_device_by_addr(addr);
+	if (!device)
+		return false;
+
+	match.device = device;
+	match.app = app;
+
+	conn = queue_remove_if(app_connections,
+				match_connection_by_device_and_app, &match);
+	if (!conn)
+		return false;
+
+	destroy_connection(conn);
+
+	return true;
+}
+
+bool bt_gatt_add_autoconnect(unsigned int id, const bdaddr_t *addr)
+{
+	struct gatt_device *dev;
+	struct gatt_app *app;
+
+	DBG("");
+
+	app = find_app_by_id(id);
+	if (!app) {
+		error("gatt: App ID=%d not found", id);
+		return false;
+	}
+
+	dev = find_device_by_addr(addr);
+	if (!dev) {
+		error("gatt: Device not found");
+		return false;
+	}
+
+	/* Take reference of device for auto connect purpose */
+	if (queue_isempty(dev->autoconnect_apps))
+		device_ref(dev);
+
+	if (!queue_find(dev->autoconnect_apps, NULL, INT_TO_PTR(id)))
+		return queue_push_head(dev->autoconnect_apps, INT_TO_PTR(id));
+
+	return true;
+}
+
+void bt_gatt_remove_autoconnect(unsigned int id, const bdaddr_t *addr)
+{
+	struct gatt_device *dev;
+
+	DBG("");
+
+	dev = find_device_by_addr(addr);
+	if (!dev) {
+		error("gatt: Device not found");
+		return;
+	}
+
+	queue_remove(dev->autoconnect_apps, INT_TO_PTR(id));
+
+	if (queue_isempty(dev->autoconnect_apps))
+		remove_autoconnect_device(dev);
+}
diff --git a/repo/android/gatt.h b/repo/android/gatt.h
new file mode 100644
index 0000000..3382df9
--- /dev/null
+++ b/repo/android/gatt.h
@@ -0,0 +1,43 @@
+/*
+ *
+ *  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
+ *
+ */
+
+bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr);
+void bt_gatt_unregister(void);
+
+
+typedef enum {
+	GATT_CLIENT,
+	GATT_SERVER,
+} gatt_type_t;
+
+typedef void (*gatt_conn_cb_t)(const bdaddr_t *addr, int err, void *attrib);
+
+unsigned int bt_gatt_register_app(const char *uuid, gatt_type_t type,
+							gatt_conn_cb_t func);
+bool bt_gatt_unregister_app(unsigned int id);
+
+bool bt_gatt_connect_app(unsigned int id, const bdaddr_t *addr);
+bool bt_gatt_disconnect_app(unsigned int id, const bdaddr_t *addr);
+bool bt_gatt_set_security(const bdaddr_t *bdaddr, int sec_level);
+bool bt_gatt_add_autoconnect(unsigned int id, const bdaddr_t *addr);
+void bt_gatt_remove_autoconnect(unsigned int id, const bdaddr_t *addr);
diff --git a/repo/android/hal-a2dp-sink.c b/repo/android/hal-a2dp-sink.c
new file mode 100644
index 0000000..a0b7ed1
--- /dev/null
+++ b/repo/android/hal-a2dp-sink.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "hal-ipc.h"
+
+static const btav_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_a2dp_conn_state *ev = buf;
+
+	if (cbs->connection_state_cb)
+		cbs->connection_state_cb(ev->state,
+						(bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_audio_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_a2dp_audio_state *ev = buf;
+
+	if (cbs->audio_state_cb)
+		cbs->audio_state_cb(ev->state, (bt_bdaddr_t *)(ev->bdaddr));
+}
+
+static void handle_audio_config(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_a2dp_audio_config *ev = buf;
+
+	if (cbs->audio_config_cb)
+		cbs->audio_config_cb((bt_bdaddr_t *)(ev->bdaddr),
+					ev->sample_rate, ev->channel_count);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_A2DP_CONN_STATE */
+	{ handle_conn_state, false, sizeof(struct hal_ev_a2dp_conn_state) },
+	/* HAL_EV_A2DP_AUDIO_STATE */
+	{ handle_audio_state, false, sizeof(struct hal_ev_a2dp_audio_state) },
+	/* HAL_EV_A2DP_AUDIO_CONFIG */
+	{ handle_audio_config, false, sizeof(struct hal_ev_a2dp_audio_config) },
+};
+
+static bt_status_t a2dp_connect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_a2dp_connect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_a2dp_disconnect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_DISCONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t init(btav_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_A2DP_SINK, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_A2DP_SINK;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_A2DP_SINK);
+	}
+
+	return ret;
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_A2DP_SINK;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_A2DP_SINK);
+
+	cbs = NULL;
+}
+
+static btav_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.connect = a2dp_connect,
+	.disconnect = disconnect,
+	.cleanup = cleanup
+};
+
+btav_interface_t *bt_get_a2dp_sink_interface(void)
+{
+	return &iface;
+}
diff --git a/repo/android/hal-a2dp.c b/repo/android/hal-a2dp.c
new file mode 100644
index 0000000..f572875
--- /dev/null
+++ b/repo/android/hal-a2dp.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "hal-ipc.h"
+
+static const btav_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_a2dp_conn_state *ev = buf;
+
+	if (cbs->connection_state_cb)
+		cbs->connection_state_cb(ev->state,
+						(bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_audio_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_a2dp_audio_state *ev = buf;
+
+	if (cbs->audio_state_cb)
+		cbs->audio_state_cb(ev->state, (bt_bdaddr_t *)(ev->bdaddr));
+}
+
+static void handle_audio_config(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_a2dp_audio_config *ev = buf;
+
+	if (cbs->audio_config_cb)
+		cbs->audio_config_cb((bt_bdaddr_t *)(ev->bdaddr),
+					ev->sample_rate, ev->channel_count);
+#endif
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_A2DP_CONN_STATE */
+	{ handle_conn_state, false, sizeof(struct hal_ev_a2dp_conn_state) },
+	/* HAL_EV_A2DP_AUDIO_STATE */
+	{ handle_audio_state, false, sizeof(struct hal_ev_a2dp_audio_state) },
+	/* HAL_EV_A2DP_AUDIO_CONFIG */
+	{ handle_audio_config, false, sizeof(struct hal_ev_a2dp_audio_config) },
+};
+
+static bt_status_t a2dp_connect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_a2dp_connect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_a2dp_disconnect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t init(btav_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_A2DP, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_A2DP;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_A2DP);
+	}
+
+	return ret;
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_A2DP;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_A2DP);
+
+	cbs = NULL;
+}
+
+static btav_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.connect = a2dp_connect,
+	.disconnect = disconnect,
+	.cleanup = cleanup
+};
+
+btav_interface_t *bt_get_a2dp_interface(void)
+{
+	return &iface;
+}
diff --git a/repo/android/hal-audio-aptx.c b/repo/android/hal-audio-aptx.c
new file mode 100644
index 0000000..a800075
--- /dev/null
+++ b/repo/android/hal-audio-aptx.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2014 Tieto Poland
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <malloc.h>
+#include <dlfcn.h>
+
+#include "audio-msg.h"
+#include "hal-audio.h"
+#include "hal-log.h"
+#include "profiles/audio/a2dp-codecs.h"
+
+#define APTX_SO_NAME	"libbt-aptx.so"
+
+struct aptx_data {
+	a2dp_aptx_t aptx;
+
+	void *enc;
+};
+
+static const a2dp_aptx_t aptx_presets[] = {
+	{
+		.info = {
+			.vendor_id = APTX_VENDOR_ID,
+			.codec_id = APTX_CODEC_ID,
+		},
+		.frequency = APTX_SAMPLING_FREQ_44100 |
+						APTX_SAMPLING_FREQ_48000,
+		.channel_mode = APTX_CHANNEL_MODE_STEREO,
+	},
+	{
+		.info = {
+			.vendor_id = APTX_VENDOR_ID,
+			.codec_id = APTX_CODEC_ID,
+		},
+		.frequency = APTX_SAMPLING_FREQ_48000,
+		.channel_mode = APTX_CHANNEL_MODE_STEREO,
+	},
+	{
+		.info = {
+			.vendor_id = APTX_VENDOR_ID,
+			.codec_id = APTX_CODEC_ID,
+		},
+		.frequency = APTX_SAMPLING_FREQ_44100,
+		.channel_mode = APTX_CHANNEL_MODE_STEREO,
+	},
+};
+
+static void *aptx_handle;
+static int aptx_btencsize;
+static int (*aptx_init)(void *, short);
+static int (*aptx_encode)(void *, void *, void *, void *);
+
+static bool aptx_load(void)
+{
+	const char * (*aptx_version)(void);
+	const char * (*aptx_build)(void);
+	int (*aptx_sizeofenc)(void);
+
+	aptx_handle = dlopen(APTX_SO_NAME, RTLD_LAZY);
+	if (!aptx_handle) {
+		error("APTX: failed to open library %s (%s)", APTX_SO_NAME,
+								dlerror());
+		return false;
+	}
+
+	aptx_version = dlsym(aptx_handle, "aptxbtenc_version");
+	aptx_build = dlsym(aptx_handle, "aptxbtenc_build");
+
+	if (aptx_version && aptx_build)
+		info("APTX: using library version %s build %s", aptx_version(),
+								aptx_build());
+	else
+		warn("APTX: cannot retrieve library version");
+
+	aptx_sizeofenc = dlsym(aptx_handle, "SizeofAptxbtenc");
+	aptx_init = dlsym(aptx_handle, "aptxbtenc_init");
+	aptx_encode = dlsym(aptx_handle, "aptxbtenc_encodestereo");
+	if (!aptx_sizeofenc || !aptx_init || !aptx_encode) {
+		error("APTX: failed initialize library");
+		dlclose(aptx_handle);
+		aptx_handle = NULL;
+		return false;
+	}
+	aptx_btencsize = aptx_sizeofenc();
+
+	info("APTX: codec library initialized (size=%d)", aptx_btencsize);
+
+	return true;
+}
+
+static void aptx_unload(void)
+{
+	if (!aptx_handle)
+		return;
+
+	dlclose(aptx_handle);
+	aptx_handle = NULL;
+}
+
+static int aptx_get_presets(struct audio_preset *preset, size_t *len)
+{
+	int i;
+	int count;
+	size_t new_len = 0;
+	uint8_t *ptr = (uint8_t *) preset;
+	size_t preset_size = sizeof(*preset) + sizeof(a2dp_aptx_t);
+
+	DBG("");
+
+	count = sizeof(aptx_presets) / sizeof(aptx_presets[0]);
+
+	for (i = 0; i < count; i++) {
+		preset = (struct audio_preset *) ptr;
+
+		if (new_len + preset_size > *len)
+			break;
+
+		preset->len = sizeof(a2dp_aptx_t);
+		memcpy(preset->data, &aptx_presets[i], preset->len);
+
+		new_len += preset_size;
+		ptr += preset_size;
+	}
+
+	*len = new_len;
+
+	return i;
+}
+
+static bool aptx_codec_init(struct audio_preset *preset, uint16_t payload_len,
+							void **codec_data)
+{
+	struct aptx_data *aptx_data;
+
+	DBG("");
+
+	if (preset->len != sizeof(a2dp_aptx_t)) {
+		error("APTX: preset size mismatch");
+		return false;
+	}
+
+	aptx_data = malloc(sizeof(*aptx_data));
+	if (!aptx_data)
+		return false;
+
+	memset(aptx_data, 0, sizeof(*aptx_data));
+	memcpy(&aptx_data->aptx, preset->data, preset->len);
+
+	aptx_data->enc = calloc(1, aptx_btencsize);
+	if (!aptx_data->enc) {
+		error("APTX: failed to create encoder");
+		free(aptx_data);
+		return false;
+	}
+
+	/* 1 = big-endian, this is what devices are using */
+	aptx_init(aptx_data->enc, 1);
+
+	*codec_data = aptx_data;
+
+	return true;
+}
+
+static bool aptx_cleanup(void *codec_data)
+{
+	struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+
+	free(aptx_data->enc);
+	free(codec_data);
+
+	return true;
+}
+
+static bool aptx_get_config(void *codec_data, struct audio_input_config *config)
+{
+	struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+
+	config->rate = aptx_data->aptx.frequency & APTX_SAMPLING_FREQ_48000 ?
+								48000 : 44100;
+	config->channels = AUDIO_CHANNEL_OUT_STEREO;
+	config->format = AUDIO_FORMAT_PCM_16_BIT;
+
+	return true;
+}
+
+static size_t aptx_get_buffer_size(void *codec_data)
+{
+	/* TODO: return actual value */
+	return 0;
+}
+
+static size_t aptx_get_mediapacket_duration(void *codec_data)
+{
+	/* TODO: return actual value */
+	return 0;
+}
+
+static ssize_t aptx_encode_mediapacket(void *codec_data, const uint8_t *buffer,
+					size_t len, struct media_packet *mp,
+					size_t mp_data_len, size_t *written)
+{
+	struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+	const int16_t *ptr = (const void *) buffer;
+	size_t bytes_in = 0;
+	size_t bytes_out = 0;
+
+	while ((len - bytes_in) >= 16 && (mp_data_len - bytes_out) >= 4) {
+		int pcm_l[4], pcm_r[4];
+		int i;
+
+		for (i = 0; i < 4; i++) {
+			pcm_l[i] = ptr[0];
+			pcm_r[i] = ptr[1];
+			ptr += 2;
+		}
+
+		aptx_encode(aptx_data->enc, pcm_l, pcm_r, &mp->data[bytes_out]);
+
+		bytes_in += 16;
+		bytes_out += 4;
+	}
+
+	*written = bytes_out;
+
+	return bytes_in;
+}
+
+static bool aptx_update_qos(void *codec_data, uint8_t op)
+{
+	/*
+	 * aptX has constant bitrate of 352kbps (with constant 4:1 compression
+	 * ratio) thus QoS is not possible here.
+	 */
+
+	return false;
+}
+
+static const struct audio_codec codec = {
+	.type = A2DP_CODEC_VENDOR,
+	.use_rtp = false,
+
+	.load = aptx_load,
+	.unload = aptx_unload,
+
+	.get_presets = aptx_get_presets,
+
+	.init = aptx_codec_init,
+	.cleanup = aptx_cleanup,
+	.get_config = aptx_get_config,
+	.get_buffer_size = aptx_get_buffer_size,
+	.get_mediapacket_duration = aptx_get_mediapacket_duration,
+	.encode_mediapacket = aptx_encode_mediapacket,
+	.update_qos = aptx_update_qos,
+};
+
+const struct audio_codec *codec_aptx(void)
+{
+	return &codec;
+}
diff --git a/repo/android/hal-audio-sbc.c b/repo/android/hal-audio-sbc.c
new file mode 100644
index 0000000..7ad3a6a
--- /dev/null
+++ b/repo/android/hal-audio-sbc.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <malloc.h>
+#include <stdlib.h>
+
+#include <sbc/sbc.h>
+#include "audio-msg.h"
+#include "hal-audio.h"
+#include "hal-log.h"
+#include "../profiles/audio/a2dp-codecs.h"
+
+#define MAX_FRAMES_IN_PAYLOAD 15
+
+#define SBC_QUALITY_MIN_BITPOOL	33
+#define SBC_QUALITY_STEP	5
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_payload {
+	unsigned frame_count:4;
+	unsigned rfa0:1;
+	unsigned is_last_fragment:1;
+	unsigned is_first_fragment:1;
+	unsigned is_fragmented:1;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_payload {
+	unsigned is_fragmented:1;
+	unsigned is_first_fragment:1;
+	unsigned is_last_fragment:1;
+	unsigned rfa0:1;
+	unsigned frame_count:4;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct media_packet_sbc {
+	struct media_packet_rtp hdr;
+	struct rtp_payload payload;
+	uint8_t data[0];
+};
+
+struct sbc_data {
+	a2dp_sbc_t sbc;
+
+	sbc_t enc;
+
+	uint16_t payload_len;
+
+	size_t in_frame_len;
+	size_t in_buf_size;
+
+	size_t out_frame_len;
+
+	unsigned frame_duration;
+	unsigned frames_per_packet;
+};
+
+static const a2dp_sbc_t sbc_presets[] = {
+	{
+		.frequency = SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000,
+		.channel_mode = SBC_CHANNEL_MODE_MONO |
+				SBC_CHANNEL_MODE_DUAL_CHANNEL |
+				SBC_CHANNEL_MODE_STEREO |
+				SBC_CHANNEL_MODE_JOINT_STEREO,
+		.subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8,
+		.allocation_method = SBC_ALLOCATION_SNR |
+					SBC_ALLOCATION_LOUDNESS,
+		.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 |
+				SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16,
+		.min_bitpool = MIN_BITPOOL,
+		.max_bitpool = MAX_BITPOOL
+	},
+	{
+		.frequency = SBC_SAMPLING_FREQ_44100,
+		.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+		.subbands = SBC_SUBBANDS_8,
+		.allocation_method = SBC_ALLOCATION_LOUDNESS,
+		.block_length = SBC_BLOCK_LENGTH_16,
+		.min_bitpool = MIN_BITPOOL,
+		.max_bitpool = MAX_BITPOOL
+	},
+	{
+		.frequency = SBC_SAMPLING_FREQ_48000,
+		.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+		.subbands = SBC_SUBBANDS_8,
+		.allocation_method = SBC_ALLOCATION_LOUDNESS,
+		.block_length = SBC_BLOCK_LENGTH_16,
+		.min_bitpool = MIN_BITPOOL,
+		.max_bitpool = MAX_BITPOOL
+	},
+};
+
+static int sbc_get_presets(struct audio_preset *preset, size_t *len)
+{
+	int i;
+	int count;
+	size_t new_len = 0;
+	uint8_t *ptr = (uint8_t *) preset;
+	size_t preset_size = sizeof(*preset) + sizeof(a2dp_sbc_t);
+
+	count = sizeof(sbc_presets) / sizeof(sbc_presets[0]);
+
+	for (i = 0; i < count; i++) {
+		preset = (struct audio_preset *) ptr;
+
+		if (new_len + preset_size > *len)
+			break;
+
+		preset->len = sizeof(a2dp_sbc_t);
+		memcpy(preset->data, &sbc_presets[i], preset->len);
+
+		new_len += preset_size;
+		ptr += preset_size;
+	}
+
+	*len = new_len;
+
+	return i;
+}
+
+static int sbc_freq2int(uint8_t freq)
+{
+	switch (freq) {
+	case SBC_SAMPLING_FREQ_16000:
+		return 16000;
+	case SBC_SAMPLING_FREQ_32000:
+		return 32000;
+	case SBC_SAMPLING_FREQ_44100:
+		return 44100;
+	case SBC_SAMPLING_FREQ_48000:
+		return 48000;
+	default:
+		return 0;
+	}
+}
+
+static const char *sbc_mode2str(uint8_t mode)
+{
+	switch (mode) {
+	case SBC_CHANNEL_MODE_MONO:
+		return "Mono";
+	case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+		return "DualChannel";
+	case SBC_CHANNEL_MODE_STEREO:
+		return "Stereo";
+	case SBC_CHANNEL_MODE_JOINT_STEREO:
+		return "JointStereo";
+	default:
+		return "(unknown)";
+	}
+}
+
+static int sbc_blocks2int(uint8_t blocks)
+{
+	switch (blocks) {
+	case SBC_BLOCK_LENGTH_4:
+		return 4;
+	case SBC_BLOCK_LENGTH_8:
+		return 8;
+	case SBC_BLOCK_LENGTH_12:
+		return 12;
+	case SBC_BLOCK_LENGTH_16:
+		return 16;
+	default:
+		return 0;
+	}
+}
+
+static int sbc_subbands2int(uint8_t subbands)
+{
+	switch (subbands) {
+	case SBC_SUBBANDS_4:
+		return 4;
+	case SBC_SUBBANDS_8:
+		return 8;
+	default:
+		return 0;
+	}
+}
+
+static const char *sbc_allocation2str(uint8_t allocation)
+{
+	switch (allocation) {
+	case SBC_ALLOCATION_SNR:
+		return "SNR";
+	case SBC_ALLOCATION_LOUDNESS:
+		return "Loudness";
+	default:
+		return "(unknown)";
+	}
+}
+
+static void sbc_init_encoder(struct sbc_data *sbc_data)
+{
+	a2dp_sbc_t *in = &sbc_data->sbc;
+	sbc_t *out = &sbc_data->enc;
+
+	sbc_init_a2dp(out, 0L, in, sizeof(*in));
+
+	out->endian = SBC_LE;
+	out->bitpool = in->max_bitpool;
+
+	DBG("frequency=%d channel_mode=%s block_length=%d subbands=%d allocation=%s bitpool=%d-%d",
+			sbc_freq2int(in->frequency),
+			sbc_mode2str(in->channel_mode),
+			sbc_blocks2int(in->block_length),
+			sbc_subbands2int(in->subbands),
+			sbc_allocation2str(in->allocation_method),
+			in->min_bitpool, in->max_bitpool);
+}
+
+static void sbc_codec_calculate(struct sbc_data *sbc_data)
+{
+	size_t in_frame_len;
+	size_t out_frame_len;
+	size_t num_frames;
+
+	in_frame_len = sbc_get_codesize(&sbc_data->enc);
+	out_frame_len = sbc_get_frame_length(&sbc_data->enc);
+	num_frames = sbc_data->payload_len / out_frame_len;
+
+	if (num_frames > MAX_FRAMES_IN_PAYLOAD)
+		num_frames = MAX_FRAMES_IN_PAYLOAD;
+
+	sbc_data->in_frame_len = in_frame_len;
+	sbc_data->in_buf_size = num_frames * in_frame_len;
+
+	sbc_data->out_frame_len = out_frame_len;
+
+	sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc);
+	sbc_data->frames_per_packet = num_frames;
+
+	DBG("in_frame_len=%zu out_frame_len=%zu frames_per_packet=%zu",
+				in_frame_len, out_frame_len, num_frames);
+}
+
+static bool sbc_codec_init(struct audio_preset *preset, uint16_t payload_len,
+							void **codec_data)
+{
+	struct sbc_data *sbc_data;
+
+	if (preset->len != sizeof(a2dp_sbc_t)) {
+		error("SBC: preset size mismatch");
+		return false;
+	}
+
+	sbc_data = calloc(sizeof(struct sbc_data), 1);
+	if (!sbc_data)
+		return false;
+
+	memcpy(&sbc_data->sbc, preset->data, preset->len);
+
+	sbc_init_encoder(sbc_data);
+
+	sbc_data->payload_len = payload_len;
+
+	sbc_codec_calculate(sbc_data);
+
+	*codec_data = sbc_data;
+
+	return true;
+}
+
+static bool sbc_cleanup(void *codec_data)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+	sbc_finish(&sbc_data->enc);
+	free(codec_data);
+
+	return true;
+}
+
+static bool sbc_get_config(void *codec_data, struct audio_input_config *config)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+	switch (sbc_data->sbc.frequency) {
+	case SBC_SAMPLING_FREQ_16000:
+		config->rate = 16000;
+		break;
+	case SBC_SAMPLING_FREQ_32000:
+		config->rate = 32000;
+		break;
+	case SBC_SAMPLING_FREQ_44100:
+		config->rate = 44100;
+		break;
+	case SBC_SAMPLING_FREQ_48000:
+		config->rate = 48000;
+		break;
+	default:
+		return false;
+	}
+	config->channels = sbc_data->sbc.channel_mode == SBC_CHANNEL_MODE_MONO ?
+				AUDIO_CHANNEL_OUT_MONO :
+				AUDIO_CHANNEL_OUT_STEREO;
+	config->format = AUDIO_FORMAT_PCM_16_BIT;
+
+	return true;
+}
+
+static size_t sbc_get_buffer_size(void *codec_data)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+	return sbc_data->in_buf_size;
+}
+
+static size_t sbc_get_mediapacket_duration(void *codec_data)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+	return sbc_data->frame_duration * sbc_data->frames_per_packet;
+}
+
+static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
+					size_t len, struct media_packet *mp,
+					size_t mp_data_len, size_t *written)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+	struct media_packet_sbc *mp_sbc = (struct media_packet_sbc *) mp;
+	size_t consumed = 0;
+	size_t encoded = 0;
+	uint8_t frame_count = 0;
+
+	mp_data_len -= sizeof(mp_sbc->payload);
+
+	while (len - consumed >= sbc_data->in_frame_len &&
+			mp_data_len - encoded >= sbc_data->out_frame_len &&
+			frame_count < sbc_data->frames_per_packet) {
+		ssize_t read;
+		ssize_t written = 0;
+
+		read = sbc_encode(&sbc_data->enc, buffer + consumed,
+				sbc_data->in_frame_len, mp_sbc->data + encoded,
+				mp_data_len - encoded, &written);
+
+		if (read < 0) {
+			error("SBC: failed to encode block at frame %d (%zd)",
+							frame_count, read);
+			break;
+		}
+
+		frame_count++;
+		consumed += read;
+		encoded += written;
+	}
+
+	*written = encoded + sizeof(mp_sbc->payload);
+	mp_sbc->payload.frame_count = frame_count;
+
+	return consumed;
+}
+
+static bool sbc_update_qos(void *codec_data, uint8_t op)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+	uint8_t curr_bitpool = sbc_data->enc.bitpool;
+	uint8_t new_bitpool = curr_bitpool;
+
+	switch (op) {
+	case QOS_POLICY_DEFAULT:
+		new_bitpool = sbc_data->sbc.max_bitpool;
+		break;
+
+	case QOS_POLICY_DECREASE:
+		if (curr_bitpool > SBC_QUALITY_MIN_BITPOOL) {
+			new_bitpool = curr_bitpool - SBC_QUALITY_STEP;
+			if (new_bitpool < SBC_QUALITY_MIN_BITPOOL)
+				new_bitpool = SBC_QUALITY_MIN_BITPOOL;
+		}
+		break;
+	}
+
+	if (new_bitpool == curr_bitpool)
+		return false;
+
+	sbc_data->enc.bitpool = new_bitpool;
+
+	sbc_codec_calculate(sbc_data);
+
+	info("SBC: bitpool changed: %d -> %d", curr_bitpool, new_bitpool);
+
+	return true;
+}
+
+static const struct audio_codec codec = {
+	.type = A2DP_CODEC_SBC,
+	.use_rtp = true,
+
+	.get_presets = sbc_get_presets,
+
+	.init = sbc_codec_init,
+	.cleanup = sbc_cleanup,
+	.get_config = sbc_get_config,
+	.get_buffer_size = sbc_get_buffer_size,
+	.get_mediapacket_duration = sbc_get_mediapacket_duration,
+	.encode_mediapacket = sbc_encode_mediapacket,
+	.update_qos = sbc_update_qos,
+};
+
+const struct audio_codec *codec_sbc(void)
+{
+	return &codec;
+}
diff --git a/repo/android/hal-audio.c b/repo/android/hal-audio.c
new file mode 100644
index 0000000..207101f
--- /dev/null
+++ b/repo/android/hal-audio.c
@@ -0,0 +1,1641 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+
+#include <hardware/audio.h>
+#include <hardware/hardware.h>
+
+#include "audio-msg.h"
+#include "ipc-common.h"
+#include "hal-log.h"
+#include "hal-msg.h"
+#include "hal-audio.h"
+#include "hal-utils.h"
+#include "hal.h"
+
+#define FIXED_A2DP_PLAYBACK_LATENCY_MS 25
+
+#define FIXED_BUFFER_SIZE (20 * 512)
+
+#define MAX_DELAY	100000 /* 100ms */
+
+static const uint8_t a2dp_src_uuid[] = {
+		0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x10, 0x00,
+		0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb };
+
+static int listen_sk = -1;
+static int audio_sk = -1;
+
+static pthread_t ipc_th = 0;
+static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void timespec_add(struct timespec *base, uint64_t time_us,
+							struct timespec *res)
+{
+	res->tv_sec = base->tv_sec + time_us / 1000000;
+	res->tv_nsec = base->tv_nsec + (time_us % 1000000) * 1000;
+
+	if (res->tv_nsec >= 1000000000) {
+		res->tv_sec++;
+		res->tv_nsec -= 1000000000;
+	}
+}
+
+static void timespec_diff(struct timespec *a, struct timespec *b,
+							struct timespec *res)
+{
+	res->tv_sec = a->tv_sec - b->tv_sec;
+	res->tv_nsec = a->tv_nsec - b->tv_nsec;
+
+	if (res->tv_nsec < 0) {
+		res->tv_sec--;
+		res->tv_nsec += 1000000000; /* 1sec */
+	}
+}
+
+static uint64_t timespec_diff_us(struct timespec *a, struct timespec *b)
+{
+	struct timespec res;
+
+	timespec_diff(a, b, &res);
+
+	return res.tv_sec * 1000000ll + res.tv_nsec / 1000ll;
+}
+
+#if defined(ANDROID)
+/*
+ * Bionic does not have clock_nanosleep() prototype in time.h even though
+ * it provides its implementation.
+ */
+extern int clock_nanosleep(clockid_t clock_id, int flags,
+					const struct timespec *request,
+					struct timespec *remain);
+#endif
+
+static struct {
+	const audio_codec_get_t get_codec;
+	bool loaded;
+} audio_codecs[] = {
+		{ .get_codec = codec_aptx, .loaded = false },
+		{ .get_codec = codec_sbc, .loaded = false },
+};
+
+#define NUM_CODECS (sizeof(audio_codecs) / sizeof(audio_codecs[0]))
+
+#define MAX_AUDIO_ENDPOINTS NUM_CODECS
+
+struct audio_endpoint {
+	uint8_t id;
+	const struct audio_codec *codec;
+	void *codec_data;
+	int fd;
+
+	struct media_packet *mp;
+	size_t mp_data_len;
+
+	uint16_t seq;
+	uint32_t samples;
+	struct timespec start;
+
+	bool resync;
+};
+
+static struct audio_endpoint audio_endpoints[MAX_AUDIO_ENDPOINTS];
+
+enum a2dp_state_t {
+	AUDIO_A2DP_STATE_NONE,
+	AUDIO_A2DP_STATE_STANDBY,
+	AUDIO_A2DP_STATE_SUSPENDED,
+	AUDIO_A2DP_STATE_STARTED
+};
+
+struct a2dp_stream_out {
+	struct audio_stream_out stream;
+
+	struct audio_endpoint *ep;
+	enum a2dp_state_t audio_state;
+	struct audio_input_config cfg;
+
+	uint8_t *downmix_buf;
+};
+
+struct a2dp_audio_dev {
+	struct audio_hw_device dev;
+	struct a2dp_stream_out *out;
+};
+
+static int audio_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len,
+			void *param, size_t *rsp_len, void *rsp, int *fd)
+{
+	ssize_t ret;
+	struct msghdr msg;
+	struct iovec iv[2];
+	struct ipc_hdr cmd;
+	char cmsgbuf[CMSG_SPACE(sizeof(int))];
+	struct ipc_status s;
+	size_t s_len = sizeof(s);
+
+	pthread_mutex_lock(&sk_mutex);
+
+	if (audio_sk < 0) {
+		error("audio: Invalid cmd socket passed to audio_ipc_cmd");
+		goto failed;
+	}
+
+	if (!rsp || !rsp_len) {
+		memset(&s, 0, s_len);
+		rsp_len = &s_len;
+		rsp = &s;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.service_id = service_id;
+	cmd.opcode = opcode;
+	cmd.len = len;
+
+	iv[0].iov_base = &cmd;
+	iv[0].iov_len = sizeof(cmd);
+
+	iv[1].iov_base = param;
+	iv[1].iov_len = len;
+
+	msg.msg_iov = iv;
+	msg.msg_iovlen = 2;
+
+	ret = sendmsg(audio_sk, &msg, 0);
+	if (ret < 0) {
+		error("audio: Sending command failed:%s", strerror(errno));
+		goto failed;
+	}
+
+	/* socket was shutdown */
+	if (ret == 0) {
+		error("audio: Command socket closed");
+		goto failed;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&cmd, 0, sizeof(cmd));
+
+	iv[0].iov_base = &cmd;
+	iv[0].iov_len = sizeof(cmd);
+
+	iv[1].iov_base = rsp;
+	iv[1].iov_len = *rsp_len;
+
+	msg.msg_iov = iv;
+	msg.msg_iovlen = 2;
+
+	if (fd) {
+		memset(cmsgbuf, 0, sizeof(cmsgbuf));
+		msg.msg_control = cmsgbuf;
+		msg.msg_controllen = sizeof(cmsgbuf);
+	}
+
+	ret = recvmsg(audio_sk, &msg, 0);
+	if (ret < 0) {
+		error("audio: Receiving command response failed:%s",
+							strerror(errno));
+		goto failed;
+	}
+
+	if (ret < (ssize_t) sizeof(cmd)) {
+		error("audio: Too small response received(%zd bytes)", ret);
+		goto failed;
+	}
+
+	if (cmd.service_id != service_id) {
+		error("audio: Invalid service id (%u vs %u)", cmd.service_id,
+								service_id);
+		goto failed;
+	}
+
+	if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
+		error("audio: Malformed response received(%zd bytes)", ret);
+		goto failed;
+	}
+
+	if (cmd.opcode != opcode && cmd.opcode != AUDIO_OP_STATUS) {
+		error("audio: Invalid opcode received (%u vs %u)",
+						cmd.opcode, opcode);
+		goto failed;
+	}
+
+	if (cmd.opcode == AUDIO_OP_STATUS) {
+		struct ipc_status *s = rsp;
+
+		if (sizeof(*s) != cmd.len) {
+			error("audio: Invalid status length");
+			goto failed;
+		}
+
+		if (s->code == AUDIO_STATUS_SUCCESS) {
+			error("audio: Invalid success status response");
+			goto failed;
+		}
+
+		pthread_mutex_unlock(&sk_mutex);
+
+		return s->code;
+	}
+
+	pthread_mutex_unlock(&sk_mutex);
+
+	/* Receive auxiliary data in msg */
+	if (fd) {
+		struct cmsghdr *cmsg;
+
+		*fd = -1;
+
+		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+					cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+			if (cmsg->cmsg_level == SOL_SOCKET
+					&& cmsg->cmsg_type == SCM_RIGHTS) {
+				memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+				break;
+			}
+		}
+
+		if (*fd < 0)
+			goto failed;
+	}
+
+	*rsp_len = cmd.len;
+
+	return AUDIO_STATUS_SUCCESS;
+
+failed:
+	/* Some serious issue happen on IPC - recover */
+	shutdown(audio_sk, SHUT_RDWR);
+	pthread_mutex_unlock(&sk_mutex);
+
+	return AUDIO_STATUS_FAILED;
+}
+
+static int ipc_open_cmd(const struct audio_codec *codec)
+{
+	uint8_t buf[BLUEZ_AUDIO_MTU];
+	struct audio_cmd_open *cmd = (struct audio_cmd_open *) buf;
+	struct audio_rsp_open rsp;
+	size_t cmd_len = sizeof(buf) - sizeof(*cmd);
+	size_t rsp_len = sizeof(rsp);
+	int result;
+
+	DBG("");
+
+	memcpy(cmd->uuid, a2dp_src_uuid, sizeof(a2dp_src_uuid));
+
+	cmd->codec = codec->type;
+	cmd->presets = codec->get_presets(cmd->preset, &cmd_len);
+
+	cmd_len += sizeof(*cmd);
+
+	result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN, cmd_len, cmd,
+				&rsp_len, &rsp, NULL);
+
+	if (result != AUDIO_STATUS_SUCCESS)
+		return 0;
+
+	return rsp.id;
+}
+
+static int ipc_close_cmd(uint8_t endpoint_id)
+{
+	struct audio_cmd_close cmd;
+	int result;
+
+	DBG("");
+
+	cmd.id = endpoint_id;
+
+	result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE,
+				sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	return result;
+}
+
+static int ipc_open_stream_cmd(uint8_t *endpoint_id, uint16_t *mtu, int *fd,
+						struct audio_preset **caps)
+{
+	char buf[BLUEZ_AUDIO_MTU];
+	struct audio_cmd_open_stream cmd;
+	struct audio_rsp_open_stream *rsp =
+					(struct audio_rsp_open_stream *) &buf;
+	size_t rsp_len = sizeof(buf);
+	int result;
+
+	DBG("");
+
+	if (!caps)
+		return AUDIO_STATUS_FAILED;
+
+	cmd.id = *endpoint_id;
+
+	result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+				sizeof(cmd), &cmd, &rsp_len, rsp, fd);
+	if (result == AUDIO_STATUS_SUCCESS) {
+		size_t buf_len = sizeof(struct audio_preset) +
+					rsp->preset[0].len;
+		*endpoint_id = rsp->id;
+		*mtu = rsp->mtu;
+		*caps = malloc(buf_len);
+		memcpy(*caps, &rsp->preset, buf_len);
+	} else {
+		*caps = NULL;
+	}
+
+	return result;
+}
+
+static int ipc_close_stream_cmd(uint8_t endpoint_id)
+{
+	struct audio_cmd_close_stream cmd;
+	int result;
+
+	DBG("");
+
+	cmd.id = endpoint_id;
+
+	result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	return result;
+}
+
+static int ipc_resume_stream_cmd(uint8_t endpoint_id)
+{
+	struct audio_cmd_resume_stream cmd;
+	int result;
+
+	DBG("");
+
+	cmd.id = endpoint_id;
+
+	result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	return result;
+}
+
+static int ipc_suspend_stream_cmd(uint8_t endpoint_id)
+{
+	struct audio_cmd_suspend_stream cmd;
+	int result;
+
+	DBG("");
+
+	cmd.id = endpoint_id;
+
+	result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	return result;
+}
+
+struct register_state {
+	struct audio_endpoint *ep;
+	bool error;
+};
+
+static void register_endpoint(const struct audio_codec *codec,
+						struct register_state *state)
+{
+	struct audio_endpoint *ep = state->ep;
+
+	/* don't even try to register more endpoints if one failed */
+	if (state->error)
+		return;
+
+	ep->id = ipc_open_cmd(codec);
+
+	if (!ep->id) {
+		state->error = true;
+		error("Failed to register endpoint");
+		return;
+	}
+
+	ep->codec = codec;
+	ep->codec_data = NULL;
+	ep->fd = -1;
+
+	state->ep++;
+}
+
+static int register_endpoints(void)
+{
+	struct register_state state;
+	unsigned int i;
+
+	state.ep = &audio_endpoints[0];
+	state.error = false;
+
+	for (i = 0; i < NUM_CODECS; i++) {
+		const struct audio_codec *codec = audio_codecs[i].get_codec();
+
+		if (!audio_codecs[i].loaded)
+			continue;
+
+		register_endpoint(codec, &state);
+	}
+
+	return state.error ? AUDIO_STATUS_FAILED : AUDIO_STATUS_SUCCESS;
+}
+
+static void unregister_endpoints(void)
+{
+	size_t i;
+
+	for (i = 0; i < MAX_AUDIO_ENDPOINTS; i++) {
+		struct audio_endpoint *ep = &audio_endpoints[i];
+
+		if (ep->id) {
+			ipc_close_cmd(ep->id);
+			memset(ep, 0, sizeof(*ep));
+		}
+	}
+}
+
+static bool open_endpoint(struct audio_endpoint **epp,
+						struct audio_input_config *cfg)
+{
+	struct audio_preset *preset;
+	struct audio_endpoint *ep = *epp;
+	const struct audio_codec *codec;
+	uint16_t mtu;
+	uint16_t payload_len;
+	int fd;
+	size_t i;
+	uint8_t ep_id = 0;
+
+	if (ep)
+		ep_id = ep->id;
+
+	if (ipc_open_stream_cmd(&ep_id, &mtu, &fd, &preset) !=
+							AUDIO_STATUS_SUCCESS)
+		return false;
+
+	DBG("ep_id=%d mtu=%u", ep_id, mtu);
+
+	for (i = 0; i < MAX_AUDIO_ENDPOINTS; i++)
+		if (audio_endpoints[i].id == ep_id) {
+			ep = &audio_endpoints[i];
+			break;
+		}
+
+	if (!ep) {
+		error("Cound not find opened endpoint");
+		goto failed;
+	}
+
+	*epp = ep;
+
+	payload_len = mtu;
+	if (ep->codec->use_rtp)
+		payload_len -= sizeof(struct rtp_header);
+
+	ep->fd = fd;
+
+	codec = ep->codec;
+	codec->init(preset, payload_len, &ep->codec_data);
+	codec->get_config(ep->codec_data, cfg);
+
+	ep->mp = calloc(mtu, 1);
+	if (!ep->mp)
+		goto failed;
+
+	if (ep->codec->use_rtp) {
+		struct media_packet_rtp *mp_rtp =
+					(struct media_packet_rtp *) ep->mp;
+		mp_rtp->hdr.v = 2;
+		mp_rtp->hdr.pt = 0x60;
+		mp_rtp->hdr.ssrc = htonl(1);
+	}
+
+	ep->mp_data_len = payload_len;
+
+	free(preset);
+
+	return true;
+
+failed:
+	close(fd);
+	free(preset);
+
+	return false;
+}
+
+static void close_endpoint(struct audio_endpoint *ep)
+{
+	ipc_close_stream_cmd(ep->id);
+	if (ep->fd >= 0) {
+		close(ep->fd);
+		ep->fd = -1;
+	}
+
+	free(ep->mp);
+
+	ep->codec->cleanup(ep->codec_data);
+	ep->codec_data = NULL;
+}
+
+static bool resume_endpoint(struct audio_endpoint *ep)
+{
+	if (ipc_resume_stream_cmd(ep->id) != AUDIO_STATUS_SUCCESS)
+		return false;
+
+	ep->samples = 0;
+	ep->resync = false;
+
+	ep->codec->update_qos(ep->codec_data, QOS_POLICY_DEFAULT);
+
+	return true;
+}
+
+static void downmix_to_mono(struct a2dp_stream_out *out, const uint8_t *buffer,
+								size_t bytes)
+{
+	const int16_t *input = (const void *) buffer;
+	int16_t *output = (void *) out->downmix_buf;
+	size_t i, frames;
+
+	/* PCM 16bit stereo */
+	frames = bytes / (2 * sizeof(int16_t));
+
+	for (i = 0; i < frames; i++) {
+		int16_t l = get_le16(&input[i * 2]);
+		int16_t r = get_le16(&input[i * 2 + 1]);
+
+		put_le16((l + r) / 2, &output[i]);
+	}
+}
+
+static bool wait_for_endpoint(struct audio_endpoint *ep, bool *writable)
+{
+	int ret;
+
+	while (true) {
+		struct pollfd pollfd;
+
+		pollfd.fd = ep->fd;
+		pollfd.events = POLLOUT;
+		pollfd.revents = 0;
+
+		ret = poll(&pollfd, 1, 500);
+
+		if (ret >= 0) {
+			*writable = !!(pollfd.revents & POLLOUT);
+			break;
+		}
+
+		if (errno != EINTR) {
+			ret = errno;
+			error("poll failed (%d)", ret);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static bool write_to_endpoint(struct audio_endpoint *ep, size_t bytes)
+{
+	struct media_packet *mp = (struct media_packet *) ep->mp;
+	int ret;
+
+	while (true) {
+		ret = write(ep->fd, mp, bytes);
+
+		if (ret >= 0)
+			break;
+
+		/*
+		 * this should not happen so let's issue warning, but do not
+		 * fail, we can try to write next packet
+		 */
+		if (errno == EAGAIN) {
+			ret = errno;
+			warn("write failed (%d)", ret);
+			break;
+		}
+
+		if (errno != EINTR) {
+			ret = errno;
+			error("write failed (%d)", ret);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static bool write_data(struct a2dp_stream_out *out, const void *buffer,
+								size_t bytes)
+{
+	struct audio_endpoint *ep = out->ep;
+	struct media_packet *mp = (struct media_packet *) ep->mp;
+	struct media_packet_rtp *mp_rtp = (struct media_packet_rtp *) ep->mp;
+	size_t free_space = ep->mp_data_len;
+	size_t consumed = 0;
+
+	while (consumed < bytes) {
+		size_t written = 0;
+		ssize_t read;
+		uint32_t samples;
+		int ret;
+		struct timespec current;
+		uint64_t audio_sent, audio_passed;
+		bool do_write = false;
+
+		/*
+		 * prepare media packet in advance so we don't waste time after
+		 * wakeup
+		 */
+		if (ep->codec->use_rtp) {
+			mp_rtp->hdr.sequence_number = htons(ep->seq++);
+			mp_rtp->hdr.timestamp = htonl(ep->samples);
+		}
+		read = ep->codec->encode_mediapacket(ep->codec_data,
+						buffer + consumed,
+						bytes - consumed, mp,
+						free_space, &written);
+
+		/*
+		 * not much we can do here, let's just ignore remaining
+		 * data and continue
+		 */
+		if (read <= 0)
+			return true;
+
+		/* calculate where are we and where we should be */
+		clock_gettime(CLOCK_MONOTONIC, &current);
+		if (!ep->samples)
+			memcpy(&ep->start, &current, sizeof(ep->start));
+		audio_sent = ep->samples * 1000000ll / out->cfg.rate;
+		audio_passed = timespec_diff_us(&current, &ep->start);
+
+		/*
+		 * if we're ahead of stream then wait for next write point,
+		 * if we're lagging more than 100ms then stop writing and just
+		 * skip data until we're back in sync
+		 */
+		if (audio_sent > audio_passed) {
+			struct timespec anchor;
+
+			ep->resync = false;
+
+			timespec_add(&ep->start, audio_sent, &anchor);
+
+			while (true) {
+				ret = clock_nanosleep(CLOCK_MONOTONIC,
+							TIMER_ABSTIME, &anchor,
+							NULL);
+
+				if (!ret)
+					break;
+
+				if (ret != EINTR) {
+					error("clock_nanosleep failed (%d)",
+									ret);
+					return false;
+				}
+			}
+		} else if (!ep->resync) {
+			uint64_t diff = audio_passed - audio_sent;
+
+			if (diff > MAX_DELAY) {
+				warn("lag is %jums, resyncing", diff / 1000);
+
+				ep->codec->update_qos(ep->codec_data,
+							QOS_POLICY_DECREASE);
+				ep->resync = true;
+			}
+		}
+
+		/* we send data only in case codec encoded some data, i.e. some
+		 * codecs do internal buffering and output data only if full
+		 * frame can be encoded
+		 * in resync mode we'll just drop mediapackets
+		 */
+		if (written > 0 && !ep->resync) {
+			/* wait some time for socket to be ready for write,
+			 * but we'll just skip writing data if timeout occurs
+			 */
+			if (!wait_for_endpoint(ep, &do_write))
+				return false;
+
+			if (do_write) {
+				if (ep->codec->use_rtp)
+					written += sizeof(struct rtp_header);
+
+				if (!write_to_endpoint(ep, written))
+					return false;
+			}
+		}
+
+		/*
+		 * AudioFlinger provides 16bit PCM, so sample size is 2 bytes
+		 * multiplied by number of channels. Number of channels is
+		 * simply number of bits set in channels mask.
+		 */
+		samples = read / (2 * popcount(out->cfg.channels));
+		ep->samples += samples;
+		consumed += read;
+	}
+
+	return true;
+}
+
+static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
+								size_t bytes)
+{
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+	const void *in_buf = buffer;
+	size_t in_len = bytes;
+
+	/* just return in case we're closing */
+	if (out->audio_state == AUDIO_A2DP_STATE_NONE)
+		return -1;
+
+	/* We can auto-start only from standby */
+	if (out->audio_state == AUDIO_A2DP_STATE_STANDBY) {
+		DBG("stream in standby, auto-start");
+
+		if (!resume_endpoint(out->ep))
+			return -1;
+
+		out->audio_state = AUDIO_A2DP_STATE_STARTED;
+	}
+
+	if (out->audio_state != AUDIO_A2DP_STATE_STARTED) {
+		error("audio: stream not started");
+		return -1;
+	}
+
+	if (out->ep->fd < 0) {
+		error("audio: no transport socket");
+		return -1;
+	}
+
+	/*
+	 * currently Android audioflinger is not able to provide mono stream on
+	 * A2DP output so down mixing needs to be done in hal-audio plugin.
+	 *
+	 * for reference see
+	 * AudioFlinger::PlaybackThread::readOutputParameters()
+	 * frameworks/av/services/audioflinger/Threads.cpp:1631
+	 */
+	if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) {
+		if (!out->downmix_buf) {
+			error("audio: downmix buffer not initialized");
+			return -1;
+		}
+
+		downmix_to_mono(out, buffer, bytes);
+
+		in_buf = out->downmix_buf;
+		in_len = bytes / 2;
+	}
+
+	if (!write_data(out, in_buf, in_len))
+		return -1;
+
+	return bytes;
+}
+
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+	DBG("");
+
+	return out->cfg.rate;
+}
+
+static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+	DBG("");
+
+	if (rate != out->cfg.rate) {
+		warn("audio: cannot set sample rate to %d", rate);
+		return -1;
+	}
+
+	return 0;
+}
+
+static size_t out_get_buffer_size(const struct audio_stream *stream)
+{
+	DBG("");
+
+	/*
+	 * We should return proper buffer size calculated by codec (so each
+	 * input buffer is encoded into single media packed) but this does not
+	 * work well with AudioFlinger and causes problems. For this reason we
+	 * use magic value here and out_write code takes care of splitting
+	 * input buffer into multiple media packets.
+	 */
+	return FIXED_BUFFER_SIZE;
+}
+
+static uint32_t out_get_channels(const struct audio_stream *stream)
+{
+	DBG("");
+
+	/*
+	 * AudioFlinger can only provide stereo stream, so we return it here and
+	 * later we'll downmix this to mono in case codec requires it
+	 */
+
+	return AUDIO_CHANNEL_OUT_STEREO;
+}
+
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+	DBG("");
+
+	return out->cfg.format;
+}
+
+static int out_set_format(struct audio_stream *stream, audio_format_t format)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+	DBG("");
+
+	if (out->audio_state == AUDIO_A2DP_STATE_STARTED) {
+		if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS)
+			return -1;
+		out->audio_state = AUDIO_A2DP_STATE_STANDBY;
+	}
+
+	return 0;
+}
+
+static int out_dump(const struct audio_stream *stream, int fd)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+	char *kvpair;
+	char *str;
+	char *saveptr;
+	bool enter_suspend = false;
+	bool exit_suspend = false;
+
+	DBG("%s", kvpairs);
+
+	str = strdup(kvpairs);
+	if (!str)
+		return -ENOMEM;
+
+	kvpair = strtok_r(str, ";", &saveptr);
+
+	for (; kvpair && *kvpair; kvpair = strtok_r(NULL, ";", &saveptr)) {
+		char *keyval;
+
+		keyval = strchr(kvpair, '=');
+		if (!keyval)
+			continue;
+
+		*keyval = '\0';
+		keyval++;
+
+		if (!strcmp(kvpair, "closing")) {
+			if (!strcmp(keyval, "true"))
+				out->audio_state = AUDIO_A2DP_STATE_NONE;
+		} else if (!strcmp(kvpair, "A2dpSuspended")) {
+			if (!strcmp(keyval, "true"))
+				enter_suspend = true;
+			else
+				exit_suspend = true;
+		}
+	}
+
+	free(str);
+
+	if (enter_suspend && out->audio_state == AUDIO_A2DP_STATE_STARTED) {
+		if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS)
+			return -1;
+		out->audio_state = AUDIO_A2DP_STATE_SUSPENDED;
+	}
+
+	if (exit_suspend && out->audio_state == AUDIO_A2DP_STATE_SUSPENDED)
+		out->audio_state = AUDIO_A2DP_STATE_STANDBY;
+
+	return 0;
+}
+
+static char *out_get_parameters(const struct audio_stream *stream,
+							const char *keys)
+{
+	DBG("");
+	return strdup("");
+}
+
+static uint32_t out_get_latency(const struct audio_stream_out *stream)
+{
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+	struct audio_endpoint *ep = out->ep;
+	size_t pkt_duration;
+
+	DBG("");
+
+	pkt_duration = ep->codec->get_mediapacket_duration(ep->codec_data);
+
+	return FIXED_A2DP_PLAYBACK_LATENCY_MS + pkt_duration / 1000;
+}
+
+static int out_set_volume(struct audio_stream_out *stream, float left,
+								float right)
+{
+	DBG("");
+	/* volume controlled in audioflinger mixer (digital) */
+	return -ENOSYS;
+}
+
+static int out_get_render_position(const struct audio_stream_out *stream,
+							uint32_t *dsp_frames)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int out_add_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int out_remove_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static uint32_t in_get_sample_rate(const struct audio_stream *stream)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static size_t in_get_buffer_size(const struct audio_stream *stream)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static uint32_t in_get_channels(const struct audio_stream *stream)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static audio_format_t in_get_format(const struct audio_stream *stream)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int in_set_format(struct audio_stream *stream, audio_format_t format)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int in_standby(struct audio_stream *stream)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int in_dump(const struct audio_stream *stream, int fd)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static char *in_get_parameters(const struct audio_stream *stream,
+							const char *keys)
+{
+	DBG("");
+	return strdup("");
+}
+
+static int in_set_gain(struct audio_stream_in *stream, float gain)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static ssize_t in_read(struct audio_stream_in *stream, void *buffer,
+								size_t bytes)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int in_add_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int in_remove_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int audio_open_output_stream_real(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					audio_output_flags_t flags,
+					struct audio_config *config,
+					struct audio_stream_out **stream_out,
+					const char *address)
+{
+	struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
+	struct a2dp_stream_out *out;
+
+	out = calloc(1, sizeof(struct a2dp_stream_out));
+	if (!out)
+		return -ENOMEM;
+
+	DBG("");
+
+	out->stream.common.get_sample_rate = out_get_sample_rate;
+	out->stream.common.set_sample_rate = out_set_sample_rate;
+	out->stream.common.get_buffer_size = out_get_buffer_size;
+	out->stream.common.get_channels = out_get_channels;
+	out->stream.common.get_format = out_get_format;
+	out->stream.common.set_format = out_set_format;
+	out->stream.common.standby = out_standby;
+	out->stream.common.dump = out_dump;
+	out->stream.common.set_parameters = out_set_parameters;
+	out->stream.common.get_parameters = out_get_parameters;
+	out->stream.common.add_audio_effect = out_add_audio_effect;
+	out->stream.common.remove_audio_effect = out_remove_audio_effect;
+	out->stream.get_latency = out_get_latency;
+	out->stream.set_volume = out_set_volume;
+	out->stream.write = out_write;
+	out->stream.get_render_position = out_get_render_position;
+
+	/* We want to autoselect opened endpoint */
+	out->ep = NULL;
+
+	if (!open_endpoint(&out->ep, &out->cfg))
+		goto fail;
+
+	DBG("rate=%d channels=%d format=%d", out->cfg.rate,
+					out->cfg.channels, out->cfg.format);
+
+	if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) {
+		out->downmix_buf = malloc(FIXED_BUFFER_SIZE / 2);
+		if (!out->downmix_buf)
+			goto fail;
+	}
+
+	*stream_out = &out->stream;
+	a2dp_dev->out = out;
+
+	out->audio_state = AUDIO_A2DP_STATE_STANDBY;
+
+	return 0;
+
+fail:
+	error("audio: cannot open output stream");
+	free(out);
+	*stream_out = NULL;
+	return -EIO;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int audio_open_output_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					audio_output_flags_t flags,
+					struct audio_config *config,
+					struct audio_stream_out **stream_out,
+					const char *address)
+{
+	return audio_open_output_stream_real(dev, handle, devices, flags,
+						config, stream_out, address);
+}
+#else
+static int audio_open_output_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					audio_output_flags_t flags,
+					struct audio_config *config,
+					struct audio_stream_out **stream_out)
+{
+	return audio_open_output_stream_real(dev, handle, devices, flags,
+						config, stream_out, NULL);
+}
+#endif
+
+static void audio_close_output_stream(struct audio_hw_device *dev,
+					struct audio_stream_out *stream)
+{
+	struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+	DBG("");
+
+	close_endpoint(a2dp_dev->out->ep);
+
+	free(out->downmix_buf);
+
+	free(stream);
+	a2dp_dev->out = NULL;
+}
+
+static int audio_set_parameters(struct audio_hw_device *dev,
+							const char *kvpairs)
+{
+	struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
+	struct a2dp_stream_out *out = a2dp_dev->out;
+
+	DBG("");
+
+	if (!out)
+		return 0;
+
+	return out->stream.common.set_parameters((struct audio_stream *) out,
+								kvpairs);
+}
+
+static char *audio_get_parameters(const struct audio_hw_device *dev,
+							const char *keys)
+{
+	DBG("");
+	return strdup("");
+}
+
+static int audio_init_check(const struct audio_hw_device *dev)
+{
+	DBG("");
+	return 0;
+}
+
+static int audio_set_voice_volume(struct audio_hw_device *dev, float volume)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int audio_set_master_volume(struct audio_hw_device *dev, float volume)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int audio_set_mode(struct audio_hw_device *dev, int mode)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int audio_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int audio_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static size_t audio_get_input_buffer_size(const struct audio_hw_device *dev,
+					const struct audio_config *config)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int audio_open_input_stream_real(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					struct audio_config *config,
+					struct audio_stream_in **stream_in,
+					audio_input_flags_t flags,
+					const char *address,
+					audio_source_t source)
+{
+	struct audio_stream_in *in;
+
+	DBG("");
+
+	in = calloc(1, sizeof(struct audio_stream_in));
+	if (!in)
+		return -ENOMEM;
+
+	in->common.get_sample_rate = in_get_sample_rate;
+	in->common.set_sample_rate = in_set_sample_rate;
+	in->common.get_buffer_size = in_get_buffer_size;
+	in->common.get_channels = in_get_channels;
+	in->common.get_format = in_get_format;
+	in->common.set_format = in_set_format;
+	in->common.standby = in_standby;
+	in->common.dump = in_dump;
+	in->common.set_parameters = in_set_parameters;
+	in->common.get_parameters = in_get_parameters;
+	in->common.add_audio_effect = in_add_audio_effect;
+	in->common.remove_audio_effect = in_remove_audio_effect;
+	in->set_gain = in_set_gain;
+	in->read = in_read;
+	in->get_input_frames_lost = in_get_input_frames_lost;
+
+	*stream_in = in;
+
+	return 0;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int audio_open_input_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					struct audio_config *config,
+					struct audio_stream_in **stream_in,
+					audio_input_flags_t flags,
+					const char *address,
+					audio_source_t source)
+{
+	return audio_open_input_stream_real(dev, handle, devices, config,
+						stream_in, flags, address,
+						source);
+}
+#else
+static int audio_open_input_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					struct audio_config *config,
+					struct audio_stream_in **stream_in)
+{
+	return audio_open_input_stream_real(dev, handle, devices, config,
+						stream_in, 0, NULL, 0);
+}
+#endif
+
+static void audio_close_input_stream(struct audio_hw_device *dev,
+					struct audio_stream_in *stream_in)
+{
+	DBG("");
+	free(stream_in);
+}
+
+static int audio_dump(const audio_hw_device_t *device, int fd)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int set_master_mute(struct audio_hw_device *dev, bool mute)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int get_master_mute(struct audio_hw_device *dev, bool *mute)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int create_audio_patch(struct audio_hw_device *dev,
+					unsigned int num_sources,
+					const struct audio_port_config *sources,
+					unsigned int num_sinks,
+					const struct audio_port_config *sinks,
+					audio_patch_handle_t *handle)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int release_audio_patch(struct audio_hw_device *dev,
+					audio_patch_handle_t handle)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int get_audio_port(struct audio_hw_device *dev, struct audio_port *port)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int set_audio_port_config(struct audio_hw_device *dev,
+					const struct audio_port_config *config)
+{
+	DBG("");
+	return -ENOSYS;
+}
+#endif
+
+static int audio_close(hw_device_t *device)
+{
+	struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)device;
+	unsigned int i;
+
+	DBG("");
+
+	unregister_endpoints();
+
+	for (i = 0; i < NUM_CODECS; i++) {
+		const struct audio_codec *codec = audio_codecs[i].get_codec();
+
+		if (!audio_codecs[i].loaded)
+			continue;
+
+		if (codec->unload)
+			codec->unload();
+
+		audio_codecs[i].loaded = false;
+	}
+
+	shutdown(listen_sk, SHUT_RDWR);
+	shutdown(audio_sk, SHUT_RDWR);
+
+	pthread_join(ipc_th, NULL);
+
+	close(listen_sk);
+	listen_sk = -1;
+
+	free(a2dp_dev);
+	return 0;
+}
+
+static void *ipc_handler(void *data)
+{
+	bool done = false;
+	struct pollfd pfd;
+	int sk;
+
+	DBG("");
+
+	while (!done) {
+		DBG("Waiting for connection ...");
+
+		sk = accept(listen_sk, NULL, NULL);
+		if (sk < 0) {
+			int err = errno;
+
+			if (err == EINTR)
+				continue;
+
+			if (err != ECONNABORTED && err != EINVAL)
+				error("audio: Failed to accept socket: %d (%s)",
+							err, strerror(err));
+
+			break;
+		}
+
+		pthread_mutex_lock(&sk_mutex);
+		audio_sk = sk;
+		pthread_mutex_unlock(&sk_mutex);
+
+		DBG("Audio IPC: Connected");
+
+		if (register_endpoints() != AUDIO_STATUS_SUCCESS) {
+			error("audio: Failed to register endpoints");
+
+			unregister_endpoints();
+
+			pthread_mutex_lock(&sk_mutex);
+			shutdown(audio_sk, SHUT_RDWR);
+			close(audio_sk);
+			audio_sk = -1;
+			pthread_mutex_unlock(&sk_mutex);
+
+			continue;
+		}
+
+		memset(&pfd, 0, sizeof(pfd));
+		pfd.fd = audio_sk;
+		pfd.events = POLLHUP | POLLERR | POLLNVAL;
+
+		/* Check if socket is still alive. Empty while loop.*/
+		while (poll(&pfd, 1, -1) < 0 && errno == EINTR);
+
+		info("Audio HAL: Socket closed");
+
+		pthread_mutex_lock(&sk_mutex);
+		close(audio_sk);
+		audio_sk = -1;
+		pthread_mutex_unlock(&sk_mutex);
+	}
+
+	/* audio_sk is closed at this point, just cleanup endpoints states */
+	memset(audio_endpoints, 0, sizeof(audio_endpoints));
+
+	info("Closing Audio IPC thread");
+	return NULL;
+}
+
+static int audio_ipc_init(void)
+{
+	struct sockaddr_un addr;
+	int err;
+	int sk;
+
+	DBG("");
+
+	sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+	if (sk < 0) {
+		err = -errno;
+		error("audio: Failed to create socket: %d (%s)", -err,
+								strerror(-err));
+		return err;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+
+	memcpy(addr.sun_path, BLUEZ_AUDIO_SK_PATH,
+					sizeof(BLUEZ_AUDIO_SK_PATH));
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		err = -errno;
+		error("audio: Failed to bind socket: %d (%s)", -err,
+								strerror(-err));
+		goto failed;
+	}
+
+	if (listen(sk, 1) < 0) {
+		err = -errno;
+		error("audio: Failed to listen on the socket: %d (%s)", -err,
+								strerror(-err));
+		goto failed;
+	}
+
+	listen_sk = sk;
+
+	err = pthread_create(&ipc_th, NULL, ipc_handler, NULL);
+	if (err) {
+		err = -err;
+		ipc_th = 0;
+		error("audio: Failed to start Audio IPC thread: %d (%s)",
+							-err, strerror(-err));
+		goto failed;
+	}
+
+	return 0;
+
+failed:
+	close(sk);
+	return err;
+}
+
+static int audio_open(const hw_module_t *module, const char *name,
+							hw_device_t **device)
+{
+	struct a2dp_audio_dev *a2dp_dev;
+	size_t i;
+	int err;
+
+	DBG("");
+
+	if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) {
+		error("audio: interface %s not matching [%s]", name,
+						AUDIO_HARDWARE_INTERFACE);
+		return -EINVAL;
+	}
+
+	err = audio_ipc_init();
+	if (err < 0)
+		return err;
+
+	a2dp_dev = calloc(1, sizeof(struct a2dp_audio_dev));
+	if (!a2dp_dev)
+		return -ENOMEM;
+
+	a2dp_dev->dev.common.tag = HARDWARE_DEVICE_TAG;
+	a2dp_dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
+	a2dp_dev->dev.common.module = (struct hw_module_t *) module;
+	a2dp_dev->dev.common.close = audio_close;
+
+	a2dp_dev->dev.init_check = audio_init_check;
+	a2dp_dev->dev.set_voice_volume = audio_set_voice_volume;
+	a2dp_dev->dev.set_master_volume = audio_set_master_volume;
+	a2dp_dev->dev.set_mode = audio_set_mode;
+	a2dp_dev->dev.set_mic_mute = audio_set_mic_mute;
+	a2dp_dev->dev.get_mic_mute = audio_get_mic_mute;
+	a2dp_dev->dev.set_parameters = audio_set_parameters;
+	a2dp_dev->dev.get_parameters = audio_get_parameters;
+	a2dp_dev->dev.get_input_buffer_size = audio_get_input_buffer_size;
+	a2dp_dev->dev.open_output_stream = audio_open_output_stream;
+	a2dp_dev->dev.close_output_stream = audio_close_output_stream;
+	a2dp_dev->dev.open_input_stream = audio_open_input_stream;
+	a2dp_dev->dev.close_input_stream = audio_close_input_stream;
+	a2dp_dev->dev.dump = audio_dump;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	a2dp_dev->dev.set_master_mute = set_master_mute;
+	a2dp_dev->dev.get_master_mute = get_master_mute;
+	a2dp_dev->dev.create_audio_patch = create_audio_patch;
+	a2dp_dev->dev.release_audio_patch = release_audio_patch;
+	a2dp_dev->dev.get_audio_port = get_audio_port;
+	a2dp_dev->dev.set_audio_port_config = set_audio_port_config;
+#endif
+
+	for (i = 0; i < NUM_CODECS; i++) {
+		const struct audio_codec *codec = audio_codecs[i].get_codec();
+
+		if (codec->load && !codec->load())
+			continue;
+
+		audio_codecs[i].loaded = true;
+	}
+
+	/*
+	 * Note that &a2dp_dev->dev.common is the same pointer as a2dp_dev.
+	 * This results from the structure of following structs:a2dp_audio_dev,
+	 * audio_hw_device. We will rely on this later in the code.
+	 */
+	*device = &a2dp_dev->dev.common;
+
+	return 0;
+}
+
+static struct hw_module_methods_t hal_module_methods = {
+	.open = audio_open,
+};
+
+struct audio_module HAL_MODULE_INFO_SYM = {
+	.common = {
+		.tag = HARDWARE_MODULE_TAG,
+		.version_major = 1,
+		.version_minor = 0,
+		.id = AUDIO_HARDWARE_MODULE_ID,
+		.name = "A2DP Bluez HW HAL",
+		.author = "Intel Corporation",
+		.methods = &hal_module_methods,
+	},
+};
diff --git a/repo/android/hal-audio.h b/repo/android/hal-audio.h
new file mode 100644
index 0000000..2b47412
--- /dev/null
+++ b/repo/android/hal-audio.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <time.h>
+#include <hardware/audio.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_header {
+	unsigned cc:4;
+	unsigned x:1;
+	unsigned p:1;
+	unsigned v:2;
+
+	unsigned pt:7;
+	unsigned m:1;
+
+	uint16_t sequence_number;
+	uint32_t timestamp;
+	uint32_t ssrc;
+	uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_header {
+	unsigned v:2;
+	unsigned p:1;
+	unsigned x:1;
+	unsigned cc:4;
+
+	unsigned m:1;
+	unsigned pt:7;
+
+	uint16_t sequence_number;
+	uint32_t timestamp;
+	uint32_t ssrc;
+	uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct media_packet {
+	uint8_t data[0];
+};
+
+struct media_packet_rtp {
+	struct rtp_header hdr;
+	uint8_t data[0];
+};
+
+struct audio_input_config {
+	uint32_t rate;
+	uint32_t channels;
+	audio_format_t format;
+};
+
+struct audio_codec {
+	uint8_t type;
+	bool use_rtp;
+
+	bool (*load) (void);
+	void (*unload) (void);
+
+	int (*get_presets) (struct audio_preset *preset, size_t *len);
+
+	bool (*init) (struct audio_preset *preset, uint16_t mtu,
+				void **codec_data);
+	bool (*cleanup) (void *codec_data);
+	bool (*get_config) (void *codec_data,
+					struct audio_input_config *config);
+	size_t (*get_buffer_size) (void *codec_data);
+	size_t (*get_mediapacket_duration) (void *codec_data);
+	ssize_t (*encode_mediapacket) (void *codec_data, const uint8_t *buffer,
+					size_t len, struct media_packet *mp,
+					size_t mp_data_len, size_t *written);
+	bool (*update_qos) (void *codec_data, uint8_t op);
+};
+
+#define QOS_POLICY_DEFAULT	0x00
+#define QOS_POLICY_DECREASE	0x01
+
+typedef const struct audio_codec * (*audio_codec_get_t) (void);
+
+const struct audio_codec *codec_sbc(void);
+const struct audio_codec *codec_aptx(void);
diff --git a/repo/android/hal-avrcp-ctrl.c b/repo/android/hal-avrcp-ctrl.c
new file mode 100644
index 0000000..46b77fd
--- /dev/null
+++ b/repo/android/hal-avrcp-ctrl.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hal-utils.h"
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const btrc_ctrl_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void handle_connection_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_ctrl_conn_state *ev = buf;
+
+	if (cbs->connection_state_cb)
+		cbs->connection_state_cb(ev->state,
+						(bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_passthrough_rsp(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_ctrl_passthrough_rsp *ev = buf;
+
+	if (cbs->passthrough_rsp_cb)
+		cbs->passthrough_rsp_cb(ev->id, ev->key_state);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_AVRCP_CTRL_CONN_STATE */
+	{ handle_connection_state, false,
+			sizeof(struct hal_ev_avrcp_ctrl_conn_state) },
+	/* HAL_EV_AVRCP_CTRL_PASSTHROUGH_RSP */
+	{ handle_passthrough_rsp, false,
+			sizeof(struct hal_ev_avrcp_ctrl_passthrough_rsp) },
+};
+
+static bt_status_t init(btrc_ctrl_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_AVRCP_CTRL, ev_handlers,
+				sizeof(ev_handlers) / sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_AVRCP_CTRL;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_AVRCP_CTRL);
+	}
+
+	return ret;
+}
+
+static bt_status_t send_pass_through_cmd(bt_bdaddr_t *bd_addr, uint8_t key_code,
+							uint8_t key_state)
+{
+	struct hal_cmd_avrcp_ctrl_send_passthrough cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	cmd.key_code = key_code;
+	cmd.key_state = key_state;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP_CTRL,
+					HAL_OP_AVRCP_CTRL_SEND_PASSTHROUGH,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_AVRCP_CTRL;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_AVRCP_CTRL);
+
+	cbs = NULL;
+}
+
+static btrc_ctrl_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.send_pass_through_cmd = send_pass_through_cmd,
+	.cleanup = cleanup
+};
+
+btrc_ctrl_interface_t *bt_get_avrcp_ctrl_interface(void)
+{
+	return &iface;
+}
diff --git a/repo/android/hal-avrcp.c b/repo/android/hal-avrcp.c
new file mode 100644
index 0000000..f935eda
--- /dev/null
+++ b/repo/android/hal-avrcp.c
@@ -0,0 +1,688 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hal-utils.h"
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const btrc_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void handle_remote_features(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_remote_features *ev = buf;
+
+	if (cbs->remote_features_cb)
+		cbs->remote_features_cb((bt_bdaddr_t *) (ev->bdaddr),
+								ev->features);
+}
+
+static void handle_get_play_status(void *buf, uint16_t len, int fd)
+{
+	if (cbs->get_play_status_cb)
+		cbs->get_play_status_cb();
+}
+
+static void handle_list_player_attrs(void *buf, uint16_t len, int fd)
+{
+	if (cbs->list_player_app_attr_cb)
+		cbs->list_player_app_attr_cb();
+}
+
+static void handle_list_player_values(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_list_player_values *ev = buf;
+
+	if (cbs->list_player_app_values_cb)
+		cbs->list_player_app_values_cb(ev->attr);
+}
+
+static void handle_get_player_values(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_get_player_values *ev = buf;
+	btrc_player_attr_t attrs[4];
+	int i;
+
+	if (!cbs->get_player_app_value_cb)
+		return;
+
+	/* Convert uint8_t array to btrc_player_attr_t array */
+	for (i = 0; i < ev->number; i++)
+		attrs[i] = ev->attrs[i];
+
+	cbs->get_player_app_value_cb(ev->number, attrs);
+}
+
+static void handle_get_player_attrs_text(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_get_player_attrs_text *ev = buf;
+	btrc_player_attr_t attrs[4];
+	int i;
+
+	if (!cbs->get_player_app_attrs_text_cb)
+		return;
+
+	/* Convert uint8_t array to btrc_player_attr_t array */
+	for (i = 0; i < ev->number; i++)
+		attrs[i] = ev->attrs[i];
+
+	cbs->get_player_app_attrs_text_cb(ev->number, attrs);
+}
+
+static void handle_get_player_values_text(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_get_player_values_text *ev = buf;
+
+	if (cbs->get_player_app_values_text_cb)
+		cbs->get_player_app_values_text_cb(ev->attr, ev->number,
+								ev->values);
+}
+
+static void handle_set_player_value(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_set_player_values *ev = buf;
+	struct hal_avrcp_player_attr_value *attrs;
+	btrc_player_settings_t values;
+	int i;
+
+	if (!cbs->set_player_app_value_cb)
+		return;
+
+	attrs = (struct hal_avrcp_player_attr_value *) ev->attrs;
+
+	/* Convert to btrc_player_settings_t */
+	values.num_attr = ev->number;
+	for (i = 0; i < ev->number; i++) {
+		values.attr_ids[i] = attrs[i].attr;
+		values.attr_values[i] = attrs[i].value;
+	}
+
+	cbs->set_player_app_value_cb(&values);
+}
+
+static void handle_get_element_attrs(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_get_element_attrs *ev = buf;
+	btrc_media_attr_t attrs[BTRC_MAX_APP_SETTINGS];
+	int i;
+
+	if (!cbs->get_element_attr_cb)
+		return;
+
+	/* Convert uint8_t array to btrc_media_attr_t array */
+	for (i = 0; i < ev->number; i++)
+		attrs[i] = ev->attrs[i];
+
+	cbs->get_element_attr_cb(ev->number, attrs);
+}
+
+static void handle_register_notification(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_register_notification *ev = buf;
+
+	if (cbs->register_notification_cb)
+		cbs->register_notification_cb(ev->event, ev->param);
+}
+
+static void handle_volume_changed(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_volume_changed *ev = buf;
+
+	if (cbs->volume_change_cb)
+		cbs->volume_change_cb(ev->volume, ev->type);
+}
+
+static void handle_passthrough_cmd(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_passthrough_cmd *ev = buf;
+
+	if (cbs->passthrough_cmd_cb)
+		cbs->passthrough_cmd_cb(ev->id, ev->state);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_AVRCP_REMOTE_FEATURES */
+	{ handle_remote_features, false,
+			sizeof(struct hal_ev_avrcp_remote_features) },
+	/* HAL_EV_AVRCP_GET_PLAY_STATUS */
+	{ handle_get_play_status, false, 0 },
+	/* HAL_EV_AVRCP_LIST_PLAYER_ATTRS */
+	{ handle_list_player_attrs, false, 0 },
+	/* HAL_EV_AVRCP_LIST_PLAYER_VALUES */
+	{ handle_list_player_values, false,
+			sizeof(struct hal_ev_avrcp_list_player_values) },
+	/* HAL_EV_AVRCP_GET_PLAYER_VALUES */
+	{ handle_get_player_values, true,
+			sizeof(struct hal_ev_avrcp_get_player_values) },
+	/* HAL_EV_AVRCP_GET_PLAYER_ATTRS_TEXT */
+	{ handle_get_player_attrs_text, true,
+			sizeof(struct hal_ev_avrcp_get_player_attrs_text) },
+	/* HAL_EV_AVRCP_GET_PLAYER_VALUES_TEXT */
+	{ handle_get_player_values_text, true,
+			sizeof(struct hal_ev_avrcp_get_player_values_text) },
+	/* HAL_EV_AVRCP_SET_PLAYER_VALUES */
+	{ handle_set_player_value, true,
+			sizeof(struct hal_ev_avrcp_set_player_values) },
+	/* HAL_EV_AVRCP_GET_ELEMENT_ATTRS */
+	{ handle_get_element_attrs, true,
+			sizeof(struct hal_ev_avrcp_get_element_attrs) },
+	/* HAL_EV_AVRCP_REGISTER_NOTIFICATION */
+	{ handle_register_notification, false,
+			sizeof(struct hal_ev_avrcp_register_notification) },
+	/* HAL_EV_AVRCP_VOLUME_CHANGED */
+	{ handle_volume_changed, false,
+			sizeof(struct hal_ev_avrcp_volume_changed) },
+	/* HAL_EV_AVRCP_PASSTHROUGH_CMD */
+	{ handle_passthrough_cmd, false,
+			sizeof(struct hal_ev_avrcp_passthrough_cmd) },
+};
+
+static bt_status_t init(btrc_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_AVRCP, ev_handlers,
+				sizeof(ev_handlers) / sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_AVRCP;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_AVRCP);
+	}
+
+	return ret;
+}
+
+static bt_status_t get_play_status_rsp(btrc_play_status_t status,
+					uint32_t song_len, uint32_t song_pos)
+{
+	struct hal_cmd_avrcp_get_play_status cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.status = status;
+	cmd.duration = song_len;
+	cmd.position = song_pos;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAY_STATUS,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t list_player_app_attr_rsp(int num_attr,
+						btrc_player_attr_t *p_attrs)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_list_player_attrs *cmd = (void *) buf;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (num_attr < 0)
+		return BT_STATUS_PARM_INVALID;
+
+	len = sizeof(*cmd) + num_attr;
+	if (len > IPC_MTU)
+		return BT_STATUS_PARM_INVALID;
+
+	cmd->number = num_attr;
+	memcpy(cmd->attrs, p_attrs, num_attr);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_LIST_PLAYER_ATTRS,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t list_player_app_value_rsp(int num_val, uint8_t *p_vals)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_list_player_values *cmd = (void *) buf;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (num_val < 0)
+		return BT_STATUS_PARM_INVALID;
+
+	len = sizeof(*cmd) + num_val;
+
+	if (len > IPC_MTU)
+		return BT_STATUS_PARM_INVALID;
+
+	cmd->number = num_val;
+	memcpy(cmd->values, p_vals, num_val);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_LIST_PLAYER_VALUES,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_player_app_value_rsp(btrc_player_settings_t *p_vals)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_get_player_attrs *cmd = (void *) buf;
+	size_t len, attrs_len;
+	int i;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!p_vals)
+		return BT_STATUS_PARM_INVALID;
+
+	attrs_len = p_vals->num_attr *
+				sizeof(struct hal_avrcp_player_attr_value);
+	len = sizeof(*cmd) + attrs_len;
+
+	if (len > IPC_MTU)
+		return BT_STATUS_PARM_INVALID;
+
+	cmd->number = p_vals->num_attr;
+
+	for (i = 0; i < p_vals->num_attr; i++) {
+		cmd->attrs[i].attr = p_vals->attr_ids[i];
+		cmd->attrs[i].value = p_vals->attr_values[i];
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_GET_PLAYER_ATTRS,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static int write_text(uint8_t *ptr, uint8_t id, uint8_t *text, size_t *len)
+{
+	struct hal_avrcp_player_setting_text *value = (void *) ptr;
+	size_t attr_len = sizeof(*value);
+
+	if (attr_len + *len > IPC_MTU)
+		return 0;
+
+	value->id = id;
+	value->len = strnlen((const char *) text, BTRC_MAX_ATTR_STR_LEN);
+
+	*len += attr_len;
+
+	if (value->len + *len > IPC_MTU)
+		value->len = IPC_MTU - *len;
+
+	memcpy(value->text, text, value->len);
+
+	*len += value->len;
+
+	return attr_len + value->len;
+}
+
+static uint8_t write_player_setting_text(uint8_t *ptr, uint8_t num_attr,
+					btrc_player_setting_text_t *p_attrs,
+					size_t *len)
+{
+	int i;
+
+	for (i = 0; i < num_attr && *len < IPC_MTU; i++) {
+		int ret;
+
+		ret = write_text(ptr, p_attrs[i].id, p_attrs[i].text, len);
+		if (ret == 0)
+			break;
+
+		ptr += ret;
+	}
+
+	return i;
+}
+
+static bt_status_t get_player_app_attr_text_rsp(int num_attr,
+					btrc_player_setting_text_t *p_attrs)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_get_player_attrs_text *cmd = (void *) buf;
+	uint8_t *ptr;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (num_attr < 0 || num_attr > BTRC_MAX_APP_SETTINGS)
+		return BT_STATUS_PARM_INVALID;
+
+	len = sizeof(*cmd);
+	ptr = (uint8_t *) &cmd->attrs[0];
+	cmd->number = write_player_setting_text(ptr, num_attr, p_attrs, &len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_player_app_value_text_rsp(int num_val,
+					btrc_player_setting_text_t *p_vals)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_get_player_values_text *cmd = (void *) buf;
+	uint8_t *ptr;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (num_val < 0)
+		return BT_STATUS_PARM_INVALID;
+
+	len = sizeof(*cmd);
+	ptr = (uint8_t *) &cmd->values[0];
+	cmd->number = write_player_setting_text(ptr, num_val, p_vals, &len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static uint8_t write_element_attr_text(uint8_t *ptr, uint8_t num_attr,
+					btrc_element_attr_val_t *p_attrs,
+					size_t *len)
+{
+	int i;
+
+	for (i = 0; i < num_attr && *len < IPC_MTU; i++) {
+		int ret;
+
+		ret = write_text(ptr, p_attrs[i].attr_id, p_attrs[i].text, len);
+		if (ret == 0)
+			break;
+
+		ptr += ret;
+	}
+
+	return i;
+}
+
+static bt_status_t get_element_attr_rsp(uint8_t num_attr,
+					btrc_element_attr_val_t *p_attrs)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf;
+	size_t len;
+	uint8_t *ptr;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	len = sizeof(*cmd);
+	ptr = (uint8_t *) &cmd->values[0];
+	cmd->number = write_element_attr_text(ptr, num_attr, p_attrs, &len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t set_player_app_value_rsp(btrc_status_t rsp_status)
+{
+	struct hal_cmd_avrcp_set_player_attrs_value cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.status = rsp_status;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t play_status_changed_rsp(btrc_notification_type_t type,
+						btrc_play_status_t *play_status)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+	size_t len;
+
+	cmd->event = BTRC_EVT_PLAY_STATUS_CHANGED;
+	cmd->type = type;
+	cmd->len = 1;
+	memcpy(cmd->data, play_status, cmd->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t track_change_rsp(btrc_notification_type_t type,
+							btrc_uid_t *track)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+	size_t len;
+
+	cmd->event = BTRC_EVT_TRACK_CHANGE;
+	cmd->type = type;
+	cmd->len = sizeof(*track);
+	memcpy(cmd->data, track, cmd->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t track_reached_end_rsp(btrc_notification_type_t type)
+{
+	struct hal_cmd_avrcp_register_notification cmd;
+
+	cmd.event = BTRC_EVT_TRACK_REACHED_END;
+	cmd.type = type;
+	cmd.len = 0;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t track_reached_start_rsp(btrc_notification_type_t type)
+{
+	struct hal_cmd_avrcp_register_notification cmd;
+
+	cmd.event = BTRC_EVT_TRACK_REACHED_START;
+	cmd.type = type;
+	cmd.len = 0;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t play_pos_changed_rsp(btrc_notification_type_t type,
+							uint32_t *song_pos)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+	size_t len;
+
+	cmd->event = BTRC_EVT_PLAY_POS_CHANGED;
+	cmd->type = type;
+	cmd->len = sizeof(*song_pos);
+	memcpy(cmd->data, song_pos, cmd->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t settings_changed_rsp(btrc_notification_type_t type,
+					btrc_player_settings_t *player_setting)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+	struct hal_avrcp_player_attr_value *attrs;
+	size_t len, param_len;
+	int i;
+
+	param_len = player_setting->num_attr * sizeof(*attrs);
+	len = sizeof(*cmd) + param_len;
+
+	if (len > IPC_MTU)
+		return BT_STATUS_PARM_INVALID;
+
+	cmd->event = BTRC_EVT_APP_SETTINGS_CHANGED;
+	cmd->type = type;
+	cmd->len = param_len;
+
+	attrs = (struct hal_avrcp_player_attr_value *) &cmd->data[0];
+	for (i = 0; i < player_setting->num_attr; i++) {
+		attrs[i].attr = player_setting->attr_ids[i];
+		attrs[i].value = player_setting->attr_values[i];
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t register_notification_rsp(btrc_event_id_t event_id,
+					btrc_notification_type_t type,
+					btrc_register_notification_t *p_param)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	switch (event_id) {
+	case BTRC_EVT_PLAY_STATUS_CHANGED:
+		return play_status_changed_rsp(type, &p_param->play_status);
+	case BTRC_EVT_TRACK_CHANGE:
+		return track_change_rsp(type, &p_param->track);
+	case BTRC_EVT_TRACK_REACHED_END:
+		return track_reached_end_rsp(type);
+	case BTRC_EVT_TRACK_REACHED_START:
+		return track_reached_start_rsp(type);
+	case BTRC_EVT_PLAY_POS_CHANGED:
+		return play_pos_changed_rsp(type, &p_param->song_pos);
+	case BTRC_EVT_APP_SETTINGS_CHANGED:
+		return settings_changed_rsp(type, &p_param->player_setting);
+	default:
+		return BT_STATUS_PARM_INVALID;
+	}
+}
+
+static bt_status_t set_volume(uint8_t volume)
+{
+	struct hal_cmd_avrcp_set_volume cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.value = volume;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_VOLUME,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_AVRCP;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_AVRCP);
+
+	cbs = NULL;
+}
+
+static btrc_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.get_play_status_rsp = get_play_status_rsp,
+	.list_player_app_attr_rsp = list_player_app_attr_rsp,
+	.list_player_app_value_rsp = list_player_app_value_rsp,
+	.get_player_app_value_rsp = get_player_app_value_rsp,
+	.get_player_app_attr_text_rsp = get_player_app_attr_text_rsp,
+	.get_player_app_value_text_rsp = get_player_app_value_text_rsp,
+	.get_element_attr_rsp = get_element_attr_rsp,
+	.set_player_app_value_rsp = set_player_app_value_rsp,
+	.register_notification_rsp = register_notification_rsp,
+	.set_volume = set_volume,
+	.cleanup = cleanup
+};
+
+btrc_interface_t *bt_get_avrcp_interface(void)
+{
+	return &iface;
+}
diff --git a/repo/android/hal-bluetooth.c b/repo/android/hal-bluetooth.c
new file mode 100644
index 0000000..66f4a37
--- /dev/null
+++ b/repo/android/hal-bluetooth.c
@@ -0,0 +1,1136 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+
+#include <cutils/properties.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+#include "hal-utils.h"
+
+static const bt_callbacks_t *bt_hal_cbacks = NULL;
+
+#define enum_prop_to_hal(prop, hal_prop, type) do { \
+	static type e; \
+	prop.val = &e; \
+	prop.len = sizeof(e); \
+	e = *((uint8_t *) (hal_prop->val)); \
+} while (0)
+
+#define enum_prop_from_hal(prop, hal_len, hal_val, enum_type) do { \
+	enum_type e; \
+	if (prop->len != sizeof(e)) { \
+		error("invalid HAL property %u (%u vs %zu), aborting ", \
+					prop->type, prop->len, sizeof(e)); \
+		exit(EXIT_FAILURE); \
+	} \
+	memcpy(&e, prop->val, sizeof(e)); \
+	*((uint8_t *) hal_val) = e; /* enums are mapped to 1 byte */ \
+	*hal_len = 1; \
+} while (0)
+
+static void handle_adapter_state_changed(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_adapter_state_changed *ev = buf;
+
+	DBG("state: %s", bt_state_t2str(ev->state));
+
+	if (bt_hal_cbacks->adapter_state_changed_cb)
+		bt_hal_cbacks->adapter_state_changed_cb(ev->state);
+}
+
+static void adapter_props_to_hal(bt_property_t *send_props,
+					struct hal_property *prop,
+					uint8_t num_props, uint16_t len)
+{
+	void *buf = prop;
+	uint8_t i;
+
+	for (i = 0; i < num_props; i++) {
+		if (sizeof(*prop) + prop->len > len) {
+			error("invalid adapter properties(%zu > %u), aborting",
+					sizeof(*prop) + prop->len, len);
+			exit(EXIT_FAILURE);
+		}
+
+		send_props[i].type = prop->type;
+
+		switch (prop->type) {
+		case HAL_PROP_ADAPTER_TYPE:
+			enum_prop_to_hal(send_props[i], prop,
+							bt_device_type_t);
+			break;
+		case HAL_PROP_ADAPTER_SCAN_MODE:
+			enum_prop_to_hal(send_props[i], prop,
+							bt_scan_mode_t);
+			break;
+		case HAL_PROP_ADAPTER_SERVICE_REC:
+		default:
+			send_props[i].len = prop->len;
+			send_props[i].val = prop->val;
+			break;
+		}
+
+		DBG("prop[%d]: %s", i, btproperty2str(&send_props[i]));
+
+		len -= sizeof(*prop) + prop->len;
+		buf += sizeof(*prop) + prop->len;
+		prop = buf;
+	}
+
+	if (!len)
+		return;
+
+	error("invalid adapter properties (%u bytes left), aborting", len);
+	exit(EXIT_FAILURE);
+}
+
+static void adapter_prop_from_hal(const bt_property_t *property, uint8_t *type,
+						uint16_t *len, void *val)
+{
+	/* type match IPC type */
+	*type = property->type;
+
+	switch (property->type) {
+	case HAL_PROP_ADAPTER_SCAN_MODE:
+		enum_prop_from_hal(property, len, val, bt_scan_mode_t);
+		break;
+	case BT_PROPERTY_BDNAME:
+	case BT_PROPERTY_BDADDR:
+	case BT_PROPERTY_UUIDS:
+	case BT_PROPERTY_CLASS_OF_DEVICE:
+	case BT_PROPERTY_TYPE_OF_DEVICE:
+	case BT_PROPERTY_SERVICE_RECORD:
+	case BT_PROPERTY_ADAPTER_BONDED_DEVICES:
+	case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
+	case BT_PROPERTY_REMOTE_FRIENDLY_NAME:
+	case BT_PROPERTY_REMOTE_RSSI:
+	case BT_PROPERTY_REMOTE_VERSION_INFO:
+	case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP:
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	case BT_PROPERTY_LOCAL_LE_FEATURES:
+#endif
+	default:
+		*len = property->len;
+		memcpy(val, property->val, property->len);
+		break;
+	}
+}
+
+static void device_props_to_hal(bt_property_t *send_props,
+				struct hal_property *prop, uint8_t num_props,
+				uint16_t len)
+{
+	void *buf = prop;
+	uint8_t i;
+
+	for (i = 0; i < num_props; i++) {
+		if (sizeof(*prop) + prop->len > len) {
+			error("invalid device properties (%zu > %u), aborting",
+					sizeof(*prop) + prop->len, len);
+			exit(EXIT_FAILURE);
+		}
+
+		send_props[i].type = prop->type;
+
+		switch (prop->type) {
+		case HAL_PROP_DEVICE_TYPE:
+			enum_prop_to_hal(send_props[i], prop,
+							bt_device_type_t);
+			break;
+		case HAL_PROP_DEVICE_VERSION_INFO:
+		{
+			static bt_remote_version_t e;
+			const struct hal_prop_device_info *p;
+
+			send_props[i].val = &e;
+			send_props[i].len = sizeof(e);
+
+			p = (struct hal_prop_device_info *) prop->val;
+
+			e.manufacturer = p->manufacturer;
+			e.sub_ver = p->sub_version;
+			e.version = p->version;
+		}
+			break;
+		case HAL_PROP_DEVICE_SERVICE_REC:
+		{
+			static bt_service_record_t e;
+			const struct hal_prop_device_service_rec *p;
+
+			send_props[i].val = &e;
+			send_props[i].len = sizeof(e);
+
+			p = (struct hal_prop_device_service_rec *) prop->val;
+
+			memset(&e, 0, sizeof(e));
+			memcpy(&e.channel, &p->channel, sizeof(e.channel));
+			memcpy(e.uuid.uu, p->uuid, sizeof(e.uuid.uu));
+			memcpy(e.name, p->name, p->name_len);
+		}
+			break;
+		default:
+			send_props[i].len = prop->len;
+			send_props[i].val = prop->val;
+			break;
+		}
+
+		len -= sizeof(*prop) + prop->len;
+		buf += sizeof(*prop) + prop->len;
+		prop = buf;
+
+		DBG("prop[%d]: %s", i, btproperty2str(&send_props[i]));
+	}
+
+	if (!len)
+		return;
+
+	error("invalid device properties (%u bytes left), aborting", len);
+	exit(EXIT_FAILURE);
+}
+
+static void handle_adapter_props_changed(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_adapter_props_changed *ev = buf;
+	bt_property_t props[ev->num_props];
+
+	DBG("");
+
+	if (!bt_hal_cbacks->adapter_properties_cb)
+		return;
+
+	len -= sizeof(*ev);
+	adapter_props_to_hal(props, ev->props, ev->num_props, len);
+
+	bt_hal_cbacks->adapter_properties_cb(ev->status, ev->num_props, props);
+}
+
+static void handle_bond_state_change(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_bond_state_changed *ev = buf;
+	bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
+
+	DBG("state %u", ev->state);
+
+	if (bt_hal_cbacks->bond_state_changed_cb)
+		bt_hal_cbacks->bond_state_changed_cb(ev->status, addr,
+								ev->state);
+}
+
+static void handle_pin_request(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_pin_request *ev = buf;
+	/* Those are declared as packed, so it's safe to assign pointers */
+	bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
+	bt_bdname_t *name = (bt_bdname_t *) ev->name;
+
+	DBG("");
+
+	if (bt_hal_cbacks->pin_request_cb)
+		bt_hal_cbacks->pin_request_cb(addr, name, ev->class_of_dev);
+}
+
+static void handle_ssp_request(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_ssp_request *ev = buf;
+	/* Those are declared as packed, so it's safe to assign pointers */
+	bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
+	bt_bdname_t *name = (bt_bdname_t *) ev->name;
+
+	DBG("");
+
+	if (bt_hal_cbacks->ssp_request_cb)
+		bt_hal_cbacks->ssp_request_cb(addr, name, ev->class_of_dev,
+							ev->pairing_variant,
+							ev->passkey);
+}
+
+void bt_thread_associate(void)
+{
+	if (bt_hal_cbacks->thread_evt_cb)
+		bt_hal_cbacks->thread_evt_cb(ASSOCIATE_JVM);
+}
+
+void bt_thread_disassociate(void)
+{
+	if (bt_hal_cbacks->thread_evt_cb)
+		bt_hal_cbacks->thread_evt_cb(DISASSOCIATE_JVM);
+}
+
+static bool interface_ready(void)
+{
+	return bt_hal_cbacks != NULL;
+}
+
+static void handle_discovery_state_changed(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_discovery_state_changed *ev = buf;
+
+	DBG("");
+
+	if (bt_hal_cbacks->discovery_state_changed_cb)
+		bt_hal_cbacks->discovery_state_changed_cb(ev->state);
+}
+
+static void handle_device_found(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_device_found *ev = buf;
+	bt_property_t props[ev->num_props];
+
+	DBG("");
+
+	if (!bt_hal_cbacks->device_found_cb)
+		return;
+
+	len -= sizeof(*ev);
+	device_props_to_hal(props, ev->props, ev->num_props, len);
+
+	bt_hal_cbacks->device_found_cb(ev->num_props, props);
+}
+
+static void handle_device_state_changed(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_remote_device_props *ev = buf;
+	bt_property_t props[ev->num_props];
+
+	DBG("");
+
+	if (!bt_hal_cbacks->remote_device_properties_cb)
+		return;
+
+	len -= sizeof(*ev);
+	device_props_to_hal(props, ev->props, ev->num_props, len);
+
+	bt_hal_cbacks->remote_device_properties_cb(ev->status,
+						(bt_bdaddr_t *)ev->bdaddr,
+						ev->num_props, props);
+}
+
+static void handle_acl_state_changed(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_acl_state_changed *ev = buf;
+	bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
+
+	DBG("state %u", ev->state);
+
+	if (bt_hal_cbacks->acl_state_changed_cb)
+		bt_hal_cbacks->acl_state_changed_cb(ev->status, addr,
+								ev->state);
+}
+
+static void handle_dut_mode_receive(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_dut_mode_receive *ev = buf;
+
+	DBG("");
+
+	if (len != sizeof(*ev) + ev->len) {
+		error("invalid dut mode receive event (%u), aborting", len);
+		exit(EXIT_FAILURE);
+	}
+
+	if (bt_hal_cbacks->dut_mode_recv_cb)
+		bt_hal_cbacks->dut_mode_recv_cb(ev->opcode, ev->data, ev->len);
+}
+
+static void handle_le_test_mode(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_le_test_mode *ev = buf;
+
+	DBG("");
+
+	if (bt_hal_cbacks->le_test_mode_cb)
+		bt_hal_cbacks->le_test_mode_cb(ev->status, ev->num_packets);
+}
+
+static void handle_energy_info(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_energy_info *ev = buf;
+	bt_activity_energy_info info;
+
+	DBG("");
+
+	info.ctrl_state = ev->ctrl_state;
+	info.energy_used = ev->energy_used;
+	info.idle_time = ev->idle_time;
+	info.rx_time = ev->rx_time;
+	info.status = ev->status;
+	info.tx_time = ev->status;
+
+	if (bt_hal_cbacks->energy_info_cb)
+		bt_hal_cbacks->energy_info_cb(&info);
+#endif
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_ADAPTER_STATE_CHANGED */
+	{ handle_adapter_state_changed, false,
+				sizeof(struct hal_ev_adapter_state_changed) },
+	/* HAL_EV_ADAPTER_PROPS_CHANGED */
+	{ handle_adapter_props_changed, true,
+				sizeof(struct hal_ev_adapter_props_changed) +
+				sizeof(struct hal_property) },
+	/* HAL_EV_REMOTE_DEVICE_PROPS */
+	{ handle_device_state_changed, true,
+				sizeof(struct hal_ev_remote_device_props) +
+				sizeof(struct hal_property) },
+	/* HAL_EV_DEVICE_FOUND */
+	{ handle_device_found, true, sizeof(struct hal_ev_device_found) +
+				sizeof(struct hal_property) },
+	/* HAL_EV_DISCOVERY_STATE_CHANGED */
+	{ handle_discovery_state_changed, false,
+				sizeof(struct hal_ev_discovery_state_changed) },
+	/* HAL_EV_PIN_REQUEST */
+	{ handle_pin_request, false, sizeof(struct hal_ev_pin_request) },
+	/* HAL_EV_SSP_REQUEST */
+	{ handle_ssp_request, false, sizeof(struct hal_ev_ssp_request) },
+	/* HAL_EV_BOND_STATE_CHANGED */
+	{ handle_bond_state_change, false,
+				sizeof(struct hal_ev_bond_state_changed) },
+	/* HAL_EV_ACL_STATE_CHANGED */
+	{ handle_acl_state_changed, false,
+				sizeof(struct hal_ev_acl_state_changed) },
+	/* HAL_EV_DUT_MODE_RECEIVE */
+	{ handle_dut_mode_receive, true,
+				sizeof(struct hal_ev_dut_mode_receive) },
+	/* HAL_EV_LE_TEST_MODE */
+	{ handle_le_test_mode, false, sizeof(struct hal_ev_le_test_mode) },
+	/* HAL_EV_ENERGY_INFO */
+	{ handle_energy_info, false, sizeof(struct hal_ev_energy_info) },
+};
+
+static uint8_t get_mode(void)
+{
+	char value[PROPERTY_VALUE_MAX];
+
+	if (get_config("mode", value, NULL) > 0) {
+		if (!strcasecmp(value, "bredr"))
+			return HAL_MODE_BREDR;
+
+		if (!strcasecmp(value, "le"))
+			return HAL_MODE_LE;
+	}
+
+	return HAL_MODE_DEFAULT;
+}
+
+static uint16_t add_prop(const char *prop, uint8_t type, void *buf)
+{
+	struct hal_config_prop *hal_prop = buf;
+
+	hal_prop->type = type;
+	hal_prop->len = strlen(prop) + 1;
+	memcpy(hal_prop->val, prop, hal_prop->len);
+
+	return sizeof(*hal_prop) + hal_prop->len;
+}
+
+static int send_configuration(void)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_configuration *cmd = (void *) buf;
+	char prop[PROPERTY_VALUE_MAX];
+	uint16_t len = sizeof(*cmd);
+
+	cmd->num = 0;
+
+	if (get_config("vendor", prop, "ro.product.manufacturer") > 0) {
+		len += add_prop(prop, HAL_CONFIG_VENDOR, buf + len);
+		cmd->num++;
+	}
+
+	if (get_config("name", prop, "ro.product.name") > 0) {
+		len += add_prop(prop, HAL_CONFIG_NAME, buf + len);
+		cmd->num++;
+	}
+
+	if (get_config("model", prop, "ro.product.model") > 0) {
+		len += add_prop(prop, HAL_CONFIG_MODEL, buf + len);
+		cmd->num++;
+	}
+
+	if (get_config("serialno", prop, "ro.serialno") > 0) {
+		len += add_prop(prop, HAL_CONFIG_SERIAL_NUMBER, buf + len);
+		cmd->num++;
+	}
+
+	if (get_config("systemid", prop, NULL) > 0) {
+		len += add_prop(prop, HAL_CONFIG_SYSTEM_ID, buf + len);
+		cmd->num++;
+	}
+
+	if (get_config("pnpid", prop, NULL) > 0) {
+		len += add_prop(prop, HAL_CONFIG_PNP_ID, buf + len);
+		cmd->num++;
+	}
+
+	if (get_config("fwrev", prop, "ro.build.version.release") > 0) {
+		len += add_prop(prop, HAL_CONFIG_FW_REV, buf + len);
+		cmd->num++;
+	}
+
+	if (get_config("hwrev", prop, "ro.board.platform") > 0) {
+		len += add_prop(prop, HAL_CONFIG_HW_REV, buf + len);
+		cmd->num++;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_CONFIGURATION, len, cmd,
+							NULL, NULL, NULL);
+}
+
+static int init(bt_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int status;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	hal_ipc_register(HAL_SERVICE_ID_BLUETOOTH, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	if (!hal_ipc_init(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH)))
+		return BT_STATUS_FAIL;
+
+	bt_hal_cbacks = callbacks;
+
+	/* Start Android Bluetooth daemon service */
+	if (property_set("bluetooth.start", "daemon") < 0) {
+		error("Failed to set bluetooth.start=daemon");
+		hal_ipc_cleanup();
+		bt_hal_cbacks = NULL;
+		return BT_STATUS_FAIL;
+	}
+
+	if (!hal_ipc_accept()) {
+		hal_ipc_cleanup();
+		bt_hal_cbacks = NULL;
+		return BT_STATUS_FAIL;
+	}
+
+	status = send_configuration();
+	if (status != BT_STATUS_SUCCESS) {
+		error("Failed to send configuration");
+		goto fail;
+	}
+
+	cmd.service_id = HAL_SERVICE_ID_BLUETOOTH;
+	cmd.mode = get_mode();
+	cmd.max_clients = 1;
+
+	status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+	if (status != BT_STATUS_SUCCESS) {
+		error("Failed to register 'bluetooth' service");
+		goto fail;
+	}
+
+	cmd.service_id = HAL_SERVICE_ID_SOCKET;
+	cmd.max_clients = 1;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	cmd.mode = HAL_MODE_SOCKET_DYNAMIC_MAP;
+#else
+	cmd.mode = HAL_MODE_DEFAULT;
+#endif
+
+	status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+	if (status != BT_STATUS_SUCCESS) {
+		error("Failed to register 'socket' service");
+		goto fail;
+	}
+
+	return status;
+
+fail:
+	hal_ipc_cleanup();
+	bt_hal_cbacks = NULL;
+
+	hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH);
+
+	return status;
+}
+
+static int enable(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, 0, NULL,
+							NULL, NULL, NULL);
+}
+
+static int disable(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, 0, NULL,
+							NULL, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	hal_ipc_cleanup();
+
+	hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH);
+
+	bt_hal_cbacks = NULL;
+}
+
+static int get_adapter_properties(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROPS,
+						0, NULL, NULL, NULL, NULL);
+}
+
+static int get_adapter_property(bt_property_type_t type)
+{
+	struct hal_cmd_get_adapter_prop cmd;
+
+	DBG("prop: %s (%d)", bt_property_type_t2str(type), type);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	/* type match IPC type */
+	cmd.type = type;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int set_adapter_property(const bt_property_t *property)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_set_adapter_prop *cmd = (void *) buf;
+	size_t len;
+
+	DBG("prop: %s", btproperty2str(property));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	adapter_prop_from_hal(property, &cmd->type, &cmd->len, cmd->val);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP,
+						len, cmd, NULL, NULL, NULL);
+}
+
+static int get_remote_device_properties(bt_bdaddr_t *remote_addr)
+{
+	struct hal_cmd_get_remote_device_props cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(remote_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_GET_REMOTE_DEVICE_PROPS,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int get_remote_device_property(bt_bdaddr_t *remote_addr,
+						bt_property_type_t type)
+{
+	struct hal_cmd_get_remote_device_prop cmd;
+
+	DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr),
+						bt_property_type_t2str(type));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr));
+
+	/* type match IPC type */
+	cmd.type = type;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_GET_REMOTE_DEVICE_PROP,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int set_remote_device_property(bt_bdaddr_t *remote_addr,
+						const bt_property_t *property)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_set_remote_device_prop *cmd = (void *) buf;
+	size_t len;
+
+	DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr),
+				bt_property_type_t2str(property->type));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd->bdaddr, remote_addr, sizeof(cmd->bdaddr));
+
+	/* type match IPC type */
+	cmd->type = property->type;
+	cmd->len = property->len;
+	memcpy(cmd->val, property->val, property->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_SET_REMOTE_DEVICE_PROP,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static int get_remote_service_record(bt_bdaddr_t *remote_addr, bt_uuid_t *uuid)
+{
+	struct hal_cmd_get_remote_service_rec cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(remote_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr));
+	memcpy(cmd.uuid, uuid, sizeof(cmd.uuid));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_GET_REMOTE_SERVICE_REC,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int get_remote_services(bt_bdaddr_t *remote_addr)
+{
+	struct hal_cmd_get_remote_services cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(remote_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICES,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int start_discovery(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY, 0,
+						NULL, NULL, NULL, NULL);
+}
+
+static int cancel_discovery(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY, 0,
+						NULL, NULL, NULL, NULL);
+}
+
+static int create_bond_real(const bt_bdaddr_t *bd_addr, int transport)
+{
+	struct hal_cmd_create_bond cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.transport = transport;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int create_bond(const bt_bdaddr_t *bd_addr, int transport)
+{
+	return create_bond_real(bd_addr, transport);
+}
+#else
+static int create_bond(const bt_bdaddr_t *bd_addr)
+{
+	return create_bond_real(bd_addr, BT_TRANSPORT_UNKNOWN);
+}
+#endif
+
+static int cancel_bond(const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_cancel_bond cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int remove_bond(const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_remove_bond cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int pin_reply(const bt_bdaddr_t *bd_addr, uint8_t accept,
+				uint8_t pin_len, bt_pin_code_t *pin_code)
+{
+	struct hal_cmd_pin_reply cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	cmd.accept = accept;
+	cmd.pin_len = pin_len;
+	memcpy(cmd.pin_code, pin_code, sizeof(cmd.pin_code));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int ssp_reply(const bt_bdaddr_t *bd_addr, bt_ssp_variant_t variant,
+					uint8_t accept, uint32_t passkey)
+{
+	struct hal_cmd_ssp_reply cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	/* type match IPC type */
+	cmd.ssp_variant = variant;
+	cmd.accept = accept;
+	cmd.passkey = passkey;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static const void *get_profile_interface(const char *profile_id)
+{
+	DBG("%s", profile_id);
+
+	if (!interface_ready())
+		return NULL;
+
+	if (!strcmp(profile_id, BT_PROFILE_SOCKETS_ID))
+		return bt_get_socket_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_HIDHOST_ID))
+		return bt_get_hidhost_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_PAN_ID))
+		return bt_get_pan_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_ADVANCED_AUDIO_ID))
+		return bt_get_a2dp_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_AV_RC_ID))
+		return bt_get_avrcp_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_HANDSFREE_ID))
+		return bt_get_handsfree_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_GATT_ID))
+		return bt_get_gatt_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_HEALTH_ID))
+		return bt_get_health_interface();
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	if (!strcmp(profile_id, BT_PROFILE_AV_RC_CTRL_ID))
+		return bt_get_avrcp_ctrl_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_HANDSFREE_CLIENT_ID))
+		return bt_get_hf_client_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_MAP_CLIENT_ID))
+		return bt_get_map_client_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_ADVANCED_AUDIO_SINK_ID))
+		return bt_get_a2dp_sink_interface();
+#endif
+
+	return NULL;
+}
+
+static int dut_mode_configure(uint8_t enable)
+{
+	struct hal_cmd_dut_mode_conf cmd;
+
+	DBG("enable %u", enable);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.enable = enable;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int dut_mode_send(uint16_t opcode, uint8_t *buf, uint8_t buf_len)
+{
+	char cmd_buf[IPC_MTU];
+	struct hal_cmd_dut_mode_send *cmd = (void *) cmd_buf;
+	size_t len;
+
+	DBG("opcode %u len %u", opcode, buf_len);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->opcode = opcode;
+	cmd->len = buf_len;
+	memcpy(cmd->data, buf, cmd->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND,
+						len, cmd, NULL, NULL, NULL);
+}
+
+static int le_test_mode(uint16_t opcode, uint8_t *buf, uint8_t buf_len)
+{
+	char cmd_buf[IPC_MTU];
+	struct hal_cmd_le_test_mode *cmd = (void *) cmd_buf;
+	size_t len;
+
+	DBG("opcode %u len %u", opcode, buf_len);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->opcode = opcode;
+	cmd->len = buf_len;
+	memcpy(cmd->data, buf, cmd->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE,
+						len, cmd, NULL, NULL, NULL);
+}
+
+static int config_hci_snoop_log(uint8_t enable)
+{
+	const char *property;
+
+	DBG("enable %u", enable);
+
+	property = enable ? "bluetooth.start" : "bluetooth.stop";
+
+	if (property_set(property, "snoop") < 0) {
+		error("Failed to set %s=snoop", property);
+		return BT_STATUS_FAIL;
+	}
+
+	return BT_STATUS_SUCCESS;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int get_connection_state(const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_get_connection_state cmd;
+	struct hal_rsp_get_connection_state rsp;
+	size_t rsp_len = sizeof(rsp);
+	bt_status_t status;
+
+	DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+	if (!interface_ready())
+		return 0;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	status = hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_CONNECTION_STATE, sizeof(cmd), &cmd,
+			&rsp_len, &rsp, NULL);
+
+	if (status != BT_STATUS_SUCCESS)
+		return 0;
+
+	return rsp.connection_state;
+}
+
+static int set_os_callouts(bt_os_callouts_t *callouts)
+{
+	DBG("callouts: %p", callouts);
+
+	/* TODO: implement */
+
+	return BT_STATUS_SUCCESS;
+}
+
+static int read_energy_info(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_READ_ENERGY_INFO, 0,
+							NULL, NULL, NULL, NULL);
+}
+#endif
+
+static const bt_interface_t bluetooth_if = {
+	.size = sizeof(bt_interface_t),
+	.init = init,
+	.enable = enable,
+	.disable = disable,
+	.cleanup = cleanup,
+	.get_adapter_properties = get_adapter_properties,
+	.get_adapter_property = get_adapter_property,
+	.set_adapter_property = set_adapter_property,
+	.get_remote_device_properties = get_remote_device_properties,
+	.get_remote_device_property = get_remote_device_property,
+	.set_remote_device_property = set_remote_device_property,
+	.get_remote_service_record = get_remote_service_record,
+	.get_remote_services = get_remote_services,
+	.start_discovery = start_discovery,
+	.cancel_discovery = cancel_discovery,
+	.create_bond = create_bond,
+	.remove_bond = remove_bond,
+	.cancel_bond = cancel_bond,
+	.pin_reply = pin_reply,
+	.ssp_reply = ssp_reply,
+	.get_profile_interface = get_profile_interface,
+	.dut_mode_configure = dut_mode_configure,
+	.dut_mode_send = dut_mode_send,
+	.le_test_mode = le_test_mode,
+	.config_hci_snoop_log = config_hci_snoop_log,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.get_connection_state = get_connection_state,
+	.set_os_callouts = set_os_callouts,
+	.read_energy_info = read_energy_info,
+#endif
+};
+
+static const bt_interface_t *get_bluetooth_interface(void)
+{
+	DBG("");
+
+	return &bluetooth_if;
+}
+
+static int close_bluetooth(struct hw_device_t *device)
+{
+	DBG("");
+
+	cleanup();
+
+	free(device);
+
+	return 0;
+}
+
+static int open_bluetooth(const struct hw_module_t *module, char const *name,
+					struct hw_device_t **device)
+{
+	bluetooth_device_t *dev = malloc(sizeof(bluetooth_device_t));
+
+	DBG("");
+
+	if (!dev) {
+		error("Failed to allocate memory for device");
+		return -ENOMEM;
+	}
+
+	memset(dev, 0, sizeof(bluetooth_device_t));
+	dev->common.tag = HARDWARE_DEVICE_TAG;
+	dev->common.version = 0;
+	dev->common.module = (struct hw_module_t *) module;
+	dev->common.close = close_bluetooth;
+	dev->get_bluetooth_interface = get_bluetooth_interface;
+
+	*device = (struct hw_device_t *) dev;
+
+	return 0;
+}
+
+static struct hw_module_methods_t bluetooth_module_methods = {
+	.open = open_bluetooth,
+};
+
+struct hw_module_t HAL_MODULE_INFO_SYM = {
+	.tag = HARDWARE_MODULE_TAG,
+	.version_major = 1,
+	.version_minor = 0,
+	.id = BT_HARDWARE_MODULE_ID,
+	.name = "BlueZ Bluetooth stack",
+	.author = "Intel Corporation",
+	.methods = &bluetooth_module_methods
+};
diff --git a/repo/android/hal-gatt.c b/repo/android/hal-gatt.c
new file mode 100644
index 0000000..f7217c7
--- /dev/null
+++ b/repo/android/hal-gatt.c
@@ -0,0 +1,2103 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+#include "hal-utils.h"
+
+static const btgatt_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void gatt_id_from_hal(btgatt_gatt_id_t *to,
+						struct hal_gatt_gatt_id *from)
+{
+	memcpy(&to->uuid, from->uuid, sizeof(to->uuid));
+	to->inst_id = from->inst_id;
+}
+
+static void gatt_id_to_hal(struct hal_gatt_gatt_id *to, btgatt_gatt_id_t *from)
+{
+	memcpy(to->uuid, &from->uuid, sizeof(from->uuid));
+	to->inst_id = from->inst_id;
+}
+
+static void srvc_id_from_hal(btgatt_srvc_id_t *to,
+						struct hal_gatt_srvc_id *from)
+{
+	memcpy(&to->id.uuid, from->uuid, sizeof(to->id.uuid));
+	to->id.inst_id = from->inst_id;
+	to->is_primary = from->is_primary;
+}
+
+static void srvc_id_to_hal(struct hal_gatt_srvc_id *to, btgatt_srvc_id_t *from)
+{
+	memcpy(to->uuid, &from->id.uuid, sizeof(from->id.uuid));
+	to->inst_id = from->id.inst_id;
+	to->is_primary = from->is_primary;
+}
+
+/* Client Event Handlers */
+
+static void handle_register_client(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_register_client *ev = buf;
+
+	if (cbs->client->register_client_cb)
+		cbs->client->register_client_cb(ev->status, ev->client_if,
+						(bt_uuid_t *) ev->app_uuid);
+}
+
+static void handle_scan_result(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_scan_result *ev = buf;
+	uint8_t ad[62];
+
+	if (len != sizeof(*ev) + ev->len) {
+		error("gatt: invalid scan result event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Java assumes that passed data has 62 bytes */
+	memset(ad, 0, sizeof(ad));
+	memcpy(ad, ev->adv_data, ev->len > sizeof(ad) ? sizeof(ad) : ev->len);
+
+	if (cbs->client->scan_result_cb)
+		cbs->client->scan_result_cb((bt_bdaddr_t *) ev->bda, ev->rssi,
+									ad);
+}
+
+static void handle_connect(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_connect *ev = buf;
+
+	if (cbs->client->open_cb)
+		cbs->client->open_cb(ev->conn_id, ev->status, ev->client_if,
+						(bt_bdaddr_t *) ev->bda);
+}
+
+static void handle_disconnect(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_disconnect *ev = buf;
+
+	if (cbs->client->close_cb)
+		cbs->client->close_cb(ev->conn_id, ev->status, ev->client_if,
+						(bt_bdaddr_t *) ev->bda);
+}
+
+static void handle_search_complete(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_search_complete *ev = buf;
+
+	if (cbs->client->search_complete_cb)
+		cbs->client->search_complete_cb(ev->conn_id, ev->status);
+}
+
+static void handle_search_result(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_search_result *ev = buf;
+	btgatt_srvc_id_t srvc_id;
+
+	srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+
+	if (cbs->client->search_result_cb)
+		cbs->client->search_result_cb(ev->conn_id, &srvc_id);
+}
+
+static void handle_get_characteristic(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_get_characteristic *ev = buf;
+	btgatt_gatt_id_t char_id;
+	btgatt_srvc_id_t srvc_id;
+
+	srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+	gatt_id_from_hal(&char_id, &ev->char_id);
+
+	if (cbs->client->get_characteristic_cb)
+		cbs->client->get_characteristic_cb(ev->conn_id, ev->status,
+							&srvc_id, &char_id,
+							ev->char_prop);
+}
+
+static void handle_get_descriptor(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_get_descriptor *ev = buf;
+	btgatt_gatt_id_t descr_id;
+	btgatt_gatt_id_t char_id;
+	btgatt_srvc_id_t srvc_id;
+
+	srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+	gatt_id_from_hal(&char_id, &ev->char_id);
+	gatt_id_from_hal(&descr_id, &ev->descr_id);
+
+	if (cbs->client->get_descriptor_cb)
+		cbs->client->get_descriptor_cb(ev->conn_id, ev->status,
+						&srvc_id, &char_id, &descr_id);
+}
+
+static void handle_get_included_service(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_get_inc_service *ev = buf;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_srvc_id_t incl_srvc_id;
+
+	srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+	srvc_id_from_hal(&incl_srvc_id, &ev->incl_srvc_id);
+
+	if (cbs->client->get_included_service_cb)
+		cbs->client->get_included_service_cb(ev->conn_id, ev->status,
+								&srvc_id,
+								&incl_srvc_id);
+}
+
+static void handle_register_for_notification(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_reg_for_notif *ev = buf;
+	btgatt_gatt_id_t char_id;
+	btgatt_srvc_id_t srvc_id;
+
+	srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+	gatt_id_from_hal(&char_id, &ev->char_id);
+
+	if (cbs->client->register_for_notification_cb)
+		cbs->client->register_for_notification_cb(ev->conn_id,
+								ev->registered,
+								ev->status,
+								&srvc_id,
+								&char_id);
+}
+
+static void handle_notify(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_notify *ev = buf;
+	btgatt_notify_params_t params;
+
+	if (len != sizeof(*ev) + ev->len) {
+		error("gatt: invalid notify event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&params, 0, sizeof(params));
+	memcpy(params.value, ev->value, ev->len);
+	memcpy(&params.bda, ev->bda, sizeof(params.bda));
+
+	srvc_id_from_hal(&params.srvc_id, &ev->srvc_id);
+	gatt_id_from_hal(&params.char_id, &ev->char_id);
+
+	params.len = ev->len;
+	params.is_notify = ev->is_notify;
+
+	if (cbs->client->notify_cb)
+		cbs->client->notify_cb(ev->conn_id, &params);
+}
+
+static void handle_read_characteristic(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_read_characteristic *ev = buf;
+	btgatt_read_params_t params;
+
+	if (len != sizeof(*ev) + ev->data.len) {
+		error("gatt: invalid read characteristic event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&params, 0, sizeof(params));
+
+	srvc_id_from_hal(&params.srvc_id, &ev->data.srvc_id);
+	gatt_id_from_hal(&params.char_id, &ev->data.char_id);
+	gatt_id_from_hal(&params.descr_id, &ev->data.descr_id);
+
+	memcpy(&params.value.value, ev->data.value, ev->data.len);
+
+	params.value_type = ev->data.value_type;
+	params.value.len = ev->data.len;
+	params.status = ev->data.status;
+
+	if (cbs->client->read_characteristic_cb)
+		cbs->client->read_characteristic_cb(ev->conn_id, ev->status,
+								&params);
+}
+
+static void handle_write_characteristic(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_write_characteristic *ev = buf;
+	btgatt_write_params_t params;
+
+	memset(&params, 0, sizeof(params));
+
+	srvc_id_from_hal(&params.srvc_id, &ev->data.srvc_id);
+	gatt_id_from_hal(&params.char_id, &ev->data.char_id);
+	gatt_id_from_hal(&params.descr_id, &ev->data.descr_id);
+
+	params.status = ev->data.status;
+
+	if (cbs->client->write_characteristic_cb)
+		cbs->client->write_characteristic_cb(ev->conn_id, ev->status,
+								&params);
+}
+
+static void handle_read_descriptor(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_read_descriptor *ev = buf;
+	btgatt_read_params_t params;
+
+	if (len != sizeof(*ev) + ev->data.len) {
+		error("gatt: invalid read descriptor event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&params, 0, sizeof(params));
+
+	srvc_id_from_hal(&params.srvc_id, &ev->data.srvc_id);
+	gatt_id_from_hal(&params.char_id, &ev->data.char_id);
+	gatt_id_from_hal(&params.descr_id, &ev->data.descr_id);
+
+	memcpy(&params.value.value, ev->data.value, ev->data.len);
+
+	params.value_type = ev->data.value_type;
+	params.value.len = ev->data.len;
+	params.status = ev->data.status;
+
+	if (cbs->client->read_descriptor_cb)
+		cbs->client->read_descriptor_cb(ev->conn_id, ev->status,
+								&params);
+}
+
+static void handle_write_descriptor(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_write_descriptor *ev = buf;
+	btgatt_write_params_t params;
+
+	memset(&params, 0, sizeof(params));
+
+	srvc_id_from_hal(&params.srvc_id, &ev->data.srvc_id);
+	gatt_id_from_hal(&params.char_id, &ev->data.char_id);
+	gatt_id_from_hal(&params.descr_id, &ev->data.descr_id);
+
+	params.status = ev->data.status;
+
+	if (cbs->client->write_descriptor_cb)
+		cbs->client->write_descriptor_cb(ev->conn_id, ev->status,
+								&params);
+}
+
+static void handle_execute_write(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_exec_write *ev = buf;
+
+	if (cbs->client->execute_write_cb)
+		cbs->client->execute_write_cb(ev->conn_id, ev->status);
+}
+
+static void handle_read_remote_rssi(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_read_remote_rssi *ev = buf;
+
+	if (cbs->client->read_remote_rssi_cb)
+		cbs->client->read_remote_rssi_cb(ev->client_if,
+						(bt_bdaddr_t *) ev->address,
+						ev->rssi, ev->status);
+}
+
+static void handle_listen(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_listen *ev = buf;
+
+	if (cbs->client->listen_cb)
+		cbs->client->listen_cb(ev->status, ev->server_if);
+}
+
+/* Server Event Handlers */
+
+static void handle_register_server(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_register *ev = buf;
+
+	if (cbs->server->register_server_cb)
+		cbs->server->register_server_cb(ev->status, ev->server_if,
+						(bt_uuid_t *) &ev->uuid);
+}
+
+static void handle_connection(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_connection *ev = buf;
+
+	if (cbs->server->connection_cb)
+		cbs->server->connection_cb(ev->conn_id, ev->server_if,
+						ev->connected,
+						(bt_bdaddr_t *) &ev->bdaddr);
+}
+
+static void handle_service_added(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_service_added *ev = buf;
+	btgatt_srvc_id_t srvc_id;
+
+	srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+
+	if (cbs->server->service_added_cb)
+		cbs->server->service_added_cb(ev->status, ev->server_if,
+						&srvc_id, ev->srvc_handle);
+}
+
+static void handle_included_service_added(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_inc_srvc_added *ev = buf;
+
+	if (cbs->server->included_service_added_cb)
+		cbs->server->included_service_added_cb(ev->status,
+							ev->server_if,
+							ev->srvc_handle,
+							ev->incl_srvc_handle);
+}
+
+static void handle_characteristic_added(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_characteristic_added *ev = buf;
+
+	if (cbs->server->characteristic_added_cb)
+		cbs->server->characteristic_added_cb(ev->status, ev->server_if,
+							(bt_uuid_t *) &ev->uuid,
+							ev->srvc_handle,
+							ev->char_handle);
+}
+
+static void handle_descriptor_added(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_descriptor_added *ev = buf;
+
+	if (cbs->server->descriptor_added_cb)
+		cbs->server->descriptor_added_cb(ev->status, ev->server_if,
+							(bt_uuid_t *) &ev->uuid,
+							ev->srvc_handle,
+							ev->descr_handle);
+}
+
+static void handle_service_started(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_service_started *ev = buf;
+
+	if (cbs->server->service_started_cb)
+		cbs->server->service_started_cb(ev->status, ev->server_if,
+							ev->srvc_handle);
+}
+
+static void handle_service_stopped(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_service_stopped *ev = buf;
+
+	if (cbs->server->service_stopped_cb)
+		cbs->server->service_stopped_cb(ev->status, ev->server_if,
+							ev->srvc_handle);
+}
+
+static void handle_service_deleted(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_service_deleted *ev = buf;
+
+	if (cbs->server->service_deleted_cb)
+		cbs->server->service_deleted_cb(ev->status, ev->server_if,
+							ev->srvc_handle);
+}
+
+static void handle_request_read(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_request_read *ev = buf;
+
+	if (cbs->server->request_read_cb)
+		cbs->server->request_read_cb(ev->conn_id, ev->trans_id,
+						(bt_bdaddr_t *) &ev->bdaddr,
+						ev->attr_handle, ev->offset,
+						ev->is_long);
+}
+
+static void handle_request_write(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_request_write *ev = buf;
+
+	if (len != sizeof(*ev) + ev->length) {
+		error("gatt: invalid request write event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (cbs->server->request_write_cb)
+		cbs->server->request_write_cb(ev->conn_id, ev->trans_id,
+						(bt_bdaddr_t *) ev->bdaddr,
+						ev->attr_handle, ev->offset,
+						ev->length, ev->need_rsp,
+						ev->is_prep, ev->value);
+}
+
+static void handle_request_exec_write(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_request_exec_write *ev = buf;
+
+	if (cbs->server->request_exec_write_cb)
+		cbs->server->request_exec_write_cb(ev->conn_id, ev->trans_id,
+						(bt_bdaddr_t *) ev->bdaddr,
+						ev->exec_write);
+}
+
+static void handle_response_confirmation(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_rsp_confirmation *ev = buf;
+
+	if (cbs->server->response_confirmation_cb)
+		cbs->server->response_confirmation_cb(ev->status, ev->handle);
+}
+
+static void handle_configure_mtu(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_configure_mtu *ev = buf;
+
+	if (cbs->client->configure_mtu_cb)
+		cbs->client->configure_mtu_cb(ev->conn_id, ev->status, ev->mtu);
+#endif
+}
+
+static void handle_filter_config(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_filter_config *ev = buf;
+
+	if (cbs->client->scan_filter_cfg_cb)
+		cbs->client->scan_filter_cfg_cb(ev->action, ev->client_if,
+						ev->status, ev->type,
+						ev->space);
+#endif
+}
+
+static void handle_filter_params(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_filter_params *ev = buf;
+
+	if (cbs->client->scan_filter_param_cb)
+		cbs->client->scan_filter_param_cb(ev->action, ev->client_if,
+							ev->status, ev->space);
+#endif
+}
+
+static void handle_filter_status(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_filter_status *ev = buf;
+
+	if (cbs->client->scan_filter_status_cb)
+		cbs->client->scan_filter_status_cb(ev->enable, ev->client_if,
+								ev->status);
+#endif
+}
+
+static void handle__multi_adv_enable(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_multi_adv_enable *ev = buf;
+
+	if (cbs->client->multi_adv_enable_cb)
+		cbs->client->multi_adv_enable_cb(ev->client_if, ev->status);
+#endif
+}
+
+static void handle_multi_adv_update(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_multi_adv_update *ev = buf;
+
+	if (cbs->client->multi_adv_update_cb)
+		cbs->client->multi_adv_update_cb(ev->client_if, ev->status);
+#endif
+}
+
+static void handle_multi_adv_data(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_multi_adv_data *ev = buf;
+
+	if (cbs->client->multi_adv_data_cb)
+		cbs->client->multi_adv_data_cb(ev->client_if, ev->status);
+#endif
+}
+
+static void handle_multi_adv_disable(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_multi_adv_disable *ev = buf;
+
+	if (cbs->client->multi_adv_disable_cb)
+		cbs->client->multi_adv_disable_cb(ev->client_if, ev->status);
+#endif
+}
+
+static void handle_client_congestion(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_congestion *ev = buf;
+
+	if (cbs->client->congestion_cb)
+		cbs->client->congestion_cb(ev->conn_id, ev->congested);
+#endif
+}
+
+static void handle_config_batchscan(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_config_batchscan *ev = buf;
+
+	if (cbs->client->batchscan_cfg_storage_cb)
+		cbs->client->batchscan_cfg_storage_cb(ev->client_if,
+								ev->status);
+#endif
+}
+
+static void handle_enable_batchscan(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_enable_batchscan *ev = buf;
+
+	if (cbs->client->batchscan_enb_disable_cb)
+		cbs->client->batchscan_enb_disable_cb(ev->action, ev->client_if,
+								ev->status);
+#endif
+}
+
+static void handle_client_batchscan_reports(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_batchscan_reports *ev = buf;
+
+	if (cbs->client->batchscan_reports_cb)
+		cbs->client->batchscan_reports_cb(ev->client_if, ev->status,
+							ev->format, ev->num,
+							ev->data_len, ev->data);
+#endif
+}
+
+static void handle_batchscan_threshold(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_batchscan_threshold *ev = buf;
+
+	if (cbs->client->batchscan_threshold_cb)
+		cbs->client->batchscan_threshold_cb(ev->client_if);
+#endif
+}
+
+static void handle_track_adv(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_track_adv *ev = buf;
+
+	if (cbs->client->track_adv_event_cb)
+		cbs->client->track_adv_event_cb(ev->client_if, ev->filetr_index,
+						ev->address_type,
+						(bt_bdaddr_t *) ev->address,
+						ev->state);
+#endif
+}
+
+static void handle_indication_send(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_server_indication_sent *ev = buf;
+
+	if (cbs->server->indication_sent_cb)
+		cbs->server->indication_sent_cb(ev->conn_id, ev->status);
+#endif
+}
+
+static void handle_server_congestion(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_server_congestion *ev = buf;
+
+	if (cbs->server->congestion_cb)
+		cbs->server->congestion_cb(ev->conn_id, ev->congested);
+#endif
+}
+
+static void handle_server_mtu_changed(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 1, 0)
+	struct hal_ev_gatt_server_mtu_changed *ev = buf;
+
+	if (cbs->server->mtu_changed_cb)
+		cbs->server->mtu_changed_cb(ev->conn_id, ev->mtu);
+#endif
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_GATT_CLIENT_REGISTER_CLIENT */
+	{ handle_register_client, false,
+		sizeof(struct hal_ev_gatt_client_register_client) },
+	/* HAL_EV_GATT_CLIENT_SCAN_RESULT */
+	{ handle_scan_result, true,
+		sizeof(struct hal_ev_gatt_client_scan_result) },
+	/* HAL_EV_GATT_CLIENT_CONNECT */
+	{ handle_connect, false, sizeof(struct hal_ev_gatt_client_connect) },
+	/* HAL_EV_GATT_CLIENT_DISCONNECT */
+	{ handle_disconnect, false,
+		sizeof(struct hal_ev_gatt_client_disconnect) },
+	/* HAL_EV_GATT_CLIENT_SEARCH_COMPLETE */
+	{ handle_search_complete, false,
+		sizeof(struct hal_ev_gatt_client_search_complete) },
+	/* HAL_EV_GATT_CLIENT_SEARCH_RESULT */
+	{ handle_search_result, false,
+		sizeof(struct hal_ev_gatt_client_search_result) },
+	/* HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC */
+	{ handle_get_characteristic, false,
+		sizeof(struct hal_ev_gatt_client_get_characteristic) },
+	/* HAL_EV_GATT_CLIENT_GET_DESCRIPTOR */
+	{ handle_get_descriptor, false,
+		sizeof(struct hal_ev_gatt_client_get_descriptor) },
+	/* HAL_EV_GATT_CLIENT_GET_INC_SERVICE */
+	{ handle_get_included_service, false,
+		sizeof(struct hal_ev_gatt_client_get_inc_service) },
+	/* HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF */
+	{ handle_register_for_notification, false,
+		sizeof(struct hal_ev_gatt_client_reg_for_notif) },
+	/* HAL_EV_GATT_CLIENT_NOTIFY */
+	{ handle_notify, true, sizeof(struct hal_ev_gatt_client_notify) },
+	/* HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC */
+	{ handle_read_characteristic, true,
+		sizeof(struct hal_ev_gatt_client_read_characteristic) },
+	/* HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC */
+	{ handle_write_characteristic, false,
+		sizeof(struct hal_ev_gatt_client_write_characteristic) },
+	/* HAL_EV_GATT_CLIENT_READ_DESCRIPTOR */
+	{ handle_read_descriptor, true,
+		sizeof(struct hal_ev_gatt_client_read_descriptor) },
+	/* HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR */
+	{ handle_write_descriptor, false,
+		sizeof(struct hal_ev_gatt_client_write_descriptor) },
+	/* HAL_EV_GATT_CLIENT_EXEC_WRITE */
+	{ handle_execute_write, false,
+		sizeof(struct hal_ev_gatt_client_exec_write) },
+	/* HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI */
+	{ handle_read_remote_rssi, false,
+		sizeof(struct hal_ev_gatt_client_read_remote_rssi) },
+	/* HAL_EV_GATT_CLIENT_LISTEN */
+	{ handle_listen, false, sizeof(struct hal_ev_gatt_client_listen) },
+	/* HAL_EV_GATT_SERVER_REGISTER */
+	{ handle_register_server, false,
+		sizeof(struct hal_ev_gatt_server_register) },
+	/* HAL_EV_GATT_SERVER_CONNECTION */
+	{ handle_connection, false,
+		sizeof(struct hal_ev_gatt_server_connection) },
+	/* HAL_EV_GATT_SERVER_SERVICE_ADDED */
+	{ handle_service_added, false,
+		sizeof(struct hal_ev_gatt_server_service_added) },
+	/* HAL_EV_GATT_SERVER_INC_SRVC_ADDED */
+	{ handle_included_service_added, false,
+		sizeof(struct hal_ev_gatt_server_inc_srvc_added) },
+	/* HAL_EV_GATT_SERVER_CHAR_ADDED */
+	{ handle_characteristic_added, false,
+		sizeof(struct hal_ev_gatt_server_characteristic_added) },
+	/* HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED */
+	{ handle_descriptor_added, false,
+		sizeof(struct hal_ev_gatt_server_descriptor_added) },
+	/* HAL_EV_GATT_SERVER_SERVICE_STARTED */
+	{ handle_service_started, false,
+		sizeof(struct hal_ev_gatt_server_service_started) },
+	/* HAL_EV_GATT_SERVER_SERVICE_STOPPED */
+	{ handle_service_stopped, false,
+		sizeof(struct hal_ev_gatt_server_service_stopped) },
+	/* HAL_EV_GATT_SERVER_SERVICE_DELETED */
+	{ handle_service_deleted, false,
+		sizeof(struct hal_ev_gatt_server_service_deleted) },
+	/* HAL_EV_GATT_SERVER_REQUEST_READ */
+	{ handle_request_read, false,
+		sizeof(struct hal_ev_gatt_server_request_read) },
+	/* HAL_EV_GATT_SERVER_REQUEST_WRITE */
+	{ handle_request_write, true,
+		sizeof(struct hal_ev_gatt_server_request_write) },
+	/* HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE */
+	{ handle_request_exec_write, false,
+		sizeof(struct hal_ev_gatt_server_request_exec_write) },
+	/* HAL_EV_GATT_SERVER_RSP_CONFIRMATION */
+	{ handle_response_confirmation, false,
+		sizeof(struct hal_ev_gatt_server_rsp_confirmation) },
+	/* HAL_EV_GATT_CLIENT_CONFIGURE_MTU */
+	{ handle_configure_mtu, false,
+		sizeof(struct hal_ev_gatt_client_configure_mtu) },
+	/* HAL_EV_GATT_CLIENT_FILTER_CONFIG */
+	{ handle_filter_config, false,
+		sizeof(struct hal_ev_gatt_client_filter_config) },
+	/* HAL_EV_GATT_CLIENT_FILTER_PARAMS */
+	{ handle_filter_params, false,
+		sizeof(struct hal_ev_gatt_client_filter_params) },
+	/* HAL_EV_GATT_CLIENT_FILTER_STATUS */
+	{ handle_filter_status, false,
+		sizeof(struct hal_ev_gatt_client_filter_status) },
+	/* HAL_EV_GATT_CLIENT_MULTI_ADV_ENABLE */
+	{ handle__multi_adv_enable, false,
+		sizeof(struct hal_ev_gatt_client_multi_adv_enable) },
+	/* HAL_EV_GATT_CLIENT_MULTI_ADV_UPDATE */
+	{ handle_multi_adv_update, false,
+		sizeof(struct hal_ev_gatt_client_multi_adv_update) },
+	/* HAL_EV_GATT_CLIENT_MULTI_ADV_DATA */
+	{ handle_multi_adv_data, false,
+		sizeof(struct hal_ev_gatt_client_multi_adv_data) },
+	/* HAL_EV_GATT_CLIENT_MULTI_ADV_DISABLE */
+	{ handle_multi_adv_disable, false,
+		sizeof(struct hal_ev_gatt_client_multi_adv_disable) },
+	/* HAL_EV_GATT_CLIENT_CONGESTION */
+	{ handle_client_congestion, false,
+		sizeof(struct hal_ev_gatt_client_congestion) },
+	/* HAL_EV_GATT_CLIENT_CONFIG_BATCHSCAN */
+	{ handle_config_batchscan, false,
+		sizeof(struct hal_ev_gatt_client_config_batchscan) },
+	/* HAL_EV_GATT_CLIENT_ENABLE_BATCHSCAN */
+	{ handle_enable_batchscan, false,
+		sizeof(struct hal_ev_gatt_client_enable_batchscan) },
+	/* HAL_EV_GATT_CLIENT_BATCHSCAN_REPORTS */
+	{ handle_client_batchscan_reports, true,
+		sizeof(struct hal_ev_gatt_client_batchscan_reports) },
+	/* HAL_EV_GATT_CLIENT_BATCHSCAN_THRESHOLD */
+	{ handle_batchscan_threshold, false,
+		sizeof(struct hal_ev_gatt_client_batchscan_threshold) },
+	/* HAL_EV_GATT_CLIENT_TRACK_ADV */
+	{ handle_track_adv, false,
+		sizeof(struct hal_ev_gatt_client_track_adv) },
+	/* HAL_EV_GATT_SERVER_INDICATION_SENT */
+	{ handle_indication_send, false,
+		sizeof(struct hal_ev_gatt_server_indication_sent) },
+	/* HAL_EV_GATT_SERVER_CONGESTION */
+	{ handle_server_congestion, false,
+		sizeof(struct hal_ev_gatt_server_congestion) },
+	/* HAL_EV_GATT_SERVER_MTU_CHANGED */
+	{ handle_server_mtu_changed, false,
+		sizeof(struct hal_ev_gatt_server_mtu_changed) },
+	};
+
+/* Client API */
+
+static bt_status_t register_client(bt_uuid_t *uuid)
+{
+	struct hal_cmd_gatt_client_register cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t unregister_client(int client_if)
+{
+	struct hal_cmd_gatt_client_unregister cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_UNREGISTER,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t scan_real(int client_if, bool start)
+{
+	struct hal_cmd_gatt_client_scan cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.start = start;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t scan(bool start)
+{
+	return scan_real(0, start);
+}
+#else
+static bt_status_t scan(int client_if, bool start)
+{
+	return scan_real(client_if, start);
+}
+#endif
+
+static bt_status_t connect_real(int client_if, const bt_bdaddr_t *bd_addr,
+						bool is_direct, int transport)
+{
+	struct hal_cmd_gatt_client_connect cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.is_direct = is_direct;
+	cmd.transport = transport;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t connect(int client_if, const bt_bdaddr_t *bd_addr,
+						bool is_direct, int transport)
+{
+	return connect_real(client_if, bd_addr, is_direct, transport);
+}
+#else
+static bt_status_t connect(int client_if, const bt_bdaddr_t *bd_addr,
+								bool is_direct)
+{
+	return connect_real(client_if, bd_addr, is_direct,
+							BT_TRANSPORT_UNKNOWN);
+}
+#endif
+
+static bt_status_t disconnect(int client_if, const bt_bdaddr_t *bd_addr,
+								int conn_id)
+{
+	struct hal_cmd_gatt_client_disconnect cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.conn_id = conn_id;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_DISCONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t listen(int client_if, bool start)
+{
+	struct hal_cmd_gatt_client_listen cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.start = start;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t refresh(int client_if, const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_gatt_client_refresh cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t search_service(int conn_id, bt_uuid_t *filter_uuid)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_search_service *cmd = (void *) buf;
+	size_t len = sizeof(*cmd);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memset(cmd, 0, sizeof(*cmd));
+
+	cmd->conn_id = conn_id;
+
+	if (filter_uuid) {
+		memcpy(cmd->filter_uuid, filter_uuid, sizeof(*filter_uuid));
+		len += sizeof(*filter_uuid);
+		cmd->filtered = 1;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SEARCH_SERVICE,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_included_service(int conn_id, btgatt_srvc_id_t *srvc_id,
+					btgatt_srvc_id_t *start_incl_srvc_id)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_get_included_service *cmd = (void *) buf;
+	size_t len = sizeof(*cmd);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->conn_id = conn_id;
+
+	srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+	cmd->continuation = 0;
+
+	if (start_incl_srvc_id) {
+		srvc_id_to_hal(&cmd->incl_srvc_id[0], start_incl_srvc_id);
+		len += sizeof(cmd->incl_srvc_id[0]);
+		cmd->continuation = 1;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id,
+						btgatt_gatt_id_t *start_char_id)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_get_characteristic *cmd = (void *) buf;
+	size_t len = sizeof(*cmd);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->conn_id = conn_id;
+
+	srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+	cmd->continuation = 0;
+
+	if (start_char_id) {
+		gatt_id_to_hal(&cmd->char_id[0], start_char_id);
+		len += sizeof(cmd->char_id[0]);
+		cmd->continuation = 1;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id,
+					btgatt_gatt_id_t *char_id,
+					btgatt_gatt_id_t *start_descr_id)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_get_descriptor *cmd = (void *) buf;
+	size_t len = sizeof(*cmd);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->conn_id = conn_id;
+
+	srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+	gatt_id_to_hal(&cmd->char_id, char_id);
+	cmd->continuation = 0;
+
+	if (start_descr_id) {
+		gatt_id_to_hal(&cmd->descr_id[0], start_descr_id);
+		len += sizeof(cmd->descr_id[0]);
+		cmd->continuation = 1;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_DESCRIPTOR,
+					len, cmd, NULL , NULL, NULL);
+}
+
+static bt_status_t read_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id,
+					btgatt_gatt_id_t *char_id,
+					int auth_req)
+{
+	struct hal_cmd_gatt_client_read_characteristic cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.conn_id = conn_id;
+	cmd.auth_req = auth_req;
+
+	srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+	gatt_id_to_hal(&cmd.char_id, char_id);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t write_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id,
+					btgatt_gatt_id_t *char_id,
+					int write_type, int len, int auth_req,
+					char *p_value)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_write_characteristic *cmd = (void *) buf;
+	size_t cmd_len = sizeof(*cmd) + len;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->conn_id = conn_id;
+	cmd->write_type = write_type;
+	cmd->len = len;
+	cmd->auth_req = auth_req;
+
+	srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+	gatt_id_to_hal(&cmd->char_id, char_id);
+
+	memcpy(cmd->value, p_value, len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC,
+					cmd_len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t read_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id,
+						btgatt_gatt_id_t *char_id,
+						btgatt_gatt_id_t *descr_id,
+						int auth_req)
+{
+	struct hal_cmd_gatt_client_read_descriptor cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.conn_id = conn_id;
+	cmd.auth_req = auth_req;
+
+	srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+	gatt_id_to_hal(&cmd.char_id, char_id);
+	gatt_id_to_hal(&cmd.descr_id, descr_id);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_READ_DESCRIPTOR,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t write_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id,
+					btgatt_gatt_id_t *char_id,
+					btgatt_gatt_id_t *descr_id,
+					int write_type, int len, int auth_req,
+					char *p_value)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_write_descriptor *cmd = (void *) buf;
+	size_t cmd_len = sizeof(*cmd) + len;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->conn_id = conn_id;
+	cmd->write_type = write_type;
+	cmd->len = len;
+	cmd->auth_req = auth_req;
+
+	srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+	gatt_id_to_hal(&cmd->char_id, char_id);
+	gatt_id_to_hal(&cmd->descr_id, descr_id);
+
+	memcpy(cmd->value, p_value, len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR,
+					cmd_len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t execute_write(int conn_id, int execute)
+{
+	struct hal_cmd_gatt_client_execute_write cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.conn_id = conn_id;
+	cmd.execute = execute;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_EXECUTE_WRITE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t register_for_notification(int client_if,
+						const bt_bdaddr_t *bd_addr,
+						btgatt_srvc_id_t *srvc_id,
+						btgatt_gatt_id_t *char_id)
+{
+	struct hal_cmd_gatt_client_register_for_notification cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+	gatt_id_to_hal(&cmd.char_id, char_id);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION,
+				sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t deregister_for_notification(int client_if,
+						const bt_bdaddr_t *bd_addr,
+						btgatt_srvc_id_t *srvc_id,
+						btgatt_gatt_id_t *char_id)
+{
+	struct hal_cmd_gatt_client_deregister_for_notification cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+	gatt_id_to_hal(&cmd.char_id, char_id);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION,
+				sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t read_remote_rssi(int client_if, const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_gatt_client_read_remote_rssi cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int get_device_type(const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_gatt_client_get_device_type cmd;
+	uint8_t dev_type;
+	size_t resp_len = sizeof(dev_type);
+	bt_status_t status;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	status = hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE,
+				sizeof(cmd), &cmd, &resp_len, &dev_type, NULL);
+
+	if (status != BT_STATUS_SUCCESS || resp_len != sizeof(dev_type))
+		return 0;
+
+	return dev_type;
+}
+
+static bt_status_t set_adv_data_real(int server_if, bool set_scan_rsp,
+				bool include_name, bool include_txpower,
+				int min_interval, int max_interval,
+				int appearance, uint16_t manufacturer_len,
+				char *manufacturer_data,
+				uint16_t service_data_len, char *service_data,
+				uint16_t service_uuid_len, char *service_uuid)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_set_adv_data *cmd = (void *) buf;
+	size_t cmd_len;
+	uint8_t *data;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd_len = sizeof(*cmd) + manufacturer_len + service_data_len +
+							service_uuid_len;
+
+	if (cmd_len > IPC_MTU)
+		return BT_STATUS_FAIL;
+
+	cmd->server_if = server_if;
+	cmd->set_scan_rsp = set_scan_rsp;
+	cmd->include_name = include_name;
+	cmd->include_txpower = include_txpower;
+	cmd->min_interval = min_interval;
+	cmd->max_interval = max_interval;
+	cmd->appearance = appearance;
+	cmd->manufacturer_len = manufacturer_len;
+	cmd->service_data_len = service_data_len;
+	cmd->service_uuid_len = service_uuid_len;
+
+	data = cmd->data;
+
+	if (manufacturer_data && manufacturer_len) {
+		memcpy(data, manufacturer_data, manufacturer_len);
+		data += manufacturer_len;
+	}
+
+	if (service_data && service_data_len) {
+		memcpy(data, service_data, service_data_len);
+		data += service_data_len;
+	}
+
+	if (service_uuid && service_uuid_len)
+		memcpy(data, service_uuid, service_uuid_len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SET_ADV_DATA,
+						cmd_len, cmd, NULL, NULL, NULL);
+}
+
+/*
+ * This is temporary solution and support for older Android versions might
+ * be removed at any time.
+ */
+#if ANDROID_VERSION < PLATFORM_VER(4, 4, 3)
+static bt_status_t set_adv_data(int server_if, bool set_scan_rsp,
+				bool include_name, bool include_txpower,
+				int min_interval, int max_interval,
+				int appearance, uint16_t manufacturer_len,
+				char *manufacturer_data)
+{
+	return set_adv_data_real(server_if, set_scan_rsp, include_name,
+					include_txpower, min_interval,
+					max_interval, appearance,
+					manufacturer_len, manufacturer_data,
+					0, NULL, 0, NULL);
+}
+#else
+static bt_status_t set_adv_data(int server_if, bool set_scan_rsp,
+				bool include_name, bool include_txpower,
+				int min_interval, int max_interval,
+				int appearance, uint16_t manufacturer_len,
+				char *manufacturer_data,
+				uint16_t service_data_len, char *service_data,
+				uint16_t service_uuid_len, char *service_uuid)
+{
+	return set_adv_data_real(server_if, set_scan_rsp, include_name,
+					include_txpower, min_interval,
+					max_interval, appearance,
+					manufacturer_len, manufacturer_data,
+					service_data_len, service_data,
+					service_uuid_len, service_uuid);
+}
+#endif
+
+static bt_status_t test_command(int command, btgatt_test_params_t *params)
+{
+	struct hal_cmd_gatt_client_test_command cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.command = command;
+
+	memcpy(cmd.bda1, params->bda1, sizeof(*params->bda1));
+	memcpy(cmd.uuid1, params->uuid1, sizeof(*params->uuid1));
+
+	cmd.u1 = params->u1;
+	cmd.u2 = params->u2;
+	cmd.u3 = params->u3;
+	cmd.u4 = params->u4;
+	cmd.u5 = params->u5;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_TEST_COMMAND,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t scan_filter_param_setup(int client_if, int action,
+						int filt_index, int feat_seln,
+						int list_logic_type,
+						int filt_logic_type,
+						int rssi_high_thres,
+						int rssi_low_thres,
+						int dely_mode,
+						int found_timeout,
+						int lost_timeout,
+						int found_timeout_cnt)
+{
+	struct hal_cmd_gatt_client_scan_filter_setup cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.action = action;
+	cmd.filter_index = filt_index;
+	cmd.features = feat_seln;
+	cmd.list_type = list_logic_type;
+	cmd.filter_type = filt_logic_type;
+	cmd.rssi_hi = rssi_high_thres;
+	cmd.rssi_lo = rssi_low_thres;
+	cmd.delivery_mode = dely_mode;
+	cmd.found_timeout = found_timeout;
+	cmd.lost_timeout = lost_timeout;
+	cmd.found_timeout_cnt = found_timeout_cnt;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t scan_filter_add_remove(int client_if, int action,
+						int filt_type, int filt_index,
+						int company_id,
+						int company_id_mask,
+						const bt_uuid_t *p_uuid,
+						const bt_uuid_t *p_uuid_mask,
+						const bt_bdaddr_t *bd_addr,
+						char addr_type,
+						int data_len, char *p_data,
+						int mask_len, char *p_mask)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_scan_filter_add_remove *cmd = (void *) buf;
+	size_t cmd_len;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!p_uuid || !p_uuid_mask || !bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	cmd_len = sizeof(*cmd) + data_len + mask_len;
+	if (cmd_len > IPC_MTU)
+		return BT_STATUS_FAIL;
+
+	cmd->client_if = client_if;
+	cmd->action = action;
+	cmd->filter_type = filt_type;
+	cmd->filter_index = filt_index;
+	cmd->company_id = company_id;
+	cmd->company_id_mask = company_id_mask;
+	memcpy(cmd->uuid, p_uuid, sizeof(*p_uuid));
+	memcpy(cmd->uuid_mask, p_uuid_mask, sizeof(*p_uuid_mask));
+	memcpy(cmd->address, bd_addr, sizeof(*bd_addr));
+	cmd->address_type = addr_type;
+
+	cmd->data_len = data_len;
+	memcpy(cmd->data_mask, p_data, data_len);
+
+	cmd->mask_len = mask_len;
+	memcpy(cmd->data_mask + data_len, p_mask, mask_len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE,
+				cmd_len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t scan_filter_clear(int client_if, int filt_index)
+{
+	struct hal_cmd_gatt_client_scan_filter_clear cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.index = filt_index;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t scan_filter_enable(int client_if, bool enable)
+{
+	struct hal_cmd_gatt_client_scan_filter_enable cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.enable = enable;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t configure_mtu(int conn_id, int mtu)
+{
+	struct hal_cmd_gatt_client_configure_mtu cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.conn_id = conn_id;
+	cmd.mtu = mtu;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_CONFIGURE_MTU,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t conn_parameter_update(const bt_bdaddr_t *bd_addr,
+						int min_interval,
+						int max_interval, int latency,
+						int timeout)
+{
+	struct hal_cmd_gatt_client_conn_param_update cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.address, bd_addr, sizeof(*bd_addr));
+	cmd.min_interval = min_interval;
+	cmd.max_interval = max_interval;
+	cmd.latency = latency;
+	cmd.timeout = timeout;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t set_scan_parameters(int scan_interval, int scan_window)
+{
+	struct hal_cmd_gatt_client_set_scan_param cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.interval = scan_interval;
+	cmd.window = scan_window;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SET_SCAN_PARAM,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t multi_adv_enable(int client_if, int min_interval,
+					int max_interval, int adv_type,
+					int chnl_map, int tx_power,
+					int timeout_s)
+{
+	struct hal_cmd_gatt_client_setup_multi_adv cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.min_interval = min_interval;
+	cmd.max_interval = max_interval;
+	cmd.type = adv_type;
+	cmd.channel_map = chnl_map;
+	cmd.tx_power = tx_power;
+	cmd.timeout = timeout_s;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t multi_adv_update(int client_if, int min_interval,
+					int max_interval, int adv_type,
+					int chnl_map, int tx_power,
+					int timeout_s)
+{
+	struct hal_cmd_gatt_client_update_multi_adv cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.min_interval = min_interval;
+	cmd.max_interval = max_interval;
+	cmd.type = adv_type;
+	cmd.channel_map = chnl_map;
+	cmd.tx_power = tx_power;
+	cmd.timeout = timeout_s;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t multi_adv_set_inst_data(int client_if, bool set_scan_rsp,
+						bool include_name,
+						bool incl_txpower,
+						int appearance,
+						int manufacturer_len,
+						char *manufacturer_data,
+						int service_data_len,
+						char *service_data,
+						int service_uuid_len,
+						char *service_uuid)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_setup_multi_adv_inst *cmd = (void *) buf;
+	int off = 0;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (manufacturer_len > 0 && !manufacturer_data)
+		return BT_STATUS_PARM_INVALID;
+
+	if (service_data_len > 0 && !service_data)
+		return BT_STATUS_PARM_INVALID;
+
+	if (service_uuid_len > 0 && !service_uuid)
+		return BT_STATUS_PARM_INVALID;
+
+	if (sizeof(*cmd) + manufacturer_len + service_data_len
+						+ service_uuid_len > IPC_MTU)
+		return BT_STATUS_FAIL;
+
+	cmd->client_if = client_if;
+	cmd->set_scan_rsp = set_scan_rsp;
+	cmd->include_name = include_name;
+	cmd->include_tx_power = incl_txpower;
+	cmd->appearance = appearance;
+	cmd->manufacturer_data_len = manufacturer_len;
+	cmd->service_data_len = service_data_len;
+	cmd->service_uuid_len = service_uuid_len;
+
+	if (manufacturer_len > 0) {
+		memcpy(cmd->data_service_uuid, manufacturer_data,
+							manufacturer_len);
+		off += manufacturer_len;
+	}
+
+	if (service_data_len > 0) {
+		memcpy(cmd->data_service_uuid + off, service_data,
+							service_data_len);
+		off += service_data_len;
+	}
+
+	if (service_uuid_len > 0) {
+		memcpy(cmd->data_service_uuid + off, service_uuid,
+							service_uuid_len);
+		off += service_uuid_len;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST,
+				sizeof(*cmd) + off, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t multi_adv_disable(int client_if)
+{
+	struct hal_cmd_gatt_client_disable_multi_adv_inst cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST,
+				sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t batchscan_cfg_storage(int client_if, int batch_scan_full_max,
+						int batch_scan_trunc_max,
+						int batch_scan_notify_threshold)
+{
+	struct hal_cmd_gatt_client_configure_batchscan cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.full_max = batch_scan_full_max;
+	cmd.trunc_max = batch_scan_trunc_max;
+	cmd.notify_threshold = batch_scan_notify_threshold;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t batchscan_enb_batch_scan(int client_if, int scan_mode,
+						int scan_interval,
+						int scan_window, int addr_type,
+						int discard_rule)
+{
+	struct hal_cmd_gatt_client_enable_batchscan cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.mode = scan_mode;
+	cmd.interval = scan_interval;
+	cmd.window = scan_window;
+	cmd.address_type = addr_type;
+	cmd.discard_rule = discard_rule;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t batchscan_dis_batch_scan(int client_if)
+{
+	struct hal_cmd_gatt_client_disable_batchscan cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t batchscan_read_reports(int client_if, int scan_mode)
+{
+	struct hal_cmd_gatt_client_read_batchscan_reports cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.scan_mode = scan_mode;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS,
+				sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+#endif
+
+/* Server API */
+
+static bt_status_t register_server(bt_uuid_t *uuid)
+{
+	struct hal_cmd_gatt_server_register cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t unregister_server(int server_if)
+{
+	struct hal_cmd_gatt_server_unregister cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_UNREGISTER,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t server_connect_real(int server_if,
+						const bt_bdaddr_t *bd_addr,
+						bool is_direct, int transport)
+{
+	struct hal_cmd_gatt_server_connect cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.is_direct = is_direct;
+	cmd.transport = transport;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t server_connect(int server_if, const bt_bdaddr_t *bd_addr,
+						bool is_direct, int transport)
+{
+	return server_connect_real(server_if, bd_addr, is_direct, transport);
+}
+#else
+static bt_status_t server_connect(int server_if, const bt_bdaddr_t *bd_addr,
+								bool is_direct)
+{
+	return server_connect_real(server_if, bd_addr, is_direct,
+							BT_TRANSPORT_UNKNOWN);
+}
+#endif
+
+static bt_status_t server_disconnect(int server_if, const bt_bdaddr_t *bd_addr,
+								int conn_id)
+{
+	struct hal_cmd_gatt_server_disconnect cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.conn_id = conn_id;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_DISCONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t add_service(int server_if, btgatt_srvc_id_t *srvc_id,
+								int num_handles)
+{
+	struct hal_cmd_gatt_server_add_service cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.num_handles = num_handles;
+
+	srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_ADD_SERVICE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t add_included_service(int server_if, int service_handle,
+						int included_handle)
+{
+	struct hal_cmd_gatt_server_add_inc_service cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.service_handle = service_handle;
+	cmd.included_handle = included_handle;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_ADD_INC_SERVICE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t add_characteristic(int server_if, int service_handle,
+						bt_uuid_t *uuid, int properties,
+						int permissions)
+{
+	struct hal_cmd_gatt_server_add_characteristic cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.service_handle = service_handle;
+	cmd.properties = properties;
+	cmd.permissions = permissions;
+
+	memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t add_descriptor(int server_if, int service_handle,
+					bt_uuid_t *uuid, int permissions)
+{
+	struct hal_cmd_gatt_server_add_descriptor cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.service_handle = service_handle;
+	cmd.permissions = permissions;
+
+	memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_ADD_DESCRIPTOR,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t start_service_real(int server_if, int service_handle,
+								int transport)
+{
+	struct hal_cmd_gatt_server_start_service cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.service_handle = service_handle;
+	cmd.transport = transport;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_START_SERVICE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t start_service(int server_if, int service_handle,
+								int transport)
+{
+	return start_service_real(server_if, service_handle, transport);
+}
+#else
+static bt_status_t start_service(int server_if, int service_handle,
+								int transport)
+{
+	int transport_mask = 0;
+
+	/* Android 5 changes transport enum to bit mask. */
+	switch (transport) {
+	case 0:
+		transport_mask = GATT_SERVER_TRANSPORT_LE_BIT;
+		break;
+	case 1:
+		transport_mask = GATT_SERVER_TRANSPORT_BREDR_BIT;
+		break;
+	case 2:
+		transport_mask = GATT_SERVER_TRANSPORT_LE_BIT |
+						GATT_SERVER_TRANSPORT_BREDR_BIT;
+		break;
+	}
+
+	return start_service_real(server_if, service_handle, transport_mask);
+}
+#endif
+
+static bt_status_t stop_service(int server_if, int service_handle)
+{
+	struct hal_cmd_gatt_server_stop_service cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.service_handle = service_handle;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_STOP_SERVICE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t delete_service(int server_if, int service_handle)
+{
+	struct hal_cmd_gatt_server_delete_service cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.service_handle = service_handle;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_DELETE_SERVICE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t send_indication(int server_if, int attribute_handle,
+					int conn_id, int len, int confirm,
+					char *p_value)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_server_send_indication *cmd = (void *) buf;
+	size_t cmd_len = sizeof(*cmd) + len;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->server_if = server_if;
+	cmd->attribute_handle = attribute_handle;
+	cmd->conn_id = conn_id;
+	cmd->len = len;
+	cmd->confirm = confirm;
+
+	memcpy(cmd->value, p_value, len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_SEND_INDICATION,
+					cmd_len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t send_response(int conn_id, int trans_id, int status,
+						btgatt_response_t *response)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_server_send_response *cmd = (void *) buf;
+	size_t cmd_len = sizeof(*cmd) + sizeof(*response);
+
+	memset(buf, 0 , IPC_MTU);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->conn_id = conn_id;
+	cmd->trans_id = trans_id;
+	cmd->status = status;
+	cmd->handle = response->attr_value.handle;
+	cmd->offset = response->attr_value.offset;
+	cmd->auth_req = response->attr_value.auth_req;
+	cmd->len = response->attr_value.len;
+
+	memcpy(cmd->data, response->attr_value.value, cmd->len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_SEND_RESPONSE,
+					cmd_len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t init(const btgatt_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_GATT, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_GATT;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_GATT);
+	}
+
+	return ret;
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_GATT;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_GATT);
+
+	cbs = NULL;
+}
+
+static btgatt_client_interface_t client_iface = {
+	.register_client = register_client,
+	.unregister_client = unregister_client,
+	.scan = scan,
+	.connect = connect,
+	.disconnect = disconnect,
+	.listen = listen,
+	.refresh = refresh,
+	.search_service = search_service,
+	.get_included_service = get_included_service,
+	.get_characteristic = get_characteristic,
+	.get_descriptor = get_descriptor,
+	.read_characteristic = read_characteristic,
+	.write_characteristic = write_characteristic,
+	.read_descriptor = read_descriptor,
+	.write_descriptor = write_descriptor,
+	.execute_write = execute_write,
+	.register_for_notification = register_for_notification,
+	.deregister_for_notification = deregister_for_notification,
+	.read_remote_rssi = read_remote_rssi,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.scan_filter_param_setup = scan_filter_param_setup,
+	.scan_filter_add_remove = scan_filter_add_remove,
+	.scan_filter_clear = scan_filter_clear,
+	.scan_filter_enable = scan_filter_enable,
+#endif
+	.get_device_type = get_device_type,
+	.set_adv_data = set_adv_data,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.configure_mtu = configure_mtu,
+	.conn_parameter_update = conn_parameter_update,
+	.set_scan_parameters = set_scan_parameters,
+	.multi_adv_enable = multi_adv_enable,
+	.multi_adv_update = multi_adv_update,
+	.multi_adv_set_inst_data = multi_adv_set_inst_data,
+	.multi_adv_disable = multi_adv_disable,
+	.batchscan_cfg_storage = batchscan_cfg_storage,
+	.batchscan_enb_batch_scan = batchscan_enb_batch_scan,
+	.batchscan_dis_batch_scan = batchscan_dis_batch_scan,
+	.batchscan_read_reports = batchscan_read_reports,
+#endif
+	.test_command = test_command,
+};
+
+static btgatt_server_interface_t server_iface = {
+	.register_server = register_server,
+	.unregister_server = unregister_server,
+	.connect = server_connect,
+	.disconnect = server_disconnect,
+	.add_service = add_service,
+	.add_included_service = add_included_service,
+	.add_characteristic = add_characteristic,
+	.add_descriptor = add_descriptor,
+	.start_service = start_service,
+	.stop_service = stop_service,
+	.delete_service = delete_service,
+	.send_indication = send_indication,
+	.send_response = send_response,
+};
+
+static btgatt_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.cleanup = cleanup,
+	.client = &client_iface,
+	.server = &server_iface,
+};
+
+btgatt_interface_t *bt_get_gatt_interface(void)
+{
+	return &iface;
+}
diff --git a/repo/android/hal-handsfree-client.c b/repo/android/hal-handsfree-client.c
new file mode 100644
index 0000000..93b5746
--- /dev/null
+++ b/repo/android/hal-handsfree-client.c
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const bthf_client_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_conn_state *ev = buf;
+
+	if (cbs->connection_state_cb)
+		cbs->connection_state_cb(ev->state, ev->peer_feat,
+						ev->chld_feat,
+						(bt_bdaddr_t *) ev->bdaddr);
+}
+
+static void handle_audio_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_audio_state *ev = buf;
+
+	if (cbs->audio_state_cb)
+		cbs->audio_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_vr_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_vr_state *ev = buf;
+
+	if (cbs->vr_cmd_cb)
+		cbs->vr_cmd_cb(ev->state);
+}
+
+static void handle_network_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_net_state *ev = buf;
+
+	if (cbs->network_state_cb)
+		cbs->network_state_cb(ev->state);
+}
+
+static void handle_network_roaming(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_net_roaming_type *ev = buf;
+
+	if (cbs->network_roaming_cb)
+		cbs->network_roaming_cb(ev->state);
+}
+
+static void handle_network_signal(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_net_signal_strength *ev = buf;
+
+	if (cbs->network_signal_cb)
+		cbs->network_signal_cb(ev->signal_strength);
+}
+
+static void handle_battery_level(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_battery_level *ev = buf;
+
+	if (cbs->battery_level_cb)
+		cbs->battery_level_cb(ev->battery_level);
+}
+
+static void handle_operator_name(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_operator_name *ev = buf;
+	uint16_t name_len = ev->name_len;
+	char *name = NULL;
+
+	if (len != sizeof(*ev) + name_len ||
+		(name_len != 0 && ev->name[name_len - 1] != '\0')) {
+		error("invalid operator name, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (name_len)
+		name = (char *) ev->name;
+
+	if (cbs->current_operator_cb)
+		cbs->current_operator_cb(name);
+}
+
+static void handle_call(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_call_indicator *ev = buf;
+
+	if (cbs->call_cb)
+		cbs->call_cb(ev->call);
+}
+
+static void handle_call_setup(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_call_setup_indicator *ev = buf;
+
+	if (cbs->callsetup_cb)
+		cbs->callsetup_cb(ev->call_setup);
+}
+
+static void handle_call_held(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_call_held_indicator *ev = buf;
+
+	if (cbs->callheld_cb)
+		cbs->callheld_cb(ev->call_held);
+}
+
+static void handle_response_and_hold(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_response_and_hold_status *ev = buf;
+
+	if (cbs->resp_and_hold_cb)
+		cbs->resp_and_hold_cb(ev->status);
+}
+
+static void handle_clip(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_calling_line_ident *ev = buf;
+	uint16_t num_len = ev->number_len;
+	char *number = NULL;
+
+	if (len != sizeof(*ev) + num_len ||
+		(num_len != 0 && ev->number[num_len - 1] != '\0')) {
+		error("invalid  clip, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (num_len)
+		number = (char *) ev->number;
+
+	if (cbs->clip_cb)
+		cbs->clip_cb(number);
+}
+
+static void handle_call_waiting(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_call_waiting *ev = buf;
+	uint16_t num_len = ev->number_len;
+	char *number = NULL;
+
+	if (len != sizeof(*ev) + num_len ||
+		(num_len != 0 && ev->number[num_len - 1] != '\0')) {
+		error("invalid call waiting, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (num_len)
+		number = (char *) ev->number;
+
+	if (cbs->call_waiting_cb)
+		cbs->call_waiting_cb(number);
+}
+
+static void handle_current_calls(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_current_call *ev = buf;
+	uint16_t num_len = ev->number_len;
+	char *number = NULL;
+
+	if (len != sizeof(*ev) + num_len ||
+		(num_len != 0 && ev->number[num_len - 1] != '\0')) {
+		error("invalid current calls, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (num_len)
+		number = (char *) ev->number;
+
+	if (cbs->current_calls_cb)
+		cbs->current_calls_cb(ev->index, ev->direction, ev->call_state,
+							ev->multiparty, number);
+}
+
+static void handle_volume_change(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_volume_changed *ev = buf;
+
+	if (cbs->volume_change_cb)
+		cbs->volume_change_cb(ev->type, ev->volume);
+}
+
+static void handle_command_cmp(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_command_complete *ev = buf;
+
+	if (cbs->cmd_complete_cb)
+		cbs->cmd_complete_cb(ev->type, ev->cme);
+}
+
+static void handle_subscriber_info(void *buf, uint16_t len, int fd)
+{
+	const struct hal_ev_hf_client_subscriber_service_info *ev = buf;
+	uint16_t name_len = ev->name_len;
+	char *name = NULL;
+
+	if (len != sizeof(*ev) + name_len ||
+		(name_len != 0 && ev->name[name_len - 1] != '\0')) {
+		error("invalid sunscriber info, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (name_len)
+		name = (char *) ev->name;
+
+	if (cbs->subscriber_info_cb)
+		cbs->subscriber_info_cb(name, ev->type);
+}
+
+static void handle_in_band_ringtone(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_inband_settings *ev = buf;
+
+	if (cbs->in_band_ring_tone_cb)
+		cbs->in_band_ring_tone_cb(ev->state);
+}
+
+static void handle_last_voice_tag_number(void *buf, uint16_t len, int fd)
+{
+	const struct hal_ev_hf_client_last_void_call_tag_num *ev = buf;
+	char *number = NULL;
+	uint16_t num_len = ev->number_len;
+
+	if (len != sizeof(*ev) + num_len ||
+		(num_len != 0 && ev->number[num_len - 1] != '\0')) {
+		error("invalid voice tag, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (num_len)
+		number = (char *) ev->number;
+
+	if (cbs->last_voice_tag_number_callback)
+		cbs->last_voice_tag_number_callback(number);
+}
+
+static void handle_ring_indication(void *buf, uint16_t len, int fd)
+{
+	if (cbs->ring_indication_cb)
+		cbs->ring_indication_cb();
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_HF_CLIENT_CONN_STATE */
+	{ handle_conn_state, false,
+				sizeof(struct hal_ev_hf_client_conn_state) },
+	/* HAL_EV_HF_CLIENT_AUDIO_STATE */
+	{ handle_audio_state, false,
+				sizeof(struct hal_ev_hf_client_audio_state) },
+	/* HAL_EV_HF_CLIENT_VR_STATE */
+	{ handle_vr_state, false, sizeof(struct hal_ev_hf_client_vr_state) },
+	/*HAL_EV_HF_CLIENT_NET_STATE */
+	{ handle_network_state, false,
+				sizeof(struct hal_ev_hf_client_net_state)},
+	/*HAL_EV_HF_CLIENT_NET_ROAMING_TYPE */
+	{ handle_network_roaming, false,
+			sizeof(struct hal_ev_hf_client_net_roaming_type) },
+	/* HAL_EV_HF_CLIENT_NET_SIGNAL_STRENGTH */
+	{ handle_network_signal, false,
+			sizeof(struct hal_ev_hf_client_net_signal_strength) },
+	/* HAL_EV_HF_CLIENT_BATTERY_LEVEL */
+	{ handle_battery_level, false,
+			sizeof(struct hal_ev_hf_client_battery_level) },
+	/* HAL_EV_HF_CLIENT_OPERATOR_NAME */
+	{ handle_operator_name, true,
+			sizeof(struct hal_ev_hf_client_operator_name) },
+	/* HAL_EV_HF_CLIENT_CALL_INDICATOR */
+	{ handle_call, false,
+			sizeof(struct hal_ev_hf_client_call_indicator) },
+	/* HAL_EV_HF_CLIENT_CALL_SETUP_INDICATOR */
+	{ handle_call_setup, false,
+		sizeof(struct hal_ev_hf_client_call_setup_indicator) },
+	/* HAL_EV_HF_CLIENT_CALL_HELD_INDICATOR */
+	{ handle_call_held, false,
+			sizeof(struct hal_ev_hf_client_call_held_indicator) },
+	/* HAL_EV_HF_CLIENT_RESPONSE_AND_HOLD_STATUS */
+	{ handle_response_and_hold, false,
+		sizeof(struct hal_ev_hf_client_response_and_hold_status) },
+	/* HAL_EV_HF_CLIENT_CALLING_LINE_IDENT */
+	{ handle_clip, true,
+			sizeof(struct hal_ev_hf_client_calling_line_ident) },
+	/* HAL_EV_HF_CLIENT_CALL_WAITING */
+	{ handle_call_waiting, true,
+			sizeof(struct hal_ev_hf_client_call_waiting) },
+	/* HAL_EV_HF_CLIENT_CURRENT_CALL */
+	{ handle_current_calls, true,
+			sizeof(struct hal_ev_hf_client_current_call) },
+	/* HAL_EV_CLIENT_VOLUME_CHANGED */
+	{ handle_volume_change, false,
+			sizeof(struct hal_ev_hf_client_volume_changed) },
+	/* HAL_EV_CLIENT_COMMAND_COMPLETE */
+	{ handle_command_cmp, false,
+			sizeof(struct hal_ev_hf_client_command_complete) },
+	/* HAL_EV_CLIENT_SUBSCRIBER_SERVICE_INFO */
+	{ handle_subscriber_info, true,
+		sizeof(struct hal_ev_hf_client_subscriber_service_info) },
+	/* HAL_EV_CLIENT_INBAND_SETTINGS */
+	{ handle_in_band_ringtone, false,
+		sizeof(struct hal_ev_hf_client_inband_settings) },
+	/* HAL_EV_CLIENT_LAST_VOICE_CALL_TAG_NUM */
+	{ handle_last_voice_tag_number, true,
+		sizeof(struct hal_ev_hf_client_last_void_call_tag_num) },
+	/* HAL_EV_CLIENT_RING_INDICATION */
+	{ handle_ring_indication, false, 0 },
+};
+
+static bt_status_t init(bthf_client_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_HANDSFREE_CLIENT, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	}
+
+	return ret;
+}
+
+static bt_status_t hf_client_connect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_hf_client_connect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_CONNECT, sizeof(cmd), &cmd,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_hf_client_disconnect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_DISCONNECT, sizeof(cmd), &cmd,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t connect_audio(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_hf_client_connect_audio cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_CONNECT_AUDIO, sizeof(cmd),
+				&cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect_audio(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_hf_client_disconnect_audio cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_DISCONNECT_AUDIO, sizeof(cmd),
+				&cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t start_voice_recognition(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_START_VR, 0, NULL, NULL, NULL,
+				NULL);
+}
+
+static bt_status_t stop_voice_recognition(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_STOP_VR, 0, NULL, NULL, NULL,
+				NULL);
+}
+
+static bt_status_t volume_control(bthf_client_volume_type_t type,
+								int volume)
+{
+	struct hal_cmd_hf_client_volume_control cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.type = type;
+	cmd.volume = volume;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_VOLUME_CONTROL, sizeof(cmd),
+				&cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t dial(const char *number)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_hf_client_dial *cmd = (void *) buf;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (number) {
+		cmd->number_len = strlen(number) + 1;
+		memcpy(cmd->number, number, cmd->number_len);
+	} else {
+		cmd->number_len = 0;
+	}
+
+	len = sizeof(*cmd) + cmd->number_len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_DIAL, len, cmd, NULL, NULL,
+				NULL);
+}
+
+static bt_status_t dial_memory(int location)
+{
+	struct hal_cmd_hf_client_dial_memory cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.location = location;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_DIAL_MEMORY,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t call_action(bthf_client_call_action_t action, int index)
+{
+	struct hal_cmd_hf_client_call_action cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.action = action;
+	cmd.index = index;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_CALL_ACTION, sizeof(cmd), &cmd,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t query_current_calls(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS, 0, NULL,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t query_operator_name(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME, 0, NULL,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t retrieve_subsr_info(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO, 0, NULL,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t send_dtmf(char tone)
+{
+	struct hal_cmd_hf_client_send_dtmf cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.tone = tone;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_SEND_DTMF, sizeof(cmd), &cmd,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t request_last_voice_tag_number(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM,
+					0, NULL, NULL, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE_CLIENT);
+
+	cbs = NULL;
+}
+
+static bthf_client_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.connect = hf_client_connect,
+	.disconnect = disconnect,
+	.connect_audio = connect_audio,
+	.disconnect_audio = disconnect_audio,
+	.start_voice_recognition = start_voice_recognition,
+	.stop_voice_recognition = stop_voice_recognition,
+	.volume_control = volume_control,
+	.dial = dial,
+	.dial_memory = dial_memory,
+	.handle_call_action = call_action,
+	.query_current_calls = query_current_calls,
+	.query_current_operator_name = query_operator_name,
+	.retrieve_subscriber_info = retrieve_subsr_info,
+	.send_dtmf = send_dtmf,
+	.request_last_voice_tag_number = request_last_voice_tag_number,
+	.cleanup = cleanup
+};
+
+bthf_client_interface_t *bt_get_hf_client_interface(void)
+{
+	return &iface;
+}
diff --git a/repo/android/hal-handsfree.c b/repo/android/hal-handsfree.c
new file mode 100644
index 0000000..3847901
--- /dev/null
+++ b/repo/android/hal-handsfree.c
@@ -0,0 +1,892 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+#include "hal-utils.h"
+
+static const bthf_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_conn_state *ev = buf;
+
+	if (cbs->connection_state_cb)
+		cbs->connection_state_cb(ev->state,
+						(bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_audio_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_audio_state *ev = buf;
+
+	if (cbs->audio_state_cb)
+		cbs->audio_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_vr_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_vr_state *ev = buf;
+
+	if (cbs->vr_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		cbs->vr_cmd_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->vr_cmd_cb(ev->state);
+#endif
+}
+
+static void handle_answer(void *buf, uint16_t len, int fd)
+{
+	if (cbs->answer_call_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		struct hal_ev_handsfree_answer *ev = buf;
+
+		cbs->answer_call_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->answer_call_cmd_cb();
+#endif
+	}
+}
+
+static void handle_hangup(void *buf, uint16_t len, int fd)
+{
+	if (cbs->hangup_call_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		struct hal_ev_handsfree_hangup *ev = buf;
+
+		cbs->hangup_call_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->hangup_call_cmd_cb();
+#endif
+	}
+}
+
+static void handle_volume(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_volume *ev = buf;
+
+	if (cbs->volume_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		cbs->volume_cmd_cb(ev->type, ev->volume,
+						(bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->volume_cmd_cb(ev->type, ev->volume);
+#endif
+}
+
+static void handle_dial(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_dial *ev = buf;
+	uint16_t num_len = ev->number_len;
+	char *number = NULL;
+
+	if (len != sizeof(*ev) + num_len ||
+			(num_len != 0 && ev->number[num_len - 1] != '\0')) {
+		error("invalid dial event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (!cbs->dial_call_cmd_cb)
+		return;
+
+	if (ev->number_len)
+		number = (char *) ev->number;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	cbs->dial_call_cmd_cb(number, (bt_bdaddr_t *) (ev->bdaddr));
+#else
+	cbs->dial_call_cmd_cb(number);
+#endif
+}
+
+static void handle_dtmf(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_dtmf *ev = buf;
+
+	if (cbs->dtmf_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		cbs->dtmf_cmd_cb(ev->tone, (bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->dtmf_cmd_cb(ev->tone);
+#endif
+}
+
+static void handle_nrec(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_nrec *ev = buf;
+
+	if (cbs->nrec_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		cbs->nrec_cmd_cb(ev->nrec, (bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->nrec_cmd_cb(ev->nrec);
+#endif
+}
+
+static void handle_wbs(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_handsfree_wbs *ev = buf;
+
+	if (cbs->wbs_cb)
+		cbs->wbs_cb(ev->wbs, (bt_bdaddr_t *) (ev->bdaddr));
+#endif
+}
+
+static void handle_chld(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_chld *ev = buf;
+
+	if (cbs->chld_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		cbs->chld_cmd_cb(ev->chld, (bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->chld_cmd_cb(ev->chld);
+#endif
+}
+
+static void handle_cnum(void *buf, uint16_t len, int fd)
+{
+	if (cbs->cnum_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		struct hal_ev_handsfree_cnum *ev = buf;
+
+		cbs->cnum_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->cnum_cmd_cb(NULL);
+#endif
+	}
+}
+
+static void handle_cind(void *buf, uint16_t len, int fd)
+{
+	if (cbs->cind_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		struct hal_ev_handsfree_cind *ev = buf;
+
+		cbs->cind_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->cind_cmd_cb();
+#endif
+	}
+}
+
+static void handle_cops(void *buf, uint16_t len, int fd)
+{
+	if (cbs->cops_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		struct hal_ev_handsfree_cops *ev = buf;
+
+		cbs->cops_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->cops_cmd_cb();
+#endif
+	}
+}
+
+static void handle_clcc(void *buf, uint16_t len, int fd)
+{
+	if (cbs->clcc_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		struct hal_ev_handsfree_clcc *ev = buf;
+
+		cbs->clcc_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->clcc_cmd_cb();
+#endif
+	}
+}
+
+static void handle_unknown_at(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_unknown_at *ev = buf;
+
+	if (len != sizeof(*ev) + ev->len ||
+			(ev->len != 0 && ev->buf[ev->len - 1] != '\0')) {
+		error("invalid unknown command event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (cbs->unknown_at_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		cbs->unknown_at_cmd_cb((char *) ev->buf,
+						(bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->unknown_at_cmd_cb((char *) ev->buf);
+#endif
+}
+
+static void handle_hsp_key_press(void *buf, uint16_t len, int fd)
+{
+	if (cbs->key_pressed_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		struct hal_ev_handsfree_hsp_key_press *ev = buf;
+
+		cbs->key_pressed_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->key_pressed_cmd_cb();
+#endif
+	}
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_HANDSFREE_CONN_STATE */
+	{ handle_conn_state, false,
+				sizeof(struct hal_ev_handsfree_conn_state) },
+	/* HAL_EV_HANDSFREE_AUDIO_STATE */
+	{ handle_audio_state, false,
+				sizeof(struct hal_ev_handsfree_audio_state) },
+	/* HAL_EV_HANDSFREE_VR */
+	{ handle_vr_state, false, sizeof(struct hal_ev_handsfree_vr_state) },
+	/* HAL_EV_HANDSFREE_ANSWER */
+	{ handle_answer, false, sizeof(struct hal_ev_handsfree_answer) },
+	/* HAL_EV_HANDSFREE_HANGUP */
+	{ handle_hangup, false, sizeof(struct hal_ev_handsfree_hangup) },
+	/* HAL_EV_HANDSFREE_VOLUME */
+	{ handle_volume, false, sizeof(struct hal_ev_handsfree_volume) },
+	/* HAL_EV_HANDSFREE_DIAL */
+	{ handle_dial, true, sizeof(struct hal_ev_handsfree_dial) },
+	/* HAL_EV_HANDSFREE_DTMF */
+	{ handle_dtmf, false, sizeof(struct hal_ev_handsfree_dtmf) },
+	/* HAL_EV_HANDSFREE_NREC */
+	{ handle_nrec, false, sizeof(struct hal_ev_handsfree_nrec) },
+	/* HAL_EV_HANDSFREE_CHLD */
+	{ handle_chld, false, sizeof(struct hal_ev_handsfree_chld) },
+	/* HAL_EV_HANDSFREE_CNUM */
+	{ handle_cnum, false, sizeof(struct hal_ev_handsfree_cnum) },
+	/* HAL_EV_HANDSFREE_CIND */
+	{ handle_cind, false, sizeof(struct hal_ev_handsfree_cind) },
+	/* HAL_EV_HANDSFREE_COPS */
+	{ handle_cops, false, sizeof(struct hal_ev_handsfree_cops) },
+	/* HAL_EV_HANDSFREE_CLCC */
+	{ handle_clcc, false, sizeof(struct hal_ev_handsfree_clcc) },
+	/* HAL_EV_HANDSFREE_UNKNOWN_AT */
+	{ handle_unknown_at, true, sizeof(struct hal_ev_handsfree_unknown_at) },
+	/* HAL_EV_HANDSFREE_HSP_KEY_PRESS */
+	{ handle_hsp_key_press, false,
+				sizeof(struct hal_ev_handsfree_hsp_key_press) },
+	/* HAL_EV_HANDSFREE_WBS */
+	{ handle_wbs, false, sizeof(struct hal_ev_handsfree_wbs) },
+};
+
+static uint8_t get_mode(void)
+{
+	char value[PROPERTY_VALUE_MAX];
+
+	if (get_config("handsfree", value, NULL) > 0) {
+		if (!strcasecmp(value, "hfp"))
+			return HAL_MODE_HANDSFREE_HFP;
+
+		if (!strcasecmp(value, "hfp_wbs"))
+			return HAL_MODE_HANDSFREE_HFP_WBS;
+	}
+
+	return HAL_MODE_HANDSFREE_HSP_ONLY;
+}
+
+static bt_status_t init_real(bthf_callbacks_t *callbacks, int max_hf_clients)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_HANDSFREE, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_HANDSFREE;
+	cmd.mode = get_mode();
+	cmd.max_clients = max_hf_clients;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE);
+	}
+
+	return ret;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t init(bthf_callbacks_t *callbacks, int max_hf_clients)
+{
+	return init_real(callbacks, max_hf_clients);
+}
+#else
+static bt_status_t init(bthf_callbacks_t *callbacks)
+{
+	return init_real(callbacks, 1);
+}
+#endif
+
+static bt_status_t handsfree_connect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_connect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_disconnect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_DISCONNECT, sizeof(cmd), &cmd,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t connect_audio(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_connect_audio cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_CONNECT_AUDIO, sizeof(cmd),
+				&cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect_audio(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_disconnect_audio cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_DISCONNECT_AUDIO, sizeof(cmd),
+				&cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t start_voice_recognition_real(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_start_vr cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	if (bd_addr)
+		memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_START_VR,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t start_voice_recognition(bt_bdaddr_t *bd_addr)
+{
+	return start_voice_recognition_real(bd_addr);
+}
+#else
+static bt_status_t start_voice_recognition(void)
+{
+	return start_voice_recognition_real(NULL);
+}
+#endif
+
+static bt_status_t stop_voice_recognition_real(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_stop_vr cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	if (bd_addr)
+		memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_STOP_VR,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t stop_voice_recognition(bt_bdaddr_t *bd_addr)
+{
+	return stop_voice_recognition_real(bd_addr);
+}
+#else
+static bt_status_t stop_voice_recognition(void)
+{
+	return stop_voice_recognition_real(NULL);
+}
+#endif
+
+static bt_status_t volume_control_real(bthf_volume_type_t type, int volume,
+							bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_volume_control cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.type = type;
+	cmd.volume = volume;
+
+	if (bd_addr)
+		memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_VOLUME_CONTROL, sizeof(cmd),
+				&cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t volume_control(bthf_volume_type_t type, int volume,
+							bt_bdaddr_t *bd_addr)
+{
+	return volume_control_real(type, volume, bd_addr);
+}
+#else
+static bt_status_t volume_control(bthf_volume_type_t type, int volume)
+{
+	return volume_control_real(type, volume, NULL);
+}
+#endif
+
+static bt_status_t device_status_notification(bthf_network_state_t state,
+						bthf_service_type_t type,
+						int signal, int battery)
+{
+	struct hal_cmd_handsfree_device_status_notif cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.state = state;
+	cmd.type = type;
+	cmd.signal = signal;
+	cmd.battery = battery;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t cops_response_real(const char *cops, bt_bdaddr_t *bd_addr)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_handsfree_cops_response *cmd = (void *) buf;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!cops)
+		return BT_STATUS_PARM_INVALID;
+
+	memset(cmd, 0, sizeof(*cmd));
+
+	if (bd_addr)
+		memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
+
+	/* Size of cmd.buf */
+	cmd->len = strlen(cops) + 1;
+	memcpy(cmd->buf, cops, cmd->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+						HAL_OP_HANDSFREE_COPS_RESPONSE,
+						len, cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t cops_response(const char *cops, bt_bdaddr_t *bd_addr)
+{
+	return cops_response_real(cops, bd_addr);
+}
+#else
+static bt_status_t cops_response(const char *cops)
+{
+	return cops_response_real(cops, NULL);
+}
+#endif
+
+static bt_status_t cind_response_real(int svc, int num_active, int num_held,
+					bthf_call_state_t state, int signal,
+					int roam, int batt_chg,
+					bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_cind_response cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	if (bd_addr)
+		memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	cmd.svc = svc;
+	cmd.num_active = num_active;
+	cmd.num_held = num_held;
+	cmd.state = state;
+	cmd.signal = signal;
+	cmd.roam = roam;
+	cmd.batt_chg = batt_chg;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_CIND_RESPONSE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t cind_response(int svc, int num_active, int num_held,
+					bthf_call_state_t state, int signal,
+					int roam, int batt_chg,
+					bt_bdaddr_t *bd_addr)
+{
+	return cind_response_real(svc, num_active, num_held, state, signal,
+						roam, batt_chg, bd_addr);
+}
+#else
+static bt_status_t cind_response(int svc, int num_active, int num_held,
+					bthf_call_state_t state, int signal,
+					int roam, int batt_chg)
+{
+	return cind_response_real(svc, num_active, num_held, state, signal,
+						roam, batt_chg, NULL);
+}
+#endif
+
+static bt_status_t formatted_at_response_real(const char *rsp,
+							bt_bdaddr_t *bd_addr)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_handsfree_formatted_at_response *cmd = (void *) buf;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!rsp)
+		return BT_STATUS_PARM_INVALID;
+
+	memset(cmd, 0, sizeof(*cmd));
+
+	if (bd_addr)
+		memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
+
+	cmd->len = strlen(rsp) + 1;
+	memcpy(cmd->buf, rsp, cmd->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE,
+					len, cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t formatted_at_response(const char *rsp, bt_bdaddr_t *bd_addr)
+{
+	return formatted_at_response_real(rsp, bd_addr);
+}
+#else
+static bt_status_t formatted_at_response(const char *rsp)
+{
+	return formatted_at_response_real(rsp, NULL);
+}
+#endif
+
+static bt_status_t at_response_real(bthf_at_response_t response, int error,
+							bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_at_response cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (bd_addr)
+		memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.response = response;
+	cmd.error = error;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_AT_RESPONSE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t at_response(bthf_at_response_t response, int error,
+							bt_bdaddr_t *bd_addr)
+{
+	return at_response_real(response, error, bd_addr);
+}
+#else
+static bt_status_t at_response(bthf_at_response_t response, int error)
+{
+	return at_response_real(response, error, NULL);
+}
+#endif
+
+static bt_status_t clcc_response_real(int index, bthf_call_direction_t dir,
+					bthf_call_state_t state,
+					bthf_call_mode_t mode,
+					bthf_call_mpty_type_t mpty,
+					const char *number,
+					bthf_call_addrtype_t type,
+					bt_bdaddr_t *bd_addr)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_handsfree_clcc_response *cmd = (void *) buf;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memset(cmd, 0, sizeof(*cmd));
+
+	if (bd_addr)
+		memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
+
+	cmd->index = index;
+	cmd->dir = dir;
+	cmd->state = state;
+	cmd->mode = mode;
+	cmd->mpty = mpty;
+	cmd->type = type;
+
+	if (number) {
+		cmd->number_len = strlen(number) + 1;
+		memcpy(cmd->number, number, cmd->number_len);
+	} else {
+		cmd->number_len = 0;
+	}
+
+	len = sizeof(*cmd) + cmd->number_len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+						HAL_OP_HANDSFREE_CLCC_RESPONSE,
+						len, cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t clcc_response(int index, bthf_call_direction_t dir,
+					bthf_call_state_t state,
+					bthf_call_mode_t mode,
+					bthf_call_mpty_type_t mpty,
+					const char *number,
+					bthf_call_addrtype_t type,
+					bt_bdaddr_t *bd_addr)
+{
+	return clcc_response_real(index, dir, state, mode, mpty, number, type,
+								bd_addr);
+}
+#else
+static bt_status_t clcc_response(int index, bthf_call_direction_t dir,
+					bthf_call_state_t state,
+					bthf_call_mode_t mode,
+					bthf_call_mpty_type_t mpty,
+					const char *number,
+					bthf_call_addrtype_t type)
+{
+	return clcc_response_real(index, dir, state, mode, mpty, number, type,
+									NULL);
+}
+#endif
+
+static bt_status_t phone_state_change(int num_active, int num_held,
+					bthf_call_state_t state,
+					const char *number,
+					bthf_call_addrtype_t type)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_handsfree_phone_state_change *cmd = (void *) buf;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->num_active = num_active;
+	cmd->num_held = num_held;
+	cmd->state = state;
+	cmd->type = type;
+
+	if (number) {
+		cmd->number_len = strlen(number) + 1;
+		memcpy(cmd->number, number, cmd->number_len);
+	} else {
+		cmd->number_len = 0;
+	}
+
+	len = sizeof(*cmd) + cmd->number_len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_PHONE_STATE_CHANGE,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_HANDSFREE;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE);
+
+	cbs = NULL;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t configure_wbs(bt_bdaddr_t *bd_addr, bthf_wbs_config_t config)
+{
+	struct hal_cmd_handsfree_configure_wbs cmd;
+
+	DBG("%u", config);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	cmd.config = config;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_CONFIGURE_WBS,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+#endif
+
+static bthf_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.connect = handsfree_connect,
+	.disconnect = disconnect,
+	.connect_audio = connect_audio,
+	.disconnect_audio = disconnect_audio,
+	.start_voice_recognition = start_voice_recognition,
+	.stop_voice_recognition = stop_voice_recognition,
+	.volume_control = volume_control,
+	.device_status_notification = device_status_notification,
+	.cops_response = cops_response,
+	.cind_response = cind_response,
+	.formatted_at_response = formatted_at_response,
+	.at_response = at_response,
+	.clcc_response = clcc_response,
+	.phone_state_change = phone_state_change,
+	.cleanup = cleanup,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.configure_wbs = configure_wbs,
+#endif
+};
+
+bthf_interface_t *bt_get_handsfree_interface(void)
+{
+	return &iface;
+}
diff --git a/repo/android/hal-health.c b/repo/android/hal-health.c
new file mode 100644
index 0000000..5d5b111
--- /dev/null
+++ b/repo/android/hal-health.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const bthl_callbacks_t *cbacks = NULL;
+
+static bool interface_ready(void)
+{
+	return cbacks != NULL;
+}
+
+static void handle_app_registration_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_health_app_reg_state *ev = buf;
+
+	if (cbacks->app_reg_state_cb)
+		cbacks->app_reg_state_cb(ev->id, ev->state);
+}
+
+static void handle_channel_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_health_channel_state *ev = buf;
+	int flags;
+
+	if (fd < 0)
+		goto end;
+
+	flags = fcntl(fd, F_GETFL, 0);
+	if (flags < 0) {
+		error("health: fcntl GETFL error: %s", strerror(errno));
+		return;
+	}
+
+	/* Clean O_NONBLOCK fd flag as Android Java layer expects */
+	if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
+		error("health: fcntl SETFL error: %s", strerror(errno));
+		return;
+	}
+
+end:
+	if (cbacks->channel_state_cb)
+		cbacks->channel_state_cb(ev->app_id, (bt_bdaddr_t *) ev->bdaddr,
+						ev->mdep_index, ev->channel_id,
+						ev->channel_state, fd);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_HEALTH_APP_REG_STATE */
+	{ handle_app_registration_state, false,
+				sizeof(struct hal_ev_health_app_reg_state) },
+	/* HAL_EV_HEALTH_CHANNEL_STATE */
+	{ handle_channel_state, false,
+				sizeof(struct hal_ev_health_channel_state) },
+};
+
+static bt_status_t register_application(bthl_reg_param_t *reg, int *app_id)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_cmd_health_reg_app *cmd = (void *) buf;
+	struct hal_rsp_health_reg_app rsp;
+	size_t rsp_len = sizeof(rsp);
+	bt_status_t status;
+	uint16_t off, len;
+	int i;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!reg || !app_id || !reg->application_name)
+		return BT_STATUS_PARM_INVALID;
+
+	*app_id = -1;
+	memset(buf, 0, IPC_MTU);
+
+	cmd->num_of_mdep = reg->number_of_mdeps;
+
+	off = 0;
+	cmd->app_name_off = off;
+	len = strlen(reg->application_name) + 1;
+	memcpy(cmd->data, reg->application_name, len);
+	off += len;
+
+	cmd->provider_name_off = off;
+	if (reg->provider_name) {
+		len = strlen(reg->provider_name) + 1;
+		memcpy(cmd->data + off, reg->provider_name, len);
+		off += len;
+	}
+
+	cmd->service_name_off = off;
+	if (reg->srv_name) {
+		len = strlen(reg->srv_name) + 1;
+		memcpy(cmd->data + off, reg->srv_name, len);
+		off += len;
+	}
+
+	cmd->service_descr_off = off;
+	if (reg->srv_desp) {
+		len = strlen(reg->srv_desp) + 1;
+		memcpy(cmd->data + off, reg->srv_desp, len);
+		off += len;
+	}
+
+	cmd->len = off;
+	status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP,
+						sizeof(*cmd) + cmd->len, buf,
+						&rsp_len, &rsp, NULL);
+	if (status != BT_STATUS_SUCCESS)
+		return status;
+
+	for (i = 0; i < reg->number_of_mdeps; i++) {
+		struct hal_cmd_health_mdep *mdep = (void *) buf;
+
+		memset(buf, 0, IPC_MTU);
+		mdep->app_id = rsp.app_id;
+		mdep->role = reg->mdep_cfg[i].mdep_role;
+		mdep->data_type = reg->mdep_cfg[i].data_type;
+		mdep->channel_type = reg->mdep_cfg[i].channel_type;
+
+		if (reg->mdep_cfg[i].mdep_description) {
+			mdep->descr_len =
+				strlen(reg->mdep_cfg[i].mdep_description) + 1;
+			memcpy(mdep->descr, reg->mdep_cfg[i].mdep_description,
+							mdep->descr_len);
+		}
+
+		status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+						sizeof(*mdep) + mdep->descr_len,
+						buf, NULL, NULL, NULL);
+
+		if (status != BT_STATUS_SUCCESS)
+			return status;
+	}
+
+	*app_id = rsp.app_id;
+
+	return status;
+}
+
+static bt_status_t unregister_application(int app_id)
+{
+	struct hal_cmd_health_unreg_app cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.app_id = app_id;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_UNREG_APP,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t connect_channel(int app_id, bt_bdaddr_t *bd_addr,
+					int mdep_cfg_index, int *channel_id)
+{
+	struct hal_cmd_health_connect_channel cmd;
+	struct hal_rsp_health_connect_channel rsp;
+	size_t len = sizeof(rsp);
+	bt_status_t status;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr || !channel_id)
+		return BT_STATUS_PARM_INVALID;
+
+	*channel_id = -1;
+	cmd.app_id = app_id;
+	cmd.mdep_index = mdep_cfg_index;
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH,
+					HAL_OP_HEALTH_CONNECT_CHANNEL,
+					sizeof(cmd), &cmd, &len, &rsp, NULL);
+
+	if (status == BT_STATUS_SUCCESS)
+		*channel_id = rsp.channel_id;
+
+	return status;
+}
+
+static bt_status_t destroy_channel(int channel_id)
+{
+	struct hal_cmd_health_destroy_channel cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.channel_id = channel_id;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_DESTROY_CHANNEL,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t init(bthl_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	/* store reference to user callbacks */
+	cbacks = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_HEALTH, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_HEALTH;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbacks = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_HEALTH);
+	}
+
+	return ret;
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_HEALTH;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_HEALTH);
+
+	cbacks = NULL;
+}
+
+static bthl_interface_t health_if = {
+	.size = sizeof(health_if),
+	.init = init,
+	.register_application = register_application,
+	.unregister_application = unregister_application,
+	.connect_channel = connect_channel,
+	.destroy_channel = destroy_channel,
+	.cleanup = cleanup
+};
+
+bthl_interface_t *bt_get_health_interface(void)
+{
+	return &health_if;
+}
diff --git a/repo/android/hal-hidhost.c b/repo/android/hal-hidhost.c
new file mode 100644
index 0000000..1a60326
--- /dev/null
+++ b/repo/android/hal-hidhost.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const bthh_callbacks_t *cbacks;
+
+static bool interface_ready(void)
+{
+	return cbacks != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hidhost_conn_state *ev = buf;
+
+	if (cbacks->connection_state_cb)
+		cbacks->connection_state_cb((bt_bdaddr_t *) ev->bdaddr,
+								ev->state);
+}
+
+static void handle_info(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hidhost_info *ev = buf;
+	bthh_hid_info_t info;
+
+	info.attr_mask = ev->attr;
+	info.sub_class = ev->subclass;
+	info.app_id = ev->app_id;
+	info.vendor_id = ev->vendor;
+	info.product_id = ev->product;
+	info.version = ev->version;
+	info.ctry_code = ev->country;
+	info.dl_len = ev->descr_len;
+	memcpy(info.dsc_list, ev->descr, info.dl_len);
+
+	if (cbacks->hid_info_cb)
+		cbacks->hid_info_cb((bt_bdaddr_t *) ev->bdaddr, info);
+}
+
+static void handle_proto_mode(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hidhost_proto_mode *ev = buf;
+
+	if (cbacks->protocol_mode_cb)
+		cbacks->protocol_mode_cb((bt_bdaddr_t *) ev->bdaddr,
+							ev->status, ev->mode);
+}
+
+static void handle_idle_time(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hidhost_idle_time *ev = buf;
+
+	if (cbacks->idle_time_cb)
+		cbacks->idle_time_cb((bt_bdaddr_t *) ev->bdaddr, ev->status,
+								ev->idle_rate);
+}
+
+static void handle_get_report(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hidhost_get_report *ev = buf;
+
+	if (len != sizeof(*ev) + ev->len) {
+		error("invalid get report event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (cbacks->get_report_cb)
+		cbacks->get_report_cb((bt_bdaddr_t *) ev->bdaddr, ev->status,
+							ev->data, ev->len);
+}
+
+static void handle_virtual_unplug(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hidhost_virtual_unplug *ev = buf;
+
+	if (cbacks->virtual_unplug_cb)
+		cbacks->virtual_unplug_cb((bt_bdaddr_t *) ev->bdaddr,
+								ev->status);
+}
+
+static void handle_handshake(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_hidhost_handshake *ev = buf;
+
+	if (cbacks->handshake_cb)
+		cbacks->handshake_cb((bt_bdaddr_t *) ev->bdaddr, ev->status);
+#endif
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_HIDHOST_CONN_STATE */
+	{ handle_conn_state, false, sizeof(struct hal_ev_hidhost_conn_state) },
+	/* HAL_EV_HIDHOST_INFO */
+	{ handle_info, false, sizeof(struct hal_ev_hidhost_info) },
+	/* HAL_EV_HIDHOST_PROTO_MODE */
+	{ handle_proto_mode, false, sizeof(struct hal_ev_hidhost_proto_mode) },
+	/* HAL_EV_HIDHOST_IDLE_TIME */
+	{ handle_idle_time, false, sizeof(struct hal_ev_hidhost_idle_time) },
+	/* HAL_EV_HIDHOST_GET_REPORT */
+	{ handle_get_report, true, sizeof(struct hal_ev_hidhost_get_report) },
+	/* HAL_EV_HIDHOST_VIRTUAL_UNPLUG */
+	{ handle_virtual_unplug, false,
+				sizeof(struct hal_ev_hidhost_virtual_unplug) },
+	{ handle_handshake, false, sizeof(struct hal_ev_hidhost_handshake) },
+};
+
+static bt_status_t hidhost_connect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_hidhost_connect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_hidhost_disconnect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t virtual_unplug(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_hidhost_virtual_unplug cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
+					HAL_OP_HIDHOST_VIRTUAL_UNPLUG,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t set_info(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info)
+{
+	struct hal_cmd_hidhost_set_info cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	cmd.attr = hid_info.attr_mask;
+	cmd.subclass = hid_info.sub_class;
+	cmd.app_id = hid_info.app_id;
+	cmd.vendor = hid_info.vendor_id;
+	cmd.product = hid_info.product_id;
+	cmd.country = hid_info.ctry_code;
+	cmd.descr_len = hid_info.dl_len;
+	memcpy(cmd.descr, hid_info.dsc_list, cmd.descr_len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_protocol(bt_bdaddr_t *bd_addr,
+					bthh_protocol_mode_t protocol_mode)
+{
+	struct hal_cmd_hidhost_get_protocol cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	/* type match IPC type */
+	cmd.mode = protocol_mode;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
+				HAL_OP_HIDHOST_GET_PROTOCOL,
+				sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t set_protocol(bt_bdaddr_t *bd_addr,
+					bthh_protocol_mode_t protocol_mode)
+{
+	struct hal_cmd_hidhost_set_protocol cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	/* type match IPC type */
+	cmd.mode = protocol_mode;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
+				HAL_OP_HIDHOST_SET_PROTOCOL,
+				sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_report(bt_bdaddr_t *bd_addr,
+						bthh_report_type_t report_type,
+						uint8_t report_id,
+						int buffer_size)
+{
+	struct hal_cmd_hidhost_get_report cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	cmd.id = report_id;
+	cmd.buf_size = buffer_size;
+
+	/* type match IPC type */
+	cmd.type = report_type;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t set_report(bt_bdaddr_t *bd_addr,
+						bthh_report_type_t report_type,
+						char *report)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_cmd_hidhost_set_report *cmd = (void *) buf;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr || !report)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
+	cmd->len = strlen(report);
+	memcpy(cmd->data, report, cmd->len);
+
+	/* type match IPC type */
+	cmd->type = report_type;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT,
+				sizeof(*cmd) + cmd->len, buf, NULL, NULL, NULL);
+}
+
+static bt_status_t send_data(bt_bdaddr_t *bd_addr, char *data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_cmd_hidhost_send_data *cmd = (void *) buf;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr || !data)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
+	cmd->len = strlen(data);
+	memcpy(cmd->data, data, cmd->len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA,
+			sizeof(*cmd) + cmd->len, buf, NULL, NULL, NULL);
+}
+
+static bt_status_t init(bthh_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	/* store reference to user callbacks */
+	cbacks = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_HIDHOST, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_HIDHOST;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbacks = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST);
+	}
+
+	return ret;
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_HIDHOST;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST);
+
+	cbacks = NULL;
+}
+
+static bthh_interface_t hidhost_if = {
+	.size = sizeof(hidhost_if),
+	.init = init,
+	.connect = hidhost_connect,
+	.disconnect = disconnect,
+	.virtual_unplug = virtual_unplug,
+	.set_info = set_info,
+	.get_protocol = get_protocol,
+	.set_protocol = set_protocol,
+	.get_report = get_report,
+	.set_report = set_report,
+	.send_data = send_data,
+	.cleanup = cleanup
+};
+
+bthh_interface_t *bt_get_hidhost_interface(void)
+{
+	return &hidhost_if;
+}
diff --git a/repo/android/hal-ipc-api.txt b/repo/android/hal-ipc-api.txt
new file mode 100644
index 0000000..e3b7798
--- /dev/null
+++ b/repo/android/hal-ipc-api.txt
@@ -0,0 +1,2737 @@
+Android HAL protocol for Bluetooth
+==================================
+
+The Android HAL daemon for Bluetooth functionality implements the Unix socket
+server protocol around /run/bluetooth/daemon (tentative location) or Linux
+abstract sockets (tentative name).
+
+The daemon is single threaded and uses a mainloop for scheduling and general
+operation.
+
+The protocol is SOCK_SEQPACKET based and follows a strict PDU specification
+with a generic header and initial registration exchange. The communication
+is driven from the HAL with command/response exchanges. The daemon will use
+notification to signal events. The protocol is single PDU exchanged based,
+meaning every command requires a response. Notification does not require
+any confirmation. Not handling this PDU exchange leads to a disconnection of
+the socket.
+
+Command/response and notification use separate sockets. First connected socket
+is used for command/response, second for notification.  All services are
+multi-plexed over same pair of sockets. Separation is done to ease
+implementation of simple HAL library with dedicated thread for handling
+notification.
+
+This strict protocol requirement is done to match C based callbacks and
+callout functions that are running in a thread inside the HAL and might
+block.
+
+	.--Android--.                             .--Android--.
+	|  daemon   |                             |  HAL      |
+	|           |          Command            |           |
+	|           | <-------------------------- |           |
+	|           |                             |           |
+	|           | --------------------------> |           |
+	|           |          Response           |           |
+	|           |                             |           |
+	|           |                             |           |
+	|           |        Notification         |           |
+	|           | --------------------------> |           |
+	|           |                             |           |
+	'-----------'                             '-----------'
+
+Every packet will follow the basic header to support simple multi-plexing
+over the same socket. It will also support a basic control channel with service
+id 0.
+
+	0              8              16             24            31
+	+--------------+--------------+--------------+--------------+
+	| Service ID   | Opcode       | Data Length                 |
+	+--------------+--------------+-----------------------------+
+	|                                                           |
+
+The unique service ID is assigned by this specification for each HAL.
+
+As general rule of thumb, the opcode for command matches the opcode for a
+response. Or the opcode 0x00 for an error is returned.
+
+Notification opcodes start from 0x81.
+
+Opcode 0x80 is reserved and shall not be used.
+
+All command/response opcodes have the least significant bit not set. And all
+notifications have the least significant bit set.
+
+The HAL modules only have the job to map the callback and event functions
+to the protocol. They do not need to do anything else. Below is an example
+of a sample transaction for the Bluetooth Core HAL and enabling of an
+adapter.
+
+	HAL                                Daemon
+	----------------------------------------------------
+
+	call enable()                  --> command 0x01
+	return enable()                <-- response 0x01
+
+	call adapter_state_changed()   <-- notification 0x81
+	return adapter_state_changed()
+
+When the Android hardware framework calls into the Bluetooth Core HAL
+and executes the enable() callback, the HAL module sends the enable
+command with opcode 0x01 to the daemon. As soon as the daemon responds,
+the callback will return with the appropriate result.
+
+After the daemon switched on the adapter, it will send a notification
+with opcode 0x81 to the HAL module.
+
+The Bluetooth Core HAL and Bluetooth Socket HAL are guaranteed to be
+available from the daemon. All other HAL modules are optional.
+
+When the Bluetooth Core HAL init() function is called, it should open
+the socket and register both "bluetooth" and "socket" service modules. It is
+required to register "socket" service at the same time since the HAL module
+does not have its own init() function.
+
+It is possible to send Configure command before registering any services to
+customize stack. This step is optional.
+
+When new profiles are initiated, the get_profile_interface() callback
+will load the profile and during init() of the profile, it should register the
+specific service.
+
+	Bluetooth main thread       Daemon
+	-------------------------------------------------------
+
+	init()                  --> open command socket
+	                        --> open notification socket
+	                        --> register module "bluetooth"
+	                        --> register module "socket"
+
+	get_profile_interface() --> return profile struct
+	                        --> continue on Handsfree thread
+
+
+	Handsfree thread            Daemon
+	--------------------------------------------------------
+
+	init()                  --> register module handsfree
+
+
+Error response is common for all services and has fixed structure:
+
+	Opcode 0x00 - Error response
+
+		Response parameters: Status (1 octet)
+
+		Valid status values: 0x01 = Fail
+		                     0x02 = Not ready
+		                     0x03 = No memory
+		                     0x04 = Busy
+		                     0x05 = Done (already completed)
+		                     0x06 = Unsupported
+		                     0x07 = Parameter invalid
+		                     0x08 = Unhandled
+		                     0x09 = Authentication failure
+		                     0x0a = Remote device down
+		                     0x0b = Authentication rejected
+
+
+Core Service (ID 0)
+===================
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Register module command/response
+
+		Command parameters: Service id (1 octet)
+		                    Mode (1 octet)
+		                    Max Clients (4 octets)
+		Response parameters: <none>
+
+		In case a command is sent for an undeclared service ID, it will
+		be rejected. Also there will be no notifications for undeclared
+		service ID.
+
+		Valid Mode values: 0x00 = Default Mode
+		                   0xXX = as defined by service
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Unregister module command/response
+
+		Command parameters: Service id (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Configuration
+
+		Command parameters: Num options (1 octet)
+		                    Option Type # (1 octet)
+		                    Option Length # (2 octets)
+		                    Option Value # (variable)
+
+		Response parameters: <none>
+
+		Valid configure option types: 0x00 = Vendor
+		                              0x01 = Model
+		                              0x02 = Name
+		                              0x03 = Serial Number
+		                              0x04 = System ID
+		                              0x05 = PnP ID
+		                              0x06 = Firmware Rev
+		                              0x07 = Hardware Rev
+
+		In case of an error, the error response will be returned.
+
+Bluetooth Core HAL (ID 1)
+=========================
+
+Android HAL name: "bluetooth" (BT_HARDWARE_MODULE_ID)
+
+	Service modes: 0x00 = Enable BR/EDR/LE if supported (default)
+	               0x01 = Enable BR/EDR only
+	               0x02 = Enable LE only
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Enable command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Disable command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Get Adapter Properties command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Get Adapter Property command/response
+
+		Command parameters: Property type (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x05 - Set Adapter Property command/response
+
+		Command parameters: Property type (1 octet)
+		                    Property length (2 octets)
+		                    Property value (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x06 - Get Remote Device Properties command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x07 - Get Remote Device Property command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Property type (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x08 - Set Remote Device Property command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Property type (1 octet)
+		                    Property length (2 octets)
+		                    Property value (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x09 - Get Remote Service Record command/response
+
+		Command parameters: Remote address (6 octets)
+		                    UUID (16 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0a - Get Remote Services command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0b - Start Discovery command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0c - Cancel Discovery command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0d - Create Bond command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Transport (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0e - Remove Bond command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0f - Cancel Bond command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x10 - PIN Reply command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Accept (1 octet)
+		                    PIN length (1 octet)
+		                    PIN code (16 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x11 - SSP Reply command/response
+
+		Command parameters: Remote address (6 octets)
+		                    SSP variant (1 octet)
+		                    Accept (1 octet)
+		                    Passkey (4 octets)
+		Response parameters: <none>
+
+		Valid SSP variant values: 0x00 = Passkey Confirmation
+		                          0x01 = Passkey Entry
+		                          0x02 = Consent (for Just Works)
+		                          0x03 = Passkey Notification
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x12 - DUT Mode Configure command/response
+
+		Command parameters: Enable (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x13 - DUT Mode Send command/response
+
+		Command parameters: Opcode (2 octets)
+		                    Length (1 octet)
+		                    Data (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x14 - LE Test Mode command/response
+
+		Command parameters: Opcode (2 octets)
+		                    Length (1 octet)
+		                    Data (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Adapter State Changed notification
+
+		Notifications parameters: State (1 octet)
+
+		Valid state values: 0x00 = Off
+		                    0x01 = On
+
+	Opcode 0x82 - Adapter Properties Changed notification
+
+		Notification parameters: Status (1 octet)
+		                         Num properties (1 octet)
+		                         Type # (1 octet)
+		                         Length # (2 octets)
+		                         Value # (variable)
+		                         ...
+
+	Opcode 0x83 - Remote Device Properties notification
+
+		Notification parameters: Status (1 octet)
+		                         Remote address (6 octets)
+		                         Num properties (1 octet)
+		                         Type # (1 octet)
+		                         Length # (2 octets)
+		                         Value # (variable)
+		                         ...
+
+	Opcode 0x84 - Device Found notification
+
+		Notification parameters: Num properties (1 octet)
+		                         Type # (1 octet)
+		                         Length # (2 octets)
+		                         Value # (variable)
+		                         ...
+
+	Opcode 0x85 - Discovery State Changed notification
+
+		Notifications parameters: State (1 octet)
+
+	Opcode 0x86 - PIN Request notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Remote name (249 octets)
+		                         Class of device (4 octets)
+
+	Opcode 0x87 - SSP Request notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Remote name (249 octets)
+		                         Class of device (4 octets)
+		                         Pairing variant (1 octet)
+		                         Passkey (4 octets)
+
+	Opcode 0x88 - Bond State Changed notification
+
+		Notification parameters: Status (1 octet)
+		                         Remote address (6 octets)
+		                         Bond state (1 octet)
+
+		Valid bond state values: 0x00 = None
+		                         0x01 = Bonding
+		                         0x02 = Bonded
+
+	Opcode 0x89 - ACL State Changed notification
+
+		Notification parameters: Status (1 octet)
+		                         Remote address (6 octets)
+		                         ACL state (1 octet)
+
+	Opcode 0x8a - DUT Mode Receive notification
+
+		Notification parameters: Opcode (2 octets)
+		                         Length  (1 octet)
+		                         Data (variable)
+
+	Opcode 0x8b - LE Test Mode notification
+
+		Notification parameters: Status (1 octet)
+		                         Num packets (2 octets)
+
+
+Bluetooth Socket HAL (ID 2)
+===========================
+
+Android HAL name:: "socket" (BT_PROFILE_SOCKETS_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Listen command/response
+
+		Command parameters: Socket type (1 octet)
+		                    Service name (256 octets)
+		                    Service UUID (16 octets)
+		                    Channel (4 octets)
+		                    Socket flags (1 octet)
+		Response parameters: File descriptor (inline)
+
+		Valid socket types: 0x01 = RFCOMM
+		                    0x02 = SCO
+		                    0x03 = L2CAP
+
+		Valid socket flags: 0x01 = Encrypt
+		                    0x02 = Auth
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Connect command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Socket type (1 octet)
+		                    Service UUID (16 octets)
+		                    Channel (4 octets)
+		                    Socket flags (1 octet)
+		Response parameters: File descriptor (inline)
+
+		Valid socket types: 0x01 = RFCOMM
+		                    0x02 = SCO
+		                    0x03 = L2CAP
+
+		Valid socket flags: 0x01 = Encrypt
+		                    0x02 = Auth
+
+		In case of an error, the error response will be returned.
+
+
+Bluetooth HID Host HAL (ID 3)
+============================
+
+Android HAL name: "hidhost" (BT_PROFILE_HIDHOST_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Connect command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Disconnect command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Virtual Unplug command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Set Info command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Attribute mask (2 octets)
+		                    Subclass (1 octet)
+		                    Application ID (1 octet)
+		                    Vendor ID (2 octets)
+		                    Product ID (2 octets)
+		                    Version (2 octets)
+		                    Country code (1 octet)
+		                    Descriptor length (2 octet)
+		                    Descriptor value (884 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x05 - Get Protocol command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Protocol mode (1 octet)
+		Response parameters: <none>
+
+		Valid protocol modes: 0x00 = Report
+		                      0x01 = Boot
+		                      0xff = Unsupported
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x06 - Set Protocol command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Protocol mode (1 octet)
+		Response parameters: <none>
+
+		Valid protocol modes: 0x00 = Report
+		                      0x01 = Boot
+		                      0xff = Unsupported
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x07 - Get Report command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Report type (1 octet)
+		                    Report ID (1 octet)
+		                    Buffer size (2 octet)
+		Response parameters: <none>
+
+		Valid report types: 0x01 = Input
+		                    0x02 = Output
+		                    0x03 = Feature
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x08 - Set Report command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Report type (1 octet)
+		                    Report length (2 octets)
+		                    Report data (Report length)
+
+		Response parameters: <none>
+
+		Valid report types: 0x01 = Input
+		                    0x02 = Output
+		                    0x03 = Feature
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x09 - Send Data command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Data length (2 octets)
+		                    Data (Data length)
+
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Status is common for many notifications and has fixed value range:
+
+		Status values: 0x00 = Ok
+		               0x01 = Handshake - Device not ready
+		               0x02 = Handshake - Invalid report ID
+		               0x03 = Handshake - Transaction not SPT
+		               0x04 = Handshake - Invalid parameter
+		               0x05 = Handshake - Generic error
+		               0x06 = General error
+		               0x07 = SDP error
+		               0x08 = Set protocol error
+		               0x09 = Device database full
+		               0x0a = Device type not supported
+		               0x0b = No resources
+		               0x0c = Authentication failed
+		               0x0d = HDL
+
+	Opcode 0x81 - Connection State notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Connection State (1 octets)
+
+		Valid connection states: 0x00 = Connected
+		                         0x01 = Connecting
+		                         0x02 = Disconnected
+		                         0x03 = Disconnecting
+		                         0x04 = Failed - Mouse from host
+		                         0x05 = Failed - Keyboard from host
+		                         0x06 = Failed - Too many devices
+		                         0x07 = Failed - No HID driver
+		                         0x08 = Failed - generic
+		                         0x09 = Unknown
+
+	Opcode 0x82 - HID Info notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Attribute mask (2 octets)
+		                         Subclass (1 octet)
+		                         Application ID (1 octet)
+		                         Vendor ID (2 octets)
+		                         Product ID (2 octets)
+		                         Version (2 octets)
+		                         Country code (1 octet)
+		                         Descriptor length (2 octet)
+		                         Descriptor value (884 octets)
+
+	Opcode 0x83 - Protocol Mode notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Status (1 octet)
+		                         Protocol mode (1 octet)
+
+		Valid protocol modes: 0x00 = Report
+		                      0x01 = Boot
+		                      0xff = Unsupported
+
+	Opcode 0x84 - Idle Time notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Status (1 octet)
+		                         Idle time (2 octets)
+
+	Opcode 0x85 - Get Report notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Status (1 octet)
+		                         Report length (2 octets)
+		                         Report data (variable)
+
+	Opcode 0x86 - Virtual Unplug notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Status (1 octet)
+
+	Opcode 0x87 - Handshake notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Status (1 octet)
+
+		Only status values from 0x00 to 0x05 are valid as handshake
+		status.
+
+
+Bluetooth PAN HAL (ID 4)
+========================
+
+Android HAL name: "pan" (BT_PROFILE_PAN_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Enable command/response
+
+		Command parameters: Local role (1 octet)
+		Response parameters: <none>
+
+		Valid role values: 0x00 = None
+		                   0x01 = NAP
+		                   0x02 = PANU
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Get Local Role command/response
+
+		Command parameters: <none>
+		Response parameters: Local role (1 octet)
+
+		Valid role values: 0x00 = None
+		                   0x01 = NAP
+		                   0x02 = PANU
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Connect command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Local role (1 octet)
+		                    Remote role (1 octet)
+		Response parameters: <none>
+
+		Valid role values: 0x01 = NAP
+		                   0x02 = PANU
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Disconnect command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Control State notification
+
+		Notification parameters: Control state (1 octet)
+		                         Status (1 octet)
+		                         Local role (1 octet)
+		                         Interface name (17 octet)
+
+		Valid control states: 0x00 = Enabled
+		                      0x01 = Disabled
+
+		Valid role values: 0x00 = None
+		                   0x01 = NAP
+		                   0x02 = PANU
+
+	Opcode 0x82 - Connection State notification
+
+		Notification parameters: Connection state (1 octet)
+		                         Status (1 octet)
+		                         Remote address (6 octets)
+		                         Local role (1 octet)
+		                         Remote role (1 octet)
+
+		Valid connection states: 0x00 = Connected
+		                         0x01 = Connecting
+		                         0x02 = Disconnected
+		                         0x03 = Disconnecting
+
+		Valid role values: 0x01 = NAP
+		                   0x02 = PANU
+
+
+Bluetooth Handsfree HAL (ID 5)
+==============================
+
+Android HAL name: "handsfree" (BT_PROFILE_HANDSFREE_ID)
+
+	Service modes: 0x00 = Headset Profile only mode (default)
+	               0x01 = Handsfree Profile (narrowband speech)
+	               0x02 = Handsfree Profile (narrowband and wideband speech)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Connect command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Disconnect command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Connect Audio command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Disconnect Audio command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x05 - Start Voice Recognition command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x06 - Stop Voice Recognition command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x07 - Volume Control command/response
+
+		Command parameters: Volume type (1 octet)
+		                    Volume (1 octet)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		Valid volume types: 0x00 = Speaker
+		                    0x01 = Microphone
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x08 - Device Status Notification command/response
+
+		Command parameters: Network state (1 octet)
+		                    Service type (1 octet)
+		                    Signal strength (1 octet)
+		                    Battery level (1 octet)
+		Response parameters: <none>
+
+		Valid network states: 0x00 = Not available
+		                      0x01 = Available
+
+		Valid service types: 0x00 = Home network
+		                     0x01 = Roaming network
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x09 - COPS Response command/response
+
+		Command parameters: COPS command response (string)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0a - CIND Response command/response
+
+		Command parameters: Service (1 octet)
+		                    Number of active calls (1 octet)
+		                    Number of held calls (1 octet)
+		                    Call setup state (1 octet)
+		                    Signal strength (1 octet)
+		                    Roaming indicator (1 octet)
+		                    Battery level (1 octet)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		Valid call setup states: 0x00 = Active
+		                         0x01 = Held
+		                         0x02 = Dialing
+		                         0x03 = Alerting
+		                         0x04 = Incoming
+		                         0x05 = Waiting
+		                         0x06 = Idle
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0b - Formatted AT Response command/response
+
+		Command parameters: Pre-formatted AT response (string)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0c - AT Response command/response
+
+		Command parameters: Response code (1 octet)
+		                    Error code (1 octet)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		Valid response codes: 0x00 = ERROR
+		                      0x01 = OK
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0d - CLCC Response command/response
+
+		Command parameters: Call index (1 octet)
+		                    Call direction (1 octet)
+		                    Call state (1 octet)
+		                    Call mode (1 octet)
+		                    Call multiparty type (1 octet)
+		                    Call number type (1 octet)
+		                    Call number (string)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		Valid call directions: 0x00 = Outgoing
+		                       0x01 = Incoming
+
+		Valid call states: 0x00 = Active
+		                   0x01 = Held
+		                   0x02 = Dialing
+		                   0x03 = Alerting
+		                   0x04 = Incoming
+		                   0x05 = Waiting
+		                   0x06 = Idle
+
+		Valid call modes: 0x00 = Voice
+		                  0x01 = Data
+		                  0x02 = Fax
+
+		Valid multiparty types: 0x00 = Single call
+		                        0x01 = Multiparty call
+
+		Valid number types: 0x81 = Unknown
+		                    0x91 = International
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0e - Phone Status Change command/response
+
+		Command parameters: Number of active calls (1 octet)
+		                    Number of held calls (1 octet)
+		                    Call setup state (1 octet)
+		                    Call number type (1 octet)
+		                    Call number (string)
+		Response parameters: <none>
+
+		Valid call setup states: 0x00 = Active
+		                         0x01 = Held
+		                         0x02 = Dialing
+		                         0x03 = Alerting
+		                         0x04 = Incoming
+		                         0x05 = Waiting
+		                         0x06 = Idle
+
+		Valid number types: 0x81 = Unknown
+		                    0x91 = International
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0f - Configure WBS command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Config (1 octet)
+		Response parameters: <none>
+
+		Valid config values: 0x00 = None
+		                     0x01 = No
+		                     0x02 = Yes
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Connection State notification
+
+		Notification parameters: Connection state (1 octet)
+		                         Remote address (6 octets)
+
+		Valid connection states: 0x00 = Disconnected
+		                         0x01 = Connecting
+		                         0x02 = Connected
+		                         0x03 = SLC connected
+		                         0x04 = Disconnecting
+
+	Opcode 0x82 - Audio State notification
+
+		Notification parameters: Audio state (1 octet)
+		                         Remote address (6 octets)
+
+		Valid audio states: 0x00 = Disconnected
+		                    0x01 = Connecting
+		                    0x02 = Connected
+		                    0x03 = Disconnecting
+
+	Opcode 0x83 - Voice Recognition Command notification
+
+		Notification parameters: Voice recognition state (1 octet)
+		                         Remote address (6 octets)
+
+		Valid voice recognition states: 0x00 = Stopped
+		                                0x01 = Started
+
+	Opcode 0x84 - Answer Call Command notification
+
+		Notification parameters: Remote address (6 octets)
+
+	Opcode 0x85 - Hangup Call Command notification
+
+		Notification parameters: Remote address (6 octets)
+
+	Opcode 0x86 - Volume Command notification
+
+		Notification parameters: Volume type (1 octet)
+		                         Volume (1 octet)
+		                         Remote address (6 octets)
+
+		Valid volume types: 0x00 = Speaker
+		                    0x01 = Microphone
+
+	Opcode 0x87 - Dial Call Command notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Number (string)
+
+	Opcode 0x88 - DTMF Command notification
+
+		Notification parameters: Tone (1 octet)
+		                         Remote address (6 octets)
+
+	Opcode 0x89 - NREC Command notification
+
+		Notification parameters: NREC types (1 octet)
+		                         Remote address (6 octets)
+
+		Valid NREC types: 0x00 = Stop
+		                  0x01 = Start
+
+	Opcode 0x8a - CHLD Command notification
+
+		Notification parameters: NREC types (1 octet)
+		                         Remote address (6 octets)
+
+		Valid CHLD types: 0x00 = Release and hold
+		                  0x01 = Release active and accept held
+		                  0x02 = Hold active and accept held
+		                  0x03 = Add held call to conference
+
+	Opcode 0x8b - CNUM Command notification
+
+		Notification parameters: Remote address (6 octets)
+
+	Opcode 0x8c - CIND Command notification
+
+		Notification parameters: Remote address (6 octets)
+
+	Opcode 0x8d - COPS Command notification
+
+		Notification parameters: Remote address (6 octets)
+
+	Opcode 0x8e - CLCC Command notification
+
+		Notification parameters: Remote address (6 octets)
+
+	Opcode 0x8f - Unknown AT Command notification
+
+		Notification parameters: Remote address (6 octets)
+		                         AT command (string)
+
+	Opcode 0x90 - Key Pressed Command notification
+
+		Notification parameters: Remote address (6 octets)
+
+	Opcode 0x91 - WBS Command notification
+
+		Notification parameters: WBS types (1 octet)
+		                         Remote address (6 octets)
+
+		Valid WBS types: 0x00 = None
+		                 0x01 = No
+		                 0x02 = Yes
+
+Bluetooth Advanced Audio HAL (ID 6)
+Bluetooth Advanced Audio Sink HAL (ID 13)
+=========================================
+
+Android HAL name: "a2dp" (BT_PROFILE_ADVANCED_AUDIO_ID)
+Android HAL name: "a2dp_sink" (BT_PROFILE_ADVANCED_AUDIO__SINK_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Connect command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Disconnect command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Connection State notification
+
+		Notification parameters: Connection state (1 octet)
+		                         Remote address (6 octets)
+
+		Valid connection states: 0x00 = Disconnected
+		                         0x01 = Connecting
+		                         0x02 = Connected
+		                         0x03 = Disconnecting
+
+	Opcode 0x82 - Audio State notification
+
+		Notification parameters: Audio state (1 octet)
+		                         Remote address (6 octets)
+
+		Valid connection states: 0x00 = Remote suspend
+		                         0x01 = Stopped
+		                         0x02 = Started
+
+	Opcode 0x83 - Audio Configuration notification
+
+		Notification parameters: Remote address (6 octets)
+					 Sample Rate in Hz (4 octets)
+					 Channel Count (1 octet)
+
+		Valid channel count: 0x01 = Mono
+		                     0x02 = Stereo
+
+Bluetooth Health HAL (ID 7)
+===========================
+
+Android HAL name: "health" (BT_PROFILE_HEALTH_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Register Application command/response
+
+		Command parameters: Number of MDEP (1 octet)
+		                    Application name offset (2 octets)
+		                    Provider name offset (2 octets)
+		                    Service name offset (2 octets)
+		                    Service description offset (2 octets)
+		                    Data length (2 octets)
+		                    Data (data length)
+		Response parameters: Application ID (2 octets)
+
+		Strings are null terminated.
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Register Application MDEP data command/response
+
+		Command parameters: Application ID (2 octets)
+                                    MDEP Role (1 octet)
+		                    Data type (2 octets)
+		                    Channel type (1 octet)
+		                    MDEP description length (2 octets)
+		                    MDEP description (MDEP desciption length)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Unregister Application command/response
+
+		Command parameters: Application ID (2 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Connect Channel command/response
+
+		Command parameters: Application ID (2 octets)
+		                    Remote address (6 octets)
+		                    MDEP index (1 octet)
+		Response parameters: Channel ID (2 octets)
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x05 - Destroy Channel command/response
+
+		Command parameters: Channel ID (2 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Application Registration State notification
+
+		Notification parameters: Application ID (2 octets)
+		                         Application state (1 octet)
+
+		Valid application states: 0x00 = Registration success
+		                          0x01 = Registration failed
+		                          0x02 = Deregistration success
+		                          0x03 = Deregistration failed
+
+	Opcode 0x82 - Channel State notification
+
+		Notification parameters: Application ID (2 octets)
+		                         Remote address (6 octets)
+		                         MDEP index (1 octet)
+		                         Channel ID (2 octets)
+		                         Channel state (1 octet)
+		                         File descriptor (inline)
+
+		Valid channel states: 0x00 = Connecting
+		                      0x01 = Connected
+		                      0x02 = Disconnecting
+		                      0x03 = Disconnected
+		                      0x04 = Destroyed
+
+
+Bluetooth Remote Control Target HAL (ID 8)
+===================================
+
+Android HAL name: "avrcp" (BT_PROFILE_AV_RC_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Get Play Status Response command/response
+
+		Command parameters: Status (1 octet)
+		                    Duration (4 octets)
+		                    Position (4 octets)
+
+		In case of an error, the error response will be returned.
+
+		Valid status values: 0x00 = Stopped
+		                     0x01 = Playing
+		                     0x02 = Paused
+		                     0x03 = Fwd seek
+		                     0x04 = Rev seek
+		                     0xff = Error
+
+	Opcode 0x02 - List Player Attributes Response command/response
+
+		Command parameters: Number of attributes (1 octet)
+		                    Attribute # (1 octet)
+		                    ...
+
+		In case of an error, the error response will be returned.
+
+		Valid attributes: 0x01 = Equalizer
+		                  0x02 = Repead
+		                  0x03 = Shuffle
+		                  0x04 = Scan
+
+	Opcode 0x03 - List Player Values Response command/response
+
+		Command parameters: Number of values (1 octet)
+		                    Value # (1 octet)
+		                    ...
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Get Player Values Response command/response
+
+		Command parameters: Number of attributes (1 octet)
+		                    Attribute # (1 octet)
+		                    Value # (1 octet)
+		                    ...
+
+		In case of an error, the error response will be returned.
+
+		Valid attributes: Same as in List Player Attributes
+
+	Opcode 0x05 - Get Player Attributes Text Response command/response
+
+		Command parameters: Number of attributes (1 octet)
+		                    Attribute # (1 octet)
+		                    Attribute # text length (1 octet)
+		                    Attribute # text (variable)
+		                    ...
+
+		In case of an error, the error response will be returned.
+
+		Valid attributes: Same as in List Player Attributes
+
+	Opcode 0x06 - Get Player Values Text Response command/response
+
+		Command parameters: Number of values (1 octet)
+		                    Value # (1 octet)
+		                    Value # text length (1 octet)
+		                    Value # text (variable)
+		                    ...
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x07 - Get Element Attributes Text Response command/response
+
+		Command parameters: Number of elements (1 octet)
+		                    Element # (1 octet)
+		                    Element # text length (1 octet)
+		                    Element # text (variable)
+		                    ...
+
+		In case of an error, the error response will be returned.
+
+		Valid elements: 0x01 = Title
+		                0x02 = Artist
+		                0x03 = Album
+		                0x04 = Track Number
+		                0x05 = Number of Tracks
+		                0x06 = Genre
+		                0x06 = Duration
+
+	Opcode 0x08 - Set Player Attributes Value Response command/response
+
+		Command parameters: Status (1 octet)
+
+		In case of an error, the error response will be returned.
+
+		Valid status values: Same as in Get Play Status Response
+
+	Opcode 0x09 - Register Notification Response command/response
+
+		Command parameters: Event (1 octet)
+		                    Type (1 octet)
+		                    Data length (1 octet)
+		                    Data (variable)
+
+		In case of an error, the error response will be returned.
+
+		Valid event values: 0x01 = Status Changed
+		                    0x02 = Track Changed
+		                    0x03 = Track Reached End
+		                    0x04 = Track Reached Start
+		                    0x05 = Position Changed
+		                    0x08 = Setting Changed
+
+		Valid type values : 0x00 = Interim
+		                    0x01 = Changed
+
+	Opcode 0x0a - Set Volume command/response
+
+		Command parameters: Value (1 octet)
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Remote Features notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Features (1 octet)
+
+		Valid features values : 0x00 = None
+		                        0x01 = Metadata
+		                        0x02 = Absolute Volume
+		                        0x03 = Browse
+
+	Opcode 0x82 - Get Play Status notification
+
+		Notification parameters: <none>
+
+	Opcode 0x83 - List Player Attributes notification
+
+		Notification parameters: <none>
+
+	Opcode 0x84 - List Player Values notification
+
+		Notification parameters: Attribute (1 octet)
+
+		Valid attribute values: Same as in List Player Attributes
+
+	Opcode 0x85 - Get Player Values notification
+
+		Notification parameters: Number of attributes (1 octet)
+		                         Attribute # (1 octet)
+		                         ...
+
+		Valid attribute values: Same as in List Player Attributes
+
+	Opcode 0x86 - Get Player Attributes Text notification
+
+		Notification parameters: Number of attributes (1 octet)
+		                         Attribute # (1 octet)
+		                         ...
+
+		Valid attribute values: Same as in List Player Attributes
+
+	Opcode 0x87 - Get Player Values Text notification
+
+		Notification parameters: Attribute (1 octet)
+		                         Number of values (1 octet)
+		                         Value # (1 octet)
+		                         ...
+
+		Valid attribute values: Same as in List Player Attributes
+
+	Opcode 0x88 - Set Player Values notification
+
+		Notification parameters: Number of attributes (1 octet)
+		                         Attribute # (1 octet)
+		                         Value # (1 octet)
+		                         ...
+
+		Valid attribute values: Same as in List Player Attributes
+
+	Opcode 0x89 - Get Element Attributes notification
+
+		Notification parameters: Number of attributes (1 octet)
+		                         Attribute # (1 octet)
+		                         ...
+
+		Valid attribute values: Same as in Get Element Attribute
+
+	Opcode 0x8a - Register Notification notification
+
+		Notification parameters: Event (1 octet)
+		                         Parameter (4 octets)
+
+		Valid event values: Same as in Register Notification
+
+	Opcode 0x8b - Volume Changed notification
+
+		Notification parameters: Volume (1 octet)
+		                         Type (1 octet)
+
+		Valid type values: Same as in Register Notification
+
+	Opcode 0x8c - Passthrough Command notification
+
+		Notification parameters: ID (1 octet)
+		                         State (1 octet)
+
+Bluetooth GATT HAL (ID 9)
+=========================
+
+Android HAL name: "gatt" (BT_PROFILE_GATT_ID)
+
+Structures:
+
+	GATT Service ID: UUID (16 octets)
+	                 Instance ID (1 octet)
+	                 Is Primary (1 octet)
+
+	GATT Included Service ID: UUID (16 octets)
+	                          Instance ID (1 octet)
+	                          Is Primary (1 octet)
+
+	GATT Characteristic ID: UUID (16 octets)
+	                        Instance ID (1 octet)
+
+	GATT Descriptor ID: UUID (16 octets)
+	                    Instance ID (1 octet)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Client Register command/response
+
+		Command parameters: Service UUID (16 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Client Unregister command/response
+
+		Command parameters: Client Interface (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Client Scan command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Start (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Client Connect Device command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Remote address (6 octets)
+		                    Is Direct (1 octet)
+		                    Transport (4 octets)
+		Response parameters: <none>
+
+		Valid transport value: 0x00 = Auto
+		                       0x01 = BR/EDR
+		                       0x02 = LE
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x05 - Client Disconnect Device command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Remote address (6 octets)
+		                    Connection ID (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x06 - Client Listen command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Start (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x07 - Client Refresh command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x08 - Client Search Service command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    Filtered (1 octet)
+		                    Filter UUID (16 octets)
+		Response parameters: <none>
+
+		Filter UUID shall only be present when Filtered is non-zero.
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x09 - Client Get Included Service command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    Continuation (1 octet)
+		                    GATT Included Service ID (18 octets)
+		                    ...
+		Response parameters: <none>
+
+		GATT Included Service ID shall only be present when Continuation
+		is non-zero.
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0a - Client Get Characteristic command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    Continuation (1 octet)
+		                    GATT Characteristic ID (17 octets)
+		                    ...
+		Response parameters: <none>
+
+		GATT Characteristic ID shall only be present when Continuation
+		is non-zero.
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0b - Client Get Descriptor command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT Characteristic ID (17 octets)
+		                    Continuation (1 octet)
+		                    GATT Descriptor ID (17 octets)
+		                    ...
+		Response parameters: <none>
+
+		GATT Descriptor ID shall only be present when Continuation is
+		non-zero.
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0c - Client Read Characteristic command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT Characteristic ID (17 octets)
+		                    Authorization (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0d - Client Write Characteristic command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT Characteristic ID (17 octets)
+		                    Write Type (4 octets)
+		                    Length (4 octets)
+		                    Authorization Req. (4 octets)
+		                    Value (variable)
+		Response parameters: <none>
+
+		Valid Write Type: 0x01 = No response
+		                  0x02 = Default
+		                  0x03 = Prepare
+		                  0x04 = Signed
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0e - Client Read Descriptor command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT Characteristic ID (17 octets)
+		                    GATT Descriptor ID (17 octets)
+		                    Authorization Req. (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0f - Client Write Descriptor command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT Characteristic ID (17 octets)
+		                    GATT Descriptor ID (17 octets)
+		                    Write Type (4 octets)
+		                    Length (4 octets)
+		                    Authorization Req. (4 octets)
+		                    Value (variable)
+		Response parameters: <none>
+
+		Valid Write Type: 0x01 = No response
+		                  0x02 = Default
+		                  0x03 = Prepare
+		                  0x04 = Signed
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x10 - Client Execute Write command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    Execute (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x11 - Client Register For Notification command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Remote address (6 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT Characteristic ID (17 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x12 - Client Deregister For Notification command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Remote address (6 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT Characteristic ID (17 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x13 - Client Read Remote RSSI command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x14 - Client Get Device Type command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: Device Type
+
+		Valid Device Type: 0x01 = BREDR
+		                   0x02 = BLE
+		                   0x03 = DUAL
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x15 - Client Set Advertising data command/response
+
+		Command parameters: Server Interface (4 octets)
+		                    Set Scan Resp. (1 octet)
+		                    Include Name (1 octet)
+		                    Include TX Power (1 octet)
+		                    Min. Interval (4 octets)
+		                    Max. Interval (4 octets)
+		                    Appearance (4 octets)
+		                    Manufacturer Len. (2 octets)
+		                    Manufacturer Data (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x16 - Client Test Command command/response
+
+		Command parameters: Command (4 octets)
+		                    Address (6 octets)
+		                    UUID (16 octets)
+		                    U1 (2 octets)
+		                    U2 (2 octets)
+		                    U3 (2 octets)
+		                    U4 (2 octets)
+		                    U5 (2 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x17 - Server Register command/response
+
+		Command parameters: UUID (16 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x18 - Server Unregister command/response
+
+		Command parameters: Server (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x19 - Server Connect Peripheral command/response
+
+		Command parameters: Server (4 octets)
+		                    Remote address (6 octets)
+		                    Is Direct (1 octet)
+		                    Transport (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x1a - Server Disconnect Peripheral command/response
+
+		Command parameters: Server (4 octets)
+		                    Remote address (6 octets)
+		                    Connection ID (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x1b - Server Add Service command/response
+
+		Command parameters: Server (4 octets)
+		                    GATT Service ID (18 octets)
+		                    Number of Handles (4 octet)
+		Response parameters: <none>
+
+		Valid GATT Service ID: UUID (16 octets)
+		                       Instance ID (1 octet)
+		                       Is Primary (1 octet)
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x1c - Server Add Included Service command/response
+
+		Command parameters: Server (4 octets)
+		                    Service handle (4 octets)
+		                    Included handle (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x1d - Server Add Characteristic command/response
+
+		Command parameters: Server (4 octets)
+		                    Service handle (4 octets)
+		                    UUID (16 octets)
+		                    Properties (4 octets)
+		                    Permissions (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x1e - Server Add Descriptor command/response
+
+		Command parameters: Server (4 octets)
+		                    Service handle (4 octets)
+		                    UUID (16 octets)
+		                    Permissions (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x1f - Server Start Service command/response
+
+		Command parameters: Server (4 octets)
+		                    Service handle (4 octets)
+		                    Transport (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x20 - Server Stop Service command/response
+
+		Command parameters: Server (4 octets)
+		                    Service handle (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x21 - Server Delete Service command/response
+
+		Command parameters: Server (4 octets)
+		                    Service handle (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x22 - Server Send Indication command/response
+
+		Command parameters: Server (4 octets)
+		                    Attribute handle (4 octets)
+		                    Connection ID (4 octets)
+		                    Length (4 octets)
+		                    Confirmation (4 octets)
+		                    Value (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x23 - Server Send Response command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    Transaction ID (4 octets)
+		                    Handle (2 octets)
+		                    Offset (2 octets)
+		                    Auth Request (1 octect)
+		                    Status (4 octets)
+		                    GATT Response (4 octets)
+		Response parameters: <none>
+
+		Valid GATT Response: GATT Value (607 octets)
+		                     Handle (2 octets)
+
+		Valid GATT Value: Value (600 octets)
+		                  Handle (2 octets)
+		                  Offset (2 octets)
+		                  Length (2 octets)
+		                  Authentication Request (1 octet)
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x24 - Client Scan Filter Params Setup command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Action (4 octets)
+		                    Filter Index (4 octets)
+		                    Features (4 octets)
+		                    List Type (4 octets)
+		                    Filter Type (4 octets)
+		                    RSSI High Threshold (4 octets)
+		                    RSSI Low Threshold (4 octets)
+		                    Delivery Mode (4 octets)
+		                    Found Timeout (4 octets)
+		                    Lost Timeout (4 octets)
+		                    Found Timeout Count (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x25 - Client Scan Filter Add Remove command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Action (4 octets)
+		                    Filter Type (4 octets)
+		                    Filter Index (4 octets)
+		                    Company ID (4 octets)
+		                    Company ID Mask (4 octets)
+		                    UUID (16 octets)
+		                    UUID Mask (16 octets)
+		                    Address (6 octets)
+		                    Address Type (1 octet)
+		                    Data Length (4 octets)
+		                    Data (variable)
+		                    Mask Length (4 octets)
+		                    Mask (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x26 - Client Scan Filter Clear command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Filter Index (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x27 - Client Scan Filter Enable command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Enable (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x28 - Client Configure MTU command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    MTU (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x29 - Client Connection Parameter Update command/response
+
+		Command parameters: Address (6 octets)
+		                    Min Interval (4 octets)
+		                    Max Interval (4 octets)
+		                    Latency (4 octets)
+		                    Timeoutl (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x2a - Client Set Scan Parameters command/response
+
+		Command parameters: Scan Interval (4 octets)
+		                    Scan Window (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x2b - Client Setup Multi Advertising command/response
+
+		Command parameters: Client ID (4 octets)
+		                    Min Interval (4 octets)
+		                    Max Interval (4 octets)
+		                    ADV Type (4 octets)
+		                    Channel Map (4 octets)
+		                    TX Power (4 octets)
+		                    Timeout (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x2c - Client Update Multi Advertising command/response
+
+		Command parameters: Client ID (4 octets)
+		                    Min Interval (4 octets)
+		                    Max Interval (4 octets)
+		                    ADV Type (4 octets)
+		                    Channel Map (4 octets)
+		                    TX Power (4 octets)
+		                    Timeout (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x2d - Client Setup Multi Advertising Instance command/response
+
+		Command parameters: Client ID (4 octets)
+		                    Set Scan Response (1 octet)
+		                    Include Name (1 octet)
+		                    Include TX Power (1 octet)
+		                    Appearance (4 octets)
+		                    Manufacturer Data Length (4 octets)
+		                    Manufacturer Data (variable)
+		                    Service Data Length (4 octets)
+		                    Service Data (variable)
+		                    Service UUID Length (4 octets)
+		                    Service UUID (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x2e - Client Disable Multi Advertising Instance command/response
+
+		Command parameters: Client ID (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x2f - Client Configure Batchscan command/response
+
+		Command parameters: Client ID (4 octets)
+		                    Full Max (4 octets)
+		                    Trunc Max (4 octets)
+		                    Notify Threshold (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x30 - Client Enable Batchscan command/response
+
+		Command parameters: Client ID (4 octets)
+		                    Scan Mode (4 octets)
+		                    Scan Interval (4 octets)
+		                    Scan Window (4 octets)
+		                    Address Type (4 octets)
+		                    Discard Rule (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x31 - Client Disable Batchscan command/response
+
+		Command parameters: Client ID (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x32 - Client Read Batchscan Resports command/response
+
+		Command parameters: Client ID (4 octets)
+		                    Scan Mode (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Client Register notification
+
+		Notification parameters: Status (4 octets)
+		                         Client Interface (4 octets)
+		                         UUID (16 octets)
+
+	Opcode 0x82 - Client Scan Result notification
+
+		Notification parameters: Address (6 octets)
+		                         RSSI (4 octets)
+		                         Length (2 octets)
+		                         Data (variable)
+
+	Opcode 0x83 - Client Connect Device notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         Client Interface (4 octets)
+		                         Address (6 octets)
+
+	Opcode 0x84 - Client Disconnect Device notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         Client Interface (4 octets)
+		                         Address (6 octets)
+
+	Opcode 0x85 - Client Search Complete notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0x86 - Client Search Result notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         GATT Service ID (18 octets)
+
+	Opcode 0x87 - Client Get Characteristic notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Service ID (18 octets)
+		                         GATT Characteristic ID (17 octets)
+		                         Char Prop. (4 octets)
+
+	Opcode 0x88 - Client Get Descriptor notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Service ID (18 octets)
+		                         GATT Characteristic ID (17 octets)
+		                         GATT Descriptor ID (17 octets)
+
+	Opcode 0x89 - Client Get Included Service notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Service ID (18 octets)
+		                         GATT Included Service ID (18 octets)
+
+	Opcode 0x8a - Client Register For Notification notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Registered (4 octets)
+		                         Status (4 octets)
+		                         GATT Service ID (18 octets)
+		                         GATT Characteristic ID (17 octets)
+
+	Opcode 0x8b - Client Notify notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Address (6 octets)
+		                         GATT Service ID (18 octets)
+		                         GATT Characteristic ID (17 octets)
+		                         Is Notify (1 octet)
+		                         Length (2 octets)
+		                         Value (variable)
+
+	Opcode 0x8c - Client Read Characteristic notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Read Parameters (variable)
+
+		Valid GATT Read Parameters: GATT Service ID (18 octets)
+		                            GATT Characteristic ID (17 octets)
+		                            GATT Descriptor ID (17 octets)
+		                            Value Type (4 octets)
+		                            Status (1 octet)
+		                            Length (2 octets)
+		                            Value (variable)
+
+	Opcode 0x8d - Client Write Characteristic notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Write Parameters (53 octets)
+
+		Valid GATT Write Parameters: GATT Service ID (18 octets)
+		                             GATT Characteristic ID (17 octets)
+		                             GATT Description ID (17 octets)
+		                             Status (1 octet)
+
+	Opcode 0x8e - Client Read Descriptor notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Read Parameters (variable)
+
+		Valid GATT Read Parameters: As described in Read Characteristic
+
+	Opcode 0x8f - Client Write Descriptor notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Write Parameters (53 octets)
+
+		Valid GATT Write Parameters: As described in Write Characteristic
+
+	Opcode 0x90 - Client Execute Write notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0x91 - Client Read Remote RSSI notification
+
+		Notification parameters: Client (4 octets)
+		                         Address (6 octets)
+		                         RSSI (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0x92 - Client Listen notification
+
+		Notification parameters: Status (4 octets)
+		                         Server Interface (4 octets)
+
+	Opcode 0x93 - Server Register notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         UUID (16 octets)
+
+	Opcode 0x94 - Server Connection notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Server (4 octets)
+		                         Connected (4 octets)
+		                         Address (6 octets)
+
+	Opcode 0x95 - Server Service Added notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         GATT Service ID (18 octets)
+		                         Service Handle (4 octets)
+
+	Opcode 0x96 - Server Included Service Added notification
+
+		Notification patemeters: Status (4 octets)
+		                         Server (4 octets)
+		                         Service Handle (4 octets)
+		                         Included Service Handle (4 octets)
+
+	Opcode 0x97 - Server Characteristic Added notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         UUID (16 octets)
+		                         Service Handle (4 octets)
+		                         Characteristic Handle (4 octets)
+
+	Opcode 0x98 - Server Descriptor Added notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         UUID (6 octets)
+		                         Service Handle (4 octets)
+		                         Descriptor Handle (4 octets)
+
+	Opcode 0x99 - Server Service Started notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         Service Handle (4 octets)
+
+	Opcode 0x9a - Server Service Stopped notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         Service Handle (4 octets)
+
+	Opcode 0x9b - Server Service Deleted notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         Service Handle (4 octets)
+
+	Opcode 0x9c - Server Request Read notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Trans ID (4 octets)
+		                         Address (6 octets)
+		                         Attribute Handle (4 octets)
+		                         Offset (4 octets)
+		                         Is Long (1 octet)
+
+	Opcode 0x9d - Server Request Write notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Trans ID (4 octets)
+		                         Address (6 octets)
+		                         Attribute Handle (4 octets)
+		                         Offset (4 octets)
+		                         Length (4 octets)
+		                         Need Response (4 octets)
+		                         Is Prepare (1 octet)
+		                         Value (variable)
+
+	Opcode 0x9e - Server Request Execute Write notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Trans ID (4 octets)
+		                         Address (6 octets)
+		                         Execute Write (4 octets)
+
+	Opcode 0x9f - Server Response Confirmation notification
+
+		Notification parameters: Status (4 octets)
+		                         Handle (4 octets)
+
+	Opcode 0xa0 - Client Configure MTU notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         MTU (4 octets)
+
+	Opcode 0xa1 - Client Filter Configuration notification
+
+		Notification parameters: Action (4 octets)
+		                         Client ID (4 octets)
+		                         Status (4 octets)
+		                         Filter Type (4 octets)
+		                         Available Space (4 octets)
+
+	Opcode 0xa2 - Client Filter Parameters notification
+
+		Notification parameters: Action (4 octets)
+		                         Client ID (4 octets)
+		                         Status (4 octets)
+		                         Available Space (4 octets)
+
+	Opcode 0xa3 - Client Filter Status notification
+
+		Notification parameters: Enable (4 octets)
+		                         Client ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xa4 - Client Multi Advertising Enable notification
+
+		Notification parameters: Client ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xa5 - Client Multi Advertising Update notification
+
+		Notification parameters: Client ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xa6 - Client Multi Advertising Data notification
+
+		Notification parameters: Client ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xa7 - Client Multi Advertising Disable notification
+
+		Notification parameters: Client ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xa8 - Client Congestion notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Congested (1 octet)
+
+	Opcode 0xa9 - Client Configure Batchscan notification
+
+		Notification parameters: Client ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xaa - Client Enable Batchscan notification
+
+		Notification parameters: Action (4 octets)
+		                         Client ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xab - Client Batchscan Reports notification
+
+		Notification parameters: Client ID (4 octets)
+		                         Status (4 octets)
+		                         Report Format (4 octets)
+		                         Num Reports (4 octets)
+		                         Data Length (4 octets)
+		                         Data (variable)
+
+	Opcode 0xac - Client Batchscan Threshold notification
+
+		Notification parameters: Client ID (4 octets)
+
+	Opcode 0xad - Client Track ADV notification
+
+		Notification parameters: Client ID (4 octets)
+		                         Filter Index (4 octets)
+		                         Address Type (4 octets)
+		                         Address (6 octets)
+		                         State (4 octets)
+
+	Opcode 0xae - Server Indication Sent notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xaf - Server Congestion notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Congested (1 octet)
+
+	Opcode 0xb0 - Server MTU Changed notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         MTU (4 octets)
+
+
+Bluetooth Handsfree Client HAL (ID 10)
+======================================
+
+Android HAL name: "hf_client" (BT_PROFILE_HANDSFREE_CLIENT_ID)
+
+Commands and response:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Connect command/respose
+
+		Command parameters: Remote address (6 octects)
+		Response parameters: <none>
+
+		 In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Disonnect command/response
+
+		Command parameters: Remote address (6 octetcs)
+		Response parameters: <none>
+
+		 In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Connect Audio command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Disconnect Audio command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x05 - Start Voice Recognition command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x06 - Stop Voice Recognition command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x07 - Volume Control command/response
+
+		Command parameters: Volume type (1 octet)
+		                    Volume (1 octet)
+		Response parameters: <none>
+
+		Valid volume types: 0x00 = Speaker
+		                    0x01 = Microphone
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x08 - Dial command/response
+
+		Command parameters: Number (string)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x09 - Dial Memory command/response
+
+		Command parameters: Location (4 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x10 - Handle Call Action command/response
+
+		Command parameters: Action (1 octet)
+		                    Call Index (1 octet)
+		Response parameters: <none>
+
+		Valid actions: 0x00 = CHLD_0
+		               0x01 = CHLD_1
+		               0x02 = CHLD_2
+		               0x03 = CHLD_3
+		               0x04 = CHLD_4
+		               0x05 = CHLD_1x
+		               0x06 = CHLD_2x
+		               0x07 = ATA
+		               0x08 = CHUP
+		               0x09 = BTRH_0
+		               0x10 = BTRH_1
+		               0x11 = BTRH_2
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x11 - Query Current Calls commad/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x12 - Query Current Operator Name
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x13 - Retrieve Subscriber Info command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x14 - Send DTMF Tone command/response
+
+		Command parameters: Tone (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x15 - Request Last Voice Tag Number command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Connection State Changed notification
+
+		Notification parameters: State (1 octet)
+		                         Peer Features (4 octets)
+		                         CHLD Features (4 octets)
+		                         Address (6 octets)
+
+		Valid State values: 0x00 = Disconnected
+		                    0x01 = Connecting
+		                    0x02 = Connected
+		                    0x03 = SLC Connected
+		                    0x04 = Disconnecting
+
+		Peer Features is a bitmask of the supported features. Currently
+		available bits:
+
+			0	Three way calling
+			1	Echo cancellation and/or noise reduction
+			2	Voice recognition
+			3	In band ring tone
+			4	Attach a number to a voice tag
+			5	Ability to reject a call
+			6	Enhanced call status
+			7	Enhanced call control
+			8	Extended Error Result Codes
+			9	Codec negotiations
+			10-31	Reserved for future use
+
+		CHLD Features is a bitmask of the supported features. Currently
+		available bits:
+
+			0	Release waiting call or held calls
+			1	Release active calls and accept other call
+			2	Release specified active call only
+			3	Place all active calls on hold and accept other call
+			4	Request private mode with secified call
+			5	Add a held call to the multiparty
+			6	Connect two calls and leave multiparty
+			7-31	Reserved for future use
+
+		Note: Peer and CHLD Features are valid only in SCL Connected state
+
+
+	Opcode 0x82 - Audio State Changed notification
+
+		Notification parameters: State (1 octet)
+		                         Address (6 octets)
+
+		Valid State values: 0x00 = Disconnected
+		                    0x01 = Connecting
+		                    0x02 = Connected
+		                    0x03 = Connected mSBC
+
+	Opcode 0x83 - Voice Recognition State Changed notification
+
+		Notification parameters: State (1 octet)
+
+		Valid State values: 0x00 = VR Stopped
+		                    0x01 = VR Started
+
+	Opcode 0x84 - Network State Changed notification
+
+		Notification parameters: State (1 octet)
+
+		Valid State values: 0x00 = Network Not Available
+		                    0x01 = Network Available
+
+	Opcode 0x85 - Network Roaming Type Changed notification
+
+		Notification parameters: Type (1 octet)
+
+		Valid Type values: 0x00 = Home
+		                   0x01 = Roaming
+
+	Opcode 0x86 - Network Signal Strength notification
+
+		Notification parameters: Signal Strength (1 octet)
+
+	Opcode 0x87 - Battery Level notification
+
+		Notification parameters: Battery Level (1 octet)
+
+	Opcode 0x88 - Current Operator Name notification
+
+		Notification parameters: Name (string)
+
+	Opcode 0x89 - Call Indicatior notification
+
+		Notification parameters: Call (1 octet)
+
+		Valid Call values: 0x00 = No Call In Progress
+		                   0x01 = Call In Progress
+
+	Opcode 0x8a - Call Setup Indicator notification
+
+		Notification parameters: Call Setup (1 octet)
+
+		Valid Call Setup values: 0x00 = None
+		                         0x01 = Incoming
+		                         0x02 = Outgoing
+		                         0x03 = Alerting
+
+	Opcode 0x8b - Call Held Indicator notification
+
+		Notification parameters: Call Held (1 octet)
+
+		Valid Call Held values: 0x00 = None
+		                        0x01 = Hold and Active
+					0x02 = Hold
+
+	Opcode 0x8c - Resposne and Hold Status notification
+
+		Notification parameters: Status (1 octet)
+
+		Valid Status values: 0x00 = Held
+		                     0x01 = Accept
+		                     0x02 = Reject
+
+	Opcode 0x8d - Calling Line Identification notification
+
+		Notification parameters: Number (string)
+
+		Note: This will be called only on incoming call if number is
+		provided.
+
+	Opcode 0x8e - Call Waiting notification
+
+		Notification parameters: Nunmber (string)
+
+	Opcode 0x8f - Current Calls List notification
+
+		Notification parameters: Index (1 octet)
+		                         Direction (1 octet)
+		                         Call State (1 octet)
+		                         Multiparty (1 octet)
+		                         Number (string)
+
+		Valid Direction values: 0x00 = Outgoing
+		                        0x01 = Incoming
+
+		Valid Call Sate values: 0x00 = Active
+		                        0x01 = Held
+		                        0x02 = Dialing
+		                        0x03 = Alerting
+		                        0x04 = Incoming
+		                        0x05 = Waiting
+		                        0x06 = Call held by Response and Hold
+
+		Valid Multiparty values: 0x00 = Single Call
+		                         0x01 = Multiparty (conference) Call
+
+		Note: Number might be empty
+
+	Opcode 0x90 - Volume Changed notification
+
+		Notification parameters: Type (1 octet)
+		                         Volume (1 octet)
+
+		Valid Type values: 0x00 = Speaker
+		                   0x01 = Microphone
+
+	Opcode 0x91 - Command Complete Callback notification
+
+		Notification parameters: Type (1 octet)
+		                         CME (1 octet)
+
+		Valid Type values: 0x00 = OK
+		                   0x01 = Error
+		                   0x02 = Error no carrier
+		                   0x03 = Error busy
+		                   0x04 = Error no answer
+		                   0x05 = Error delayed
+		                   0x06 = Error blacklisted
+		                   0x07 = Error CME
+
+		Note: CME parameter is valid only for Error CME type
+
+	Opcode 0x92 - Subscriber Service Info Callback notification
+
+		Notification parameters: Name (string)
+		                         Type (1 octet)
+
+		Valid Type values: 0x00 = Service unknown
+		                   0x01 = Service voice
+		                   0x02 = Service fax
+
+	Opcode 0x93 - In Band Ring Settings Callback notification
+
+		Notification parameters: State (1 octet)
+
+		Valid State values: 0x00 = In band ringtone not provided
+		                    0x01 = In band ringtone provided
+
+	Opcode 0x94 - Last Voice Call Tag Number Callback notification
+
+		Notification parameters: Number (string)
+
+	Opcode 0x95 - Ring Indication notification
+
+		Notification parameters: <none>
+
+
+Bluetooth Map Client HAL (ID 11)
+=========================
+
+Android HAL name: "map_client" (BT_PROFILE_MAP_CLIENT_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Get Remote MAS Instances
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Remote MAS Instances notification
+
+		Notification parameters: Status (1 octet)
+		                         Remote address (6 octets)
+		                         Number of instances (4 octets)
+		                         Instance ID # (4 octets)
+		                         Channel # (4 octets)
+		                         Message types (4 octets)
+		                         Name # (string)
+
+Bluetooth Remote Control Controller HAL (ID 12)
+===================================
+
+Android HAL name: "avrcp-ctrl" (BT_PROFILE_AV_RC_CTRL_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Send Pass Through command/response
+
+		Command parameters: Remote Address (6 octets)
+		                    Key Code (1 octet)
+		                    Key State (1 octet)
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Passthrough Response Notification
+
+		Notification parameters: ID (1 octet)
+		                         Key State (1 octet)
+
+	Opcode 0x82 - Connection State Notification
+
+		Notification parameters: State (1 octet)
+		                         Remote Address (6 octets)
diff --git a/repo/android/hal-ipc.c b/repo/android/hal-ipc.c
new file mode 100644
index 0000000..363072c
--- /dev/null
+++ b/repo/android/hal-ipc.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <pthread.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdbool.h>
+#include <poll.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+
+#include "hal.h"
+#include "hal-msg.h"
+#include "hal-log.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+#define CONNECT_TIMEOUT (10 * 1000)
+
+static int listen_sk = -1;
+static int cmd_sk = -1;
+static int notif_sk = -1;
+
+static pthread_mutex_t cmd_sk_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static pthread_t notif_th = 0;
+
+struct service_handler {
+	const struct hal_ipc_handler *handler;
+	uint8_t size;
+};
+
+static struct service_handler services[HAL_SERVICE_ID_MAX + 1];
+
+void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers,
+								uint8_t size)
+{
+	services[service].handler = handlers;
+	services[service].size = size;
+}
+
+void hal_ipc_unregister(uint8_t service)
+{
+	services[service].handler = NULL;
+	services[service].size = 0;
+}
+
+static bool handle_msg(void *buf, ssize_t len, int fd)
+{
+	struct ipc_hdr *msg = buf;
+	const struct hal_ipc_handler *handler;
+	uint8_t opcode;
+
+	if (len < (ssize_t) sizeof(*msg)) {
+		error("IPC: message too small (%zd bytes)", len);
+		return false;
+	}
+
+	if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
+		error("IPC: message malformed (%zd bytes)", len);
+		return false;
+	}
+
+	/* if service is valid */
+	if (msg->service_id > HAL_SERVICE_ID_MAX) {
+		error("IPC: unknown service (0x%x)", msg->service_id);
+		return false;
+	}
+
+	/* if service is registered */
+	if (!services[msg->service_id].handler) {
+		error("IPC: unregistered service (0x%x)", msg->service_id);
+		return false;
+	}
+
+	/* if opcode fit valid range */
+	if (msg->opcode < HAL_MINIMUM_EVENT) {
+		error("IPC: invalid opcode for service 0x%x (0x%x)",
+						msg->service_id, msg->opcode);
+		return false;
+	}
+
+	/*
+	 * opcode is used as table offset and must be adjusted as events start
+	 * with HAL_MINIMUM_EVENT offset
+	 */
+	opcode = msg->opcode - HAL_MINIMUM_EVENT;
+
+	/* if opcode is valid */
+	if (opcode >= services[msg->service_id].size) {
+		error("IPC: invalid opcode for service 0x%x (0x%x)",
+						msg->service_id, msg->opcode);
+		return false;
+	}
+
+	handler = &services[msg->service_id].handler[opcode];
+
+	/* if payload size is valid */
+	if ((handler->var_len && handler->data_len > msg->len) ||
+			(!handler->var_len && handler->data_len != msg->len)) {
+		error("IPC: message size invalid for service 0x%x opcode 0x%x "
+				"(%u bytes)",
+				msg->service_id, msg->opcode, msg->len);
+		return false;
+	}
+
+	handler->handler(msg->payload, msg->len, fd);
+
+	return true;
+}
+
+static void *notification_handler(void *data)
+{
+	struct msghdr msg;
+	struct iovec iv;
+	struct cmsghdr *cmsg;
+	char cmsgbuf[CMSG_SPACE(sizeof(int))];
+	char buf[IPC_MTU];
+	ssize_t ret;
+	int fd;
+
+	bt_thread_associate();
+
+	while (true) {
+		memset(&msg, 0, sizeof(msg));
+		memset(buf, 0, sizeof(buf));
+		memset(cmsgbuf, 0, sizeof(cmsgbuf));
+
+		iv.iov_base = buf;
+		iv.iov_len = sizeof(buf);
+
+		msg.msg_iov = &iv;
+		msg.msg_iovlen = 1;
+
+		msg.msg_control = cmsgbuf;
+		msg.msg_controllen = sizeof(cmsgbuf);
+
+		ret = recvmsg(notif_sk, &msg, 0);
+		if (ret < 0) {
+			error("Receiving notifications failed: %s",
+							strerror(errno));
+			goto failed;
+		}
+
+		/* socket was shutdown */
+		if (ret == 0) {
+			pthread_mutex_lock(&cmd_sk_mutex);
+			if (cmd_sk == -1) {
+				pthread_mutex_unlock(&cmd_sk_mutex);
+				break;
+			}
+			pthread_mutex_unlock(&cmd_sk_mutex);
+
+			error("Notification socket closed");
+			goto failed;
+		}
+
+		fd = -1;
+
+		/* Receive auxiliary data in msg */
+		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+					cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+			if (cmsg->cmsg_level == SOL_SOCKET
+					&& cmsg->cmsg_type == SCM_RIGHTS) {
+				memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
+				break;
+			}
+		}
+
+		if (!handle_msg(buf, ret, fd))
+			goto failed;
+	}
+
+	close(notif_sk);
+	notif_sk = -1;
+
+	bt_thread_disassociate();
+
+	DBG("exit");
+
+	return NULL;
+
+failed:
+	exit(EXIT_FAILURE);
+}
+
+static int accept_connection(int sk)
+{
+	int err;
+	struct pollfd pfd;
+	int new_sk;
+
+	memset(&pfd, 0 , sizeof(pfd));
+	pfd.fd = sk;
+	pfd.events = POLLIN;
+
+	err = poll(&pfd, 1, CONNECT_TIMEOUT);
+	if (err < 0) {
+		err = errno;
+		error("Failed to poll: %d (%s)", err, strerror(err));
+		return -1;
+	}
+
+	if (err == 0) {
+		error("bluetoothd connect timeout");
+		return -1;
+	}
+
+	new_sk = accept(sk, NULL, NULL);
+	if (new_sk < 0) {
+		err = errno;
+		error("Failed to accept socket: %d (%s)", err, strerror(err));
+		return -1;
+	}
+
+	return new_sk;
+}
+
+bool hal_ipc_accept(void)
+{
+	int err;
+
+	cmd_sk = accept_connection(listen_sk);
+	if (cmd_sk < 0)
+		return false;
+
+	notif_sk = accept_connection(listen_sk);
+	if (notif_sk < 0) {
+		close(cmd_sk);
+		cmd_sk = -1;
+		return false;
+	}
+
+	err = pthread_create(&notif_th, NULL, notification_handler, NULL);
+	if (err) {
+		notif_th = 0;
+		error("Failed to start notification thread: %d (%s)", err,
+							strerror(err));
+		close(cmd_sk);
+		cmd_sk = -1;
+		close(notif_sk);
+		notif_sk = -1;
+		return false;
+	}
+
+	info("IPC connected");
+
+	return true;
+}
+
+bool hal_ipc_init(const char *path, size_t size)
+{
+	struct sockaddr_un addr;
+	int sk;
+	int err;
+
+	sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
+	if (sk < 0) {
+		err = errno;
+		error("Failed to create socket: %d (%s)", err,
+							strerror(err));
+		return false;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+
+	memcpy(addr.sun_path, path, size);
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		err = errno;
+		error("Failed to bind socket: %d (%s)", err, strerror(err));
+		close(sk);
+		return false;
+	}
+
+	if (listen(sk, 2) < 0) {
+		err = errno;
+		error("Failed to listen on socket: %d (%s)", err,
+								strerror(err));
+		close(sk);
+		return false;
+	}
+
+	listen_sk = sk;
+
+	return true;
+}
+
+void hal_ipc_cleanup(void)
+{
+	close(listen_sk);
+	listen_sk = -1;
+
+	pthread_mutex_lock(&cmd_sk_mutex);
+	if (cmd_sk >= 0) {
+		close(cmd_sk);
+		cmd_sk = -1;
+	}
+	pthread_mutex_unlock(&cmd_sk_mutex);
+
+	if (notif_sk < 0)
+		return;
+
+	shutdown(notif_sk, SHUT_RD);
+
+	pthread_join(notif_th, NULL);
+	notif_th = 0;
+}
+
+int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
+					size_t *rsp_len, void *rsp, int *fd)
+{
+	ssize_t ret;
+	struct msghdr msg;
+	struct iovec iv[2];
+	struct ipc_hdr cmd;
+	char cmsgbuf[CMSG_SPACE(sizeof(int))];
+	struct ipc_status s;
+	size_t s_len = sizeof(s);
+
+	if (cmd_sk < 0) {
+		error("Invalid cmd socket passed to hal_ipc_cmd");
+		goto failed;
+	}
+
+	if (!rsp || !rsp_len) {
+		memset(&s, 0, s_len);
+		rsp_len = &s_len;
+		rsp = &s;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.service_id = service_id;
+	cmd.opcode = opcode;
+	cmd.len = len;
+
+	iv[0].iov_base = &cmd;
+	iv[0].iov_len = sizeof(cmd);
+
+	iv[1].iov_base = param;
+	iv[1].iov_len = len;
+
+	msg.msg_iov = iv;
+	msg.msg_iovlen = 2;
+
+	pthread_mutex_lock(&cmd_sk_mutex);
+
+	ret = sendmsg(cmd_sk, &msg, 0);
+	if (ret < 0) {
+		error("Sending command failed:%s", strerror(errno));
+		pthread_mutex_unlock(&cmd_sk_mutex);
+		goto failed;
+	}
+
+	/* socket was shutdown */
+	if (ret == 0) {
+		error("Command socket closed");
+		pthread_mutex_unlock(&cmd_sk_mutex);
+		goto failed;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&cmd, 0, sizeof(cmd));
+
+	iv[0].iov_base = &cmd;
+	iv[0].iov_len = sizeof(cmd);
+
+	iv[1].iov_base = rsp;
+	iv[1].iov_len = *rsp_len;
+
+	msg.msg_iov = iv;
+	msg.msg_iovlen = 2;
+
+	if (fd) {
+		memset(cmsgbuf, 0, sizeof(cmsgbuf));
+		msg.msg_control = cmsgbuf;
+		msg.msg_controllen = sizeof(cmsgbuf);
+	}
+
+	ret = recvmsg(cmd_sk, &msg, 0);
+
+	pthread_mutex_unlock(&cmd_sk_mutex);
+
+	if (ret < 0) {
+		error("Receiving command response failed: %s", strerror(errno));
+		goto failed;
+	}
+
+
+	if (ret < (ssize_t) sizeof(cmd)) {
+		error("Too small response received(%zd bytes)", ret);
+		goto failed;
+	}
+
+	if (cmd.service_id != service_id) {
+		error("Invalid service id (0x%x vs 0x%x)",
+						cmd.service_id, service_id);
+		goto failed;
+	}
+
+	if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
+		error("Malformed response received(%zd bytes)", ret);
+		goto failed;
+	}
+
+	if (cmd.opcode != opcode && cmd.opcode != HAL_OP_STATUS) {
+		error("Invalid opcode received (0x%x vs 0x%x)",
+						cmd.opcode, opcode);
+		goto failed;
+	}
+
+	if (cmd.opcode == HAL_OP_STATUS) {
+		struct ipc_status *s = rsp;
+
+		if (sizeof(*s) != cmd.len) {
+			error("Invalid status length");
+			goto failed;
+		}
+
+		if (s->code == HAL_STATUS_SUCCESS) {
+			error("Invalid success status response");
+			goto failed;
+		}
+
+		return s->code;
+	}
+
+	/* Receive auxiliary data in msg */
+	if (fd) {
+		struct cmsghdr *cmsg;
+
+		*fd = -1;
+
+		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+					cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+			if (cmsg->cmsg_level == SOL_SOCKET
+					&& cmsg->cmsg_type == SCM_RIGHTS) {
+				memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+				break;
+			}
+		}
+	}
+
+	*rsp_len = cmd.len;
+
+	return BT_STATUS_SUCCESS;
+
+failed:
+	exit(EXIT_FAILURE);
+}
diff --git a/repo/android/hal-ipc.h b/repo/android/hal-ipc.h
new file mode 100644
index 0000000..08ed7cc
--- /dev/null
+++ b/repo/android/hal-ipc.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+struct hal_ipc_handler {
+	void (*handler) (void *buf, uint16_t len, int fd);
+	bool var_len;
+	size_t data_len;
+};
+
+bool hal_ipc_init(const char *path, size_t size);
+bool hal_ipc_accept(void);
+void hal_ipc_cleanup(void);
+
+int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
+					size_t *rsp_len, void *rsp, int *fd);
+
+void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers,
+								uint8_t size);
+void hal_ipc_unregister(uint8_t service);
diff --git a/repo/android/hal-log.h b/repo/android/hal-log.h
new file mode 100644
index 0000000..63ff61b
--- /dev/null
+++ b/repo/android/hal-log.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define LOG_TAG "BlueZ"
+
+#ifdef __BIONIC__
+#include <cutils/log.h>
+#else
+#include <stdio.h>
+#define LOG_INFO " I"
+#define LOG_WARN " W"
+#define LOG_ERROR " E"
+#define LOG_DEBUG " D"
+#define ALOG(pri, tag, fmt, arg...) fprintf(stderr, tag pri": " fmt"\n", ##arg)
+#endif
+
+#define info(fmt, arg...) ALOG(LOG_INFO, LOG_TAG, fmt, ##arg)
+#define warn(fmt, arg...) ALOG(LOG_WARN, LOG_TAG, fmt, ##arg)
+#define error(fmt, arg...) ALOG(LOG_ERROR, LOG_TAG, fmt, ##arg)
+#define DBG(fmt, arg...) ALOG(LOG_DEBUG, LOG_TAG, "%s:%s() "fmt, __FILE__, \
+							__func__, ##arg)
diff --git a/repo/android/hal-map-client.c b/repo/android/hal-map-client.c
new file mode 100644
index 0000000..adf04fc
--- /dev/null
+++ b/repo/android/hal-map-client.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "hal-ipc.h"
+
+static const btmce_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+/* Event Handlers */
+
+static void remote_mas_instances_to_hal(btmce_mas_instance_t *send_instance,
+				struct hal_map_client_mas_instance *instance,
+				int num_instances, uint16_t len)
+{
+	void *buf = instance;
+	char *name;
+	int i;
+
+	DBG("");
+
+	for (i = 0; i < num_instances; i++) {
+		name = (char *) instance->name;
+		if (sizeof(*instance) + instance->name_len > len ||
+					(instance->name_len != 0 &&
+					name[instance->name_len - 1] != '\0')) {
+			error("invalid remote mas instance %d, aborting", i);
+			exit(EXIT_FAILURE);
+		}
+
+		send_instance[i].id = instance->id;
+		send_instance[i].msg_types = instance->msg_types;
+		send_instance[i].scn = instance->scn;
+		send_instance[i].p_name = name;
+
+		len -= sizeof(*instance) + instance->name_len;
+		buf += sizeof(*instance) + instance->name_len;
+		instance = buf;
+	}
+
+	if (!len)
+		return;
+
+	error("invalid remote mas instances (%u bytes left), aborting", len);
+	exit(EXIT_FAILURE);
+}
+
+static void handle_remote_mas_instances(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_map_client_remote_mas_instances *ev = buf;
+	btmce_mas_instance_t instances[ev->num_instances];
+
+	DBG("");
+
+	len -= sizeof(*ev);
+	remote_mas_instances_to_hal(instances, ev->instances, ev->num_instances,
+									len);
+
+	if (cbs->remote_mas_instances_cb)
+		cbs->remote_mas_instances_cb(ev->status,
+						(bt_bdaddr_t *) ev->bdaddr,
+						ev->num_instances, instances);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_MCE_REMOTE_MAS_INSTANCES */
+	{ handle_remote_mas_instances, true,
+			sizeof(struct hal_ev_map_client_remote_mas_instances) }
+};
+
+/* API */
+
+static bt_status_t get_remote_mas_instances(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_map_client_get_instances cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_MAP_CLIENT,
+				HAL_OP_MAP_CLIENT_GET_INSTANCES, sizeof(cmd),
+				&cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t init(btmce_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	/*
+	 * Interface ready check was removed because there is no cleanup
+	 * function to unregister and clear callbacks. MAP client testers may
+	 * restart bluetooth, unregister this profile and try to reuse it.
+	 * This situation make service unregistered but callbacks are still
+	 * set - interface is ready. On android devices there is no need to
+	 * re-init MAP client profile while bluetooth is loaded.
+	 */
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_MAP_CLIENT, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_MAP_CLIENT;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_MAP_CLIENT);
+	}
+
+	return ret;
+}
+
+static btmce_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.get_remote_mas_instances = get_remote_mas_instances
+};
+
+btmce_interface_t *bt_get_map_client_interface(void)
+{
+	return &iface;
+}
diff --git a/repo/android/hal-msg.h b/repo/android/hal-msg.h
new file mode 100644
index 0000000..ea79fa7
--- /dev/null
+++ b/repo/android/hal-msg.h
@@ -0,0 +1,2335 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013  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
+ *
+ */
+
+static const char BLUEZ_HAL_SK_PATH[] = "\0bluez_hal_socket";
+
+#define HAL_MINIMUM_EVENT		0x81
+
+#define HAL_SERVICE_ID_CORE		0
+#define HAL_SERVICE_ID_BLUETOOTH	1
+#define HAL_SERVICE_ID_SOCKET		2
+#define HAL_SERVICE_ID_HIDHOST		3
+#define HAL_SERVICE_ID_PAN		4
+#define HAL_SERVICE_ID_HANDSFREE	5
+#define HAL_SERVICE_ID_A2DP		6
+#define HAL_SERVICE_ID_HEALTH		7
+#define HAL_SERVICE_ID_AVRCP		8
+#define HAL_SERVICE_ID_GATT		9
+#define HAL_SERVICE_ID_HANDSFREE_CLIENT	10
+#define HAL_SERVICE_ID_MAP_CLIENT	11
+#define HAL_SERVICE_ID_AVRCP_CTRL	12
+#define HAL_SERVICE_ID_A2DP_SINK	13
+
+#define HAL_SERVICE_ID_MAX HAL_SERVICE_ID_A2DP_SINK
+
+/* Core Service */
+
+#define HAL_STATUS_SUCCESS		IPC_STATUS_SUCCESS
+#define HAL_STATUS_FAILED		0x01
+#define HAL_STATUS_NOT_READY		0x02
+#define HAL_STATUS_NOMEM		0x03
+#define HAL_STATUS_BUSY			0x04
+#define HAL_STATUS_DONE			0x05
+#define HAL_STATUS_UNSUPPORTED		0x06
+#define HAL_STATUS_INVALID		0x07
+#define HAL_STATUS_UNHANDLED		0x08
+#define HAL_STATUS_AUTH_FAILURE		0x09
+#define HAL_STATUS_REMOTE_DEVICE_DOWN	0x0a
+
+#define HAL_OP_STATUS			IPC_OP_STATUS
+
+#define HAL_MODE_DEFAULT		0x00
+#define HAL_MODE_BREDR			0x01
+#define HAL_MODE_LE			0x02
+
+#define HAL_OP_REGISTER_MODULE		0x01
+struct hal_cmd_register_module {
+	uint8_t service_id;
+	uint8_t mode;
+	int32_t max_clients;
+} __attribute__((packed));
+
+#define HAL_OP_UNREGISTER_MODULE	0x02
+struct hal_cmd_unregister_module {
+	uint8_t service_id;
+} __attribute__((packed));
+
+#define HAL_CONFIG_VENDOR		0x00
+#define HAL_CONFIG_MODEL		0x01
+#define HAL_CONFIG_NAME			0x02
+#define HAL_CONFIG_SERIAL_NUMBER	0x03
+#define HAL_CONFIG_SYSTEM_ID		0x04
+#define HAL_CONFIG_PNP_ID		0x05
+#define HAL_CONFIG_FW_REV		0x06
+#define HAL_CONFIG_HW_REV		0x07
+
+struct hal_config_prop {
+	uint8_t type;
+	uint16_t len;
+	uint8_t val[0];
+} __attribute__((packed));
+
+#define HAL_OP_CONFIGURATION		0x03
+struct hal_cmd_configuration {
+	uint8_t num;
+	struct hal_config_prop props[0];
+} __attribute__((packed));
+
+/* Bluetooth Core HAL API */
+
+#define HAL_OP_ENABLE			0x01
+
+#define HAL_OP_DISABLE			0x02
+
+#define HAL_OP_GET_ADAPTER_PROPS	0x03
+
+#define HAL_OP_GET_ADAPTER_PROP		0x04
+struct hal_cmd_get_adapter_prop {
+	uint8_t type;
+} __attribute__((packed));
+
+#define HAL_MAX_NAME_LENGTH		249
+
+#define HAL_PROP_ADAPTER_NAME			0x01
+#define HAL_PROP_ADAPTER_ADDR			0x02
+#define HAL_PROP_ADAPTER_UUIDS			0x03
+#define HAL_PROP_ADAPTER_CLASS			0x04
+#define HAL_PROP_ADAPTER_TYPE			0x05
+#define HAL_PROP_ADAPTER_SERVICE_REC		0x06
+#define HAL_PROP_ADAPTER_SCAN_MODE		0x07
+#define HAL_PROP_ADAPTER_BONDED_DEVICES		0x08
+#define HAL_PROP_ADAPTER_DISC_TIMEOUT		0x09
+
+#define HAL_PROP_DEVICE_NAME			0x01
+#define HAL_PROP_DEVICE_ADDR			0x02
+#define HAL_PROP_DEVICE_UUIDS			0x03
+#define HAL_PROP_DEVICE_CLASS			0x04
+#define HAL_PROP_DEVICE_TYPE			0x05
+#define HAL_PROP_DEVICE_SERVICE_REC		0x06
+struct hal_prop_device_service_rec {
+	uint8_t uuid[16];
+	uint16_t channel;
+	uint8_t name_len;
+	uint8_t name[];
+} __attribute__((packed));
+
+#define HAL_PROP_DEVICE_FRIENDLY_NAME		0x0a
+#define HAL_PROP_DEVICE_RSSI			0x0b
+#define HAL_PROP_DEVICE_VERSION_INFO		0x0c
+struct hal_prop_device_info {
+	uint8_t version;
+	uint16_t sub_version;
+	uint16_t manufacturer;
+} __attribute__((packed));
+
+#define HAL_PROP_ADAPTER_LOCAL_LE_FEAT		0x0d
+#define HAL_PROP_DEVICE_TIMESTAMP		0xFF
+
+#define HAL_ADAPTER_SCAN_MODE_NONE		0x00
+#define HAL_ADAPTER_SCAN_MODE_CONN		0x01
+#define HAL_ADAPTER_SCAN_MODE_CONN_DISC	0x02
+
+#define HAL_TYPE_BREDR				0x01
+#define HAL_TYPE_LE				0x02
+#define HAL_TYPE_DUAL				0x03
+
+#define HAL_OP_SET_ADAPTER_PROP		0x05
+struct hal_cmd_set_adapter_prop {
+	uint8_t  type;
+	uint16_t len;
+	uint8_t  val[0];
+} __attribute__((packed));
+
+#define HAL_OP_GET_REMOTE_DEVICE_PROPS	0x06
+struct hal_cmd_get_remote_device_props {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_GET_REMOTE_DEVICE_PROP	0x07
+struct hal_cmd_get_remote_device_prop {
+	uint8_t bdaddr[6];
+	uint8_t type;
+} __attribute__((packed));
+
+#define HAL_OP_SET_REMOTE_DEVICE_PROP	0x08
+struct hal_cmd_set_remote_device_prop {
+	uint8_t  bdaddr[6];
+	uint8_t  type;
+	uint16_t len;
+	uint8_t  val[0];
+} __attribute__((packed));
+
+#define HAL_OP_GET_REMOTE_SERVICE_REC	0x09
+struct hal_cmd_get_remote_service_rec {
+	uint8_t bdaddr[6];
+	uint8_t uuid[16];
+} __attribute__((packed));
+
+#define HAL_OP_GET_REMOTE_SERVICES	0x0a
+struct hal_cmd_get_remote_services {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_START_DISCOVERY		0x0b
+
+#define HAL_OP_CANCEL_DISCOVERY		0x0c
+
+#define BT_TRANSPORT_UNKNOWN		0x00
+#define BT_TRANSPORT_BR_EDR		0x01
+#define BT_TRANSPORT_LE			0x02
+
+#define HAL_OP_CREATE_BOND		0x0d
+struct hal_cmd_create_bond {
+	uint8_t bdaddr[6];
+	uint8_t transport;
+} __attribute__((packed));
+
+#define HAL_OP_REMOVE_BOND		0x0e
+struct hal_cmd_remove_bond {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_CANCEL_BOND		0x0f
+struct hal_cmd_cancel_bond {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_PIN_REPLY		0x10
+struct hal_cmd_pin_reply {
+	uint8_t bdaddr[6];
+	uint8_t accept;
+	uint8_t pin_len;
+	uint8_t pin_code[16];
+} __attribute__((packed));
+
+#define HAL_SSP_VARIANT_CONFIRM		0x00
+#define HAL_SSP_VARIANT_ENTRY		0x01
+#define HAL_SSP_VARIANT_CONSENT		0x02
+#define HAL_SSP_VARIANT_NOTIF		0x03
+
+#define HAL_OP_SSP_REPLY		0x11
+struct hal_cmd_ssp_reply {
+	uint8_t  bdaddr[6];
+	uint8_t  ssp_variant;
+	uint8_t  accept;
+	uint32_t passkey;
+} __attribute__((packed));
+
+#define HAL_OP_DUT_MODE_CONF		0x12
+struct hal_cmd_dut_mode_conf {
+	uint8_t enable;
+} __attribute__((packed));
+
+#define HAL_OP_DUT_MODE_SEND		0x13
+struct hal_cmd_dut_mode_send {
+	uint16_t opcode;
+	uint8_t  len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+#define HAL_OP_LE_TEST_MODE		0x14
+struct hal_cmd_le_test_mode {
+	uint16_t opcode;
+	uint8_t  len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+#define HAL_OP_GET_CONNECTION_STATE	0x15
+struct hal_cmd_get_connection_state {
+	uint8_t  bdaddr[6];
+} __attribute__((packed));
+
+struct hal_rsp_get_connection_state {
+	int32_t connection_state;
+} __attribute__((packed));
+
+#define HAL_OP_READ_ENERGY_INFO		0x16
+
+/* Bluetooth Socket HAL api */
+
+#define HAL_MODE_SOCKET_DEFAULT		HAL_MODE_DEFAULT
+#define HAL_MODE_SOCKET_DYNAMIC_MAP	0x01
+
+#define HAL_SOCK_RFCOMM		0x01
+#define HAL_SOCK_SCO		0x02
+#define HAL_SOCK_L2CAP		0x03
+
+#define HAL_SOCK_FLAG_ENCRYPT	0x01
+#define HAL_SOCK_FLAG_AUTH	0x02
+
+#define HAL_OP_SOCKET_LISTEN		0x01
+struct hal_cmd_socket_listen {
+	uint8_t type;
+	uint8_t name[256];
+	uint8_t uuid[16];
+	int32_t channel;
+	uint8_t flags;
+} __attribute__((packed));
+
+#define HAL_OP_SOCKET_CONNECT		0x02
+struct hal_cmd_socket_connect {
+	uint8_t bdaddr[6];
+	uint8_t type;
+	uint8_t uuid[16];
+	int32_t channel;
+	uint8_t flags;
+} __attribute__((packed));
+
+/* Bluetooth HID Host HAL API */
+
+#define HAL_OP_HIDHOST_CONNECT		0x01
+struct hal_cmd_hidhost_connect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_DISCONNECT		0x02
+struct hal_cmd_hidhost_disconnect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_VIRTUAL_UNPLUG		0x03
+struct hal_cmd_hidhost_virtual_unplug {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_SET_INFO		0x04
+struct hal_cmd_hidhost_set_info {
+	uint8_t  bdaddr[6];
+	uint8_t  attr;
+	uint8_t  subclass;
+	uint8_t  app_id;
+	uint16_t vendor;
+	uint16_t product;
+	uint16_t country;
+	uint16_t descr_len;
+	uint8_t  descr[0];
+} __attribute__((packed));
+
+#define HAL_HIDHOST_REPORT_PROTOCOL		0x00
+#define HAL_HIDHOST_BOOT_PROTOCOL		0x01
+#define HAL_HIDHOST_UNSUPPORTED_PROTOCOL	0xff
+
+#define HAL_OP_HIDHOST_GET_PROTOCOL	0x05
+struct hal_cmd_hidhost_get_protocol {
+	uint8_t bdaddr[6];
+	uint8_t mode;
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_SET_PROTOCOL	0x06
+struct hal_cmd_hidhost_set_protocol {
+	uint8_t bdaddr[6];
+	uint8_t mode;
+} __attribute__((packed));
+
+#define HAL_HIDHOST_INPUT_REPORT		0x01
+#define HAL_HIDHOST_OUTPUT_REPORT		0x02
+#define HAL_HIDHOST_FEATURE_REPORT		0x03
+
+#define HAL_OP_HIDHOST_GET_REPORT		0x07
+struct hal_cmd_hidhost_get_report {
+	uint8_t  bdaddr[6];
+	uint8_t  type;
+	uint8_t  id;
+	uint16_t buf_size;
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_SET_REPORT		0x08
+struct hal_cmd_hidhost_set_report {
+	uint8_t  bdaddr[6];
+	uint8_t  type;
+	uint16_t len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_SEND_DATA		0x09
+struct hal_cmd_hidhost_send_data {
+	uint8_t  bdaddr[6];
+	uint16_t len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+/* a2dp source and sink HAL API */
+
+#define HAL_OP_A2DP_CONNECT	0x01
+struct hal_cmd_a2dp_connect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_A2DP_DISCONNECT	0x02
+struct hal_cmd_a2dp_disconnect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+/* PAN HAL API */
+
+/* PAN Roles */
+#define HAL_PAN_ROLE_NONE	0x00
+#define HAL_PAN_ROLE_NAP	0x01
+#define HAL_PAN_ROLE_PANU	0x02
+
+/* PAN Control states */
+#define HAL_PAN_CTRL_ENABLED	0x00
+#define HAL_PAN_CTRL_DISABLED	0x01
+
+/* PAN Connection states */
+#define HAL_PAN_STATE_CONNECTED		0x00
+#define HAL_PAN_STATE_CONNECTING	0x01
+#define HAL_PAN_STATE_DISCONNECTED	0x02
+#define HAL_PAN_STATE_DISCONNECTING	0x03
+
+/* PAN status values */
+#define HAL_PAN_STATUS_FAIL		0x01
+#define HAL_PAN_STATUS_NOT_READY	0x02
+#define HAL_PAN_STATUS_NO_MEMORY	0x03
+#define HAL_PAN_STATUS_BUSY		0x04
+#define HAL_PAN_STATUS_DONE		0x05
+#define HAL_PAN_STATUS_UNSUPORTED	0x06
+#define HAL_PAN_STATUS_INVAL		0x07
+#define HAL_PAN_STATUS_UNHANDLED	0x08
+#define HAL_PAN_STATUS_AUTH_FAILED	0x09
+#define HAL_PAN_STATUS_DEVICE_DOWN	0x0A
+
+#define HAL_OP_PAN_ENABLE	0x01
+struct hal_cmd_pan_enable {
+	uint8_t local_role;
+} __attribute__((packed));
+
+#define HAL_OP_PAN_GET_ROLE	0x02
+struct hal_rsp_pan_get_role {
+	uint8_t local_role;
+} __attribute__((packed));
+
+#define HAL_OP_PAN_CONNECT	0x03
+struct hal_cmd_pan_connect {
+	uint8_t bdaddr[6];
+	uint8_t local_role;
+	uint8_t remote_role;
+} __attribute__((packed));
+
+#define HAL_OP_PAN_DISCONNECT	0x04
+struct hal_cmd_pan_disconnect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HEALTH_MDEP_ROLE_SOURCE	0x00
+#define HAL_HEALTH_MDEP_ROLE_SINK	0x01
+
+#define HAL_HEALTH_CHANNEL_TYPE_RELIABLE	0x00
+#define HAL_HEALTH_CHANNEL_TYPE_STREAMING	0x01
+#define HAL_HEALTH_CHANNEL_TYPE_ANY		0x02
+
+#define HAL_OP_HEALTH_REG_APP		0x01
+struct hal_cmd_health_reg_app {
+	uint8_t  num_of_mdep;
+	uint16_t app_name_off;
+	uint16_t provider_name_off;
+	uint16_t service_name_off;
+	uint16_t service_descr_off;
+	uint16_t len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+struct hal_rsp_health_reg_app {
+	uint16_t app_id;
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_MDEP		0x02
+struct hal_cmd_health_mdep {
+	uint16_t app_id;
+	uint8_t  role;
+	uint16_t data_type;
+	uint8_t  channel_type;
+	uint16_t descr_len;
+	uint8_t  descr[0];
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_UNREG_APP		0x03
+struct hal_cmd_health_unreg_app {
+	uint16_t app_id;
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_CONNECT_CHANNEL	0x04
+struct hal_cmd_health_connect_channel {
+	uint16_t app_id;
+	uint8_t  bdaddr[6];
+	uint8_t  mdep_index;
+} __attribute__((packed));
+
+struct hal_rsp_health_connect_channel {
+	uint16_t  channel_id;
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_DESTROY_CHANNEL	0x05
+struct hal_cmd_health_destroy_channel {
+	uint16_t channel_id;
+} __attribute__((packed));
+
+/* Handsfree HAL API */
+
+#define HAL_MODE_HANDSFREE_HSP_ONLY		HAL_MODE_DEFAULT
+#define HAL_MODE_HANDSFREE_HFP			0x01
+#define HAL_MODE_HANDSFREE_HFP_WBS		0x02
+
+#define HAL_OP_HANDSFREE_CONNECT		0x01
+struct hal_cmd_handsfree_connect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_DISCONNECT		0x02
+struct hal_cmd_handsfree_disconnect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_CONNECT_AUDIO		0x03
+struct hal_cmd_handsfree_connect_audio {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_DISCONNECT_AUDIO	0x04
+struct hal_cmd_handsfree_disconnect_audio {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_START_VR		0x05
+struct hal_cmd_handsfree_start_vr {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_STOP_VR		0x06
+struct hal_cmd_handsfree_stop_vr {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_VOLUME_TYPE_SPEAKER	0x00
+#define HAL_HANDSFREE_VOLUME_TYPE_MIC		0x01
+
+#define HAL_OP_HANDSFREE_VOLUME_CONTROL		0x07
+struct hal_cmd_handsfree_volume_control {
+	uint8_t type;
+	uint8_t volume;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_NETWORK_STATE_NOT_AVAILABLE	0x00
+#define HAL_HANDSFREE_NETWORK_STATE_AVAILABLE		0x01
+
+#define HAL_HANDSFREE_SERVICE_TYPE_HOME		0x00
+#define HAL_HANDSFREE_SERVICE_TYPE_ROAMING	0x01
+
+#define HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF	0x08
+struct hal_cmd_handsfree_device_status_notif {
+	uint8_t state;
+	uint8_t type;
+	uint8_t signal;
+	uint8_t battery;
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_COPS_RESPONSE		0x09
+struct hal_cmd_handsfree_cops_response {
+	uint16_t len;
+	uint8_t bdaddr[6];
+	uint8_t buf[0];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_CALL_STATE_ACTIVE		0x00
+#define HAL_HANDSFREE_CALL_STATE_HELD		0x01
+#define HAL_HANDSFREE_CALL_STATE_DIALING	0x02
+#define HAL_HANDSFREE_CALL_STATE_ALERTING	0x03
+#define HAL_HANDSFREE_CALL_STATE_INCOMING	0x04
+#define HAL_HANDSFREE_CALL_STATE_WAITING	0x05
+#define HAL_HANDSFREE_CALL_STATE_IDLE		0x06
+
+#define HAL_OP_HANDSFREE_CIND_RESPONSE		0x0A
+struct hal_cmd_handsfree_cind_response {
+	uint8_t svc;
+	uint8_t num_active;
+	uint8_t num_held;
+	uint8_t state;
+	uint8_t signal;
+	uint8_t roam;
+	uint8_t batt_chg;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE	0x0B
+struct hal_cmd_handsfree_formatted_at_response {
+	uint16_t len;
+	uint8_t bdaddr[6];
+	uint8_t buf[0];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_AT_RESPONSE_ERROR		0x00
+#define HAL_HANDSFREE_AT_RESPONSE_OK		0x01
+
+#define HAL_OP_HANDSFREE_AT_RESPONSE		0x0C
+struct hal_cmd_handsfree_at_response {
+	uint8_t response;
+	uint8_t error;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_CALL_DIRECTION_OUTGOING	0x00
+#define HAL_HANDSFREE_CALL_DIRECTION_INCOMING	0x01
+
+#define HAL_HANDSFREE_CALL_TYPE_VOICE		0x00
+#define HAL_HANDSFREE_CALL_TYPE_DATA		0x01
+#define HAL_HANDSFREE_CALL_TYPE_FAX		0x02
+
+#define HAL_HANDSFREE_CALL_MPTY_TYPE_SINGLE	0x00
+#define HAL_HANDSFREE_CALL_MPTY_TYPE_MULTI	0x01
+
+#define HAL_HANDSFREE_CALL_ADDRTYPE_UNKNOWN	0x81
+#define HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL	0x91
+
+#define HAL_OP_HANDSFREE_CLCC_RESPONSE		0x0D
+struct hal_cmd_handsfree_clcc_response {
+	uint8_t index;
+	uint8_t dir;
+	uint8_t state;
+	uint8_t mode;
+	uint8_t mpty;
+	uint8_t type;
+	uint8_t bdaddr[6];
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_PHONE_STATE_CHANGE	0x0E
+struct hal_cmd_handsfree_phone_state_change {
+	uint8_t num_active;
+	uint8_t num_held;
+	uint8_t state;
+	uint8_t type;
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_WBS_NONE			0x00
+#define HAL_HANDSFREE_WBS_NO			0x01
+#define HAL_HANDSFREE_WBS_YES			0x02
+
+#define HAL_OP_HANDSFREE_CONFIGURE_WBS		0x0F
+struct hal_cmd_handsfree_configure_wbs {
+	uint8_t bdaddr[6];
+	uint8_t config;
+} __attribute__((packed));
+
+/* AVRCP TARGET HAL API */
+
+#define HAL_AVRCP_PLAY_STATUS_STOPPED	0x00
+#define HAL_AVRCP_PLAY_STATUS_PLAYING	0x01
+#define HAL_AVRCP_PLAY_STATUS_PAUSED	0x02
+#define HAL_AVRCP_PLAY_STATUS_FWD_SEEK	0x03
+#define HAL_AVRCP_PLAY_STATUS_REV_SEEK	0x04
+#define HAL_AVRCP_PLAY_STATUS_ERROR	0xff
+
+#define HAL_OP_AVRCP_GET_PLAY_STATUS	0x01
+struct hal_cmd_avrcp_get_play_status {
+	uint8_t status;
+	uint32_t duration;
+	uint32_t position;
+} __attribute__((packed));
+
+#define HAL_AVRCP_PLAYER_ATTR_EQUALIZER	0x01
+#define HAL_AVRCP_PLAYER_ATTR_REPEAT	0x02
+#define HAL_AVRCP_PLAYER_ATTR_SHUFFLE	0x03
+#define HAL_AVRCP_PLAYER_ATTR_SCAN	0x04
+
+#define HAL_OP_AVRCP_LIST_PLAYER_ATTRS	0x02
+struct hal_cmd_avrcp_list_player_attrs {
+	uint8_t number;
+	uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_LIST_PLAYER_VALUES	0x03
+struct hal_cmd_avrcp_list_player_values {
+	uint8_t number;
+	uint8_t values[0];
+} __attribute__((packed));
+
+struct hal_avrcp_player_attr_value {
+	uint8_t attr;
+	uint8_t value;
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_GET_PLAYER_ATTRS	0x04
+struct hal_cmd_avrcp_get_player_attrs {
+	uint8_t number;
+	struct hal_avrcp_player_attr_value attrs[0];
+} __attribute__((packed));
+
+struct hal_avrcp_player_setting_text {
+	uint8_t id;
+	uint8_t len;
+	uint8_t text[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT	0x05
+struct hal_cmd_avrcp_get_player_attrs_text {
+	uint8_t number;
+	struct hal_avrcp_player_setting_text attrs[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT	0x06
+struct hal_cmd_avrcp_get_player_values_text {
+	uint8_t number;
+	struct hal_avrcp_player_setting_text values[0];
+} __attribute__((packed));
+
+#define HAL_AVRCP_MEDIA_ATTR_TITLE		0x01
+#define HAL_AVRCP_MEDIA_ATTR_ARTIST		0x02
+#define HAL_AVRCP_MEDIA_ATTR_ALBUM		0x03
+#define HAL_AVRCP_MEDIA_ATTR_TRACK_NUM		0x04
+#define HAL_AVRCP_MEDIA_ATTR_NUM_TRACKS		0x05
+#define HAL_AVRCP_MEDIA_ATTR_GENRE		0x06
+#define HAL_AVRCP_MEDIA_ATTR_DURATION		0x07
+
+#define HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT	0x07
+struct hal_cmd_avrcp_get_element_attrs_text {
+	uint8_t number;
+	struct hal_avrcp_player_setting_text values[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE	0x08
+struct hal_cmd_avrcp_set_player_attrs_value {
+	uint8_t status;
+} __attribute__((packed));
+
+#define HAL_AVRCP_EVENT_STATUS_CHANGED		0x01
+#define HAL_AVRCP_EVENT_TRACK_CHANGED		0x02
+#define HAL_AVRCP_EVENT_TRACK_REACHED_END	0x03
+#define HAL_AVRCP_EVENT_TRACK_REACHED_START	0x04
+#define HAL_AVRCP_EVENT_POSITION_CHANGED	0x05
+#define HAL_AVRCP_EVENT_SETTING_CHANGED		0x08
+
+#define HAL_AVRCP_EVENT_TYPE_INTERIM		0x00
+#define HAL_AVRCP_EVENT_TYPE_CHANGED		0x01
+
+#define HAL_OP_AVRCP_REGISTER_NOTIFICATION	0x09
+struct hal_cmd_avrcp_register_notification {
+	uint8_t event;
+	uint8_t type;
+	uint8_t len;
+	uint8_t data[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_SET_VOLUME			0x0a
+struct hal_cmd_avrcp_set_volume {
+	uint8_t value;
+} __attribute__((packed));
+
+/* AVRCP CTRL HAL API */
+
+#define HAL_OP_AVRCP_CTRL_SEND_PASSTHROUGH	0x01
+struct hal_cmd_avrcp_ctrl_send_passthrough {
+	uint8_t bdaddr[6];
+	uint8_t key_code;
+	uint8_t key_state;
+} __attribute__((packed));
+
+/* GATT HAL API */
+
+#define HAL_OP_GATT_CLIENT_REGISTER		0x01
+struct hal_cmd_gatt_client_register {
+	uint8_t uuid[16];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_UNREGISTER		0x02
+struct hal_cmd_gatt_client_unregister {
+	int32_t client_if;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN			0x03
+struct hal_cmd_gatt_client_scan {
+	int32_t client_if;
+	uint8_t start;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_CONNECT		0x04
+struct hal_cmd_gatt_client_connect {
+	int32_t client_if;
+	uint8_t bdaddr[6];
+	uint8_t is_direct;
+	int32_t transport;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_DISCONNECT		0x05
+struct hal_cmd_gatt_client_disconnect {
+	int32_t client_if;
+	uint8_t bdaddr[6];
+	int32_t conn_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_LISTEN		0x06
+struct hal_cmd_gatt_client_listen {
+	int32_t client_if;
+	uint8_t start;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_REFRESH		0x07
+struct hal_cmd_gatt_client_refresh {
+	int32_t client_if;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SEARCH_SERVICE	0x08
+struct hal_cmd_gatt_client_search_service {
+	int32_t conn_id;
+	uint8_t filtered;
+	uint8_t filter_uuid[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE	0x09
+struct hal_gatt_srvc_id {
+	uint8_t uuid[16];
+	uint8_t inst_id;
+	uint8_t is_primary;
+} __attribute__((packed));
+
+struct hal_cmd_gatt_client_get_included_service {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+	uint8_t continuation;
+	struct hal_gatt_srvc_id incl_srvc_id[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC	0x0a
+struct hal_gatt_gatt_id {
+	uint8_t uuid[16];
+	uint8_t inst_id;
+} __attribute__((packed));
+
+struct hal_cmd_gatt_client_get_characteristic {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+	uint8_t continuation;
+	struct hal_gatt_gatt_id char_id[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_DESCRIPTOR	0x0b
+struct hal_cmd_gatt_client_get_descriptor {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	uint8_t continuation;
+	struct hal_gatt_gatt_id descr_id[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC	0x0c
+struct hal_cmd_gatt_client_read_characteristic {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	int32_t auth_req;
+} __attribute__((packed));
+
+#define GATT_WRITE_TYPE_NO_RESPONSE	0x01
+#define GATT_WRITE_TYPE_DEFAULT		0x02
+#define GATT_WRITE_TYPE_PREPARE		0x03
+#define GATT_WRITE_TYPE_SIGNED		0x04
+
+#define HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC	0x0d
+struct hal_cmd_gatt_client_write_characteristic {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	int32_t write_type;
+	int32_t len;
+	int32_t auth_req;
+	uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_READ_DESCRIPTOR	0x0e
+struct hal_cmd_gatt_client_read_descriptor {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	struct hal_gatt_gatt_id descr_id;
+	int32_t auth_req;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR	0x0f
+struct hal_cmd_gatt_client_write_descriptor {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	struct hal_gatt_gatt_id descr_id;
+	int32_t write_type;
+	int32_t len;
+	int32_t auth_req;
+	uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_EXECUTE_WRITE	0x10
+struct hal_cmd_gatt_client_execute_write {
+	int32_t conn_id;
+	int32_t execute;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION	0x11
+struct hal_cmd_gatt_client_register_for_notification {
+	int32_t client_if;
+	uint8_t bdaddr[6];
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION	0x12
+struct hal_cmd_gatt_client_deregister_for_notification {
+	int32_t client_if;
+	uint8_t bdaddr[6];
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI	0x13
+struct hal_cmd_gatt_client_read_remote_rssi {
+	int32_t client_if;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE	0x14
+struct hal_cmd_gatt_client_get_device_type {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+struct hal_rsp_gatt_client_get_device_type {
+	uint8_t type;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SET_ADV_DATA		0x015
+struct hal_cmd_gatt_client_set_adv_data {
+	int32_t  server_if;
+	uint8_t  set_scan_rsp;
+	uint8_t  include_name;
+	uint8_t  include_txpower;
+	int32_t  min_interval;
+	int32_t  max_interval;
+	int32_t  appearance;
+	uint16_t manufacturer_len;
+	uint16_t service_data_len;
+	uint16_t service_uuid_len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+#define GATT_CLIENT_TEST_CMD_ENABLE		0x01
+#define GATT_CLIENT_TEST_CMD_CONNECT		0x02
+#define GATT_CLIENT_TEST_CMD_DISCONNECT		0x03
+#define GATT_CLIENT_TEST_CMD_DISCOVER		0x04
+#define GATT_CLIENT_TEST_CMD_READ		0xe0
+#define GATT_CLIENT_TEST_CMD_WRITE		0xe1
+#define GATT_CLIENT_TEST_CMD_INCREASE_SECURITY	0xe2
+#define GATT_CLIENT_TEST_CMD_PAIRING_CONFIG	0xf0
+
+#define HAL_OP_GATT_CLIENT_TEST_COMMAND		0x16
+struct hal_cmd_gatt_client_test_command {
+	int32_t command;
+	uint8_t  bda1[6];
+	uint8_t  uuid1[16];
+	uint16_t u1;
+	uint16_t u2;
+	uint16_t u3;
+	uint16_t u4;
+	uint16_t u5;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_REGISTER		0x17
+struct hal_cmd_gatt_server_register {
+	uint8_t uuid[16];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_UNREGISTER		0x18
+struct hal_cmd_gatt_server_unregister {
+	int32_t server_if;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_CONNECT		0x19
+struct hal_cmd_gatt_server_connect {
+	int32_t server_if;
+	uint8_t bdaddr[6];
+	uint8_t is_direct;
+	int32_t transport;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_DISCONNECT		0x1a
+struct hal_cmd_gatt_server_disconnect {
+	int32_t server_if;
+	uint8_t bdaddr[6];
+	int32_t conn_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_SERVICE		0x1b
+struct hal_cmd_gatt_server_add_service {
+	int32_t server_if;
+	struct hal_gatt_srvc_id srvc_id;
+	int32_t num_handles;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_INC_SERVICE	0x1c
+struct hal_cmd_gatt_server_add_inc_service {
+	int32_t server_if;
+	int32_t service_handle;
+	int32_t included_handle;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC	0x1d
+struct hal_cmd_gatt_server_add_characteristic {
+	int32_t server_if;
+	int32_t service_handle;
+	uint8_t uuid[16];
+	int32_t properties;
+	int32_t permissions;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_DESCRIPTOR	0x1e
+struct hal_cmd_gatt_server_add_descriptor {
+	int32_t server_if;
+	int32_t service_handle;
+	uint8_t uuid[16];
+	int32_t permissions;
+} __attribute__((packed));
+
+#define GATT_SERVER_TRANSPORT_LE_BIT		0x01
+#define GATT_SERVER_TRANSPORT_BREDR_BIT		0x02
+
+#define HAL_OP_GATT_SERVER_START_SERVICE	0x1f
+struct hal_cmd_gatt_server_start_service {
+	int32_t server_if;
+	int32_t service_handle;
+	int32_t transport;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_STOP_SERVICE		0x20
+struct hal_cmd_gatt_server_stop_service {
+	int32_t server_if;
+	int32_t service_handle;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_DELETE_SERVICE	0x21
+struct hal_cmd_gatt_server_delete_service {
+	int32_t server_if;
+	int32_t service_handle;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_SEND_INDICATION	0x22
+struct hal_cmd_gatt_server_send_indication {
+	int32_t server_if;
+	int32_t attribute_handle;
+	int32_t conn_id;
+	int32_t len;
+	int32_t confirm;
+	uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_SEND_RESPONSE	0x23
+struct hal_cmd_gatt_server_send_response {
+	int32_t conn_id;
+	int32_t trans_id;
+	uint16_t handle;
+	uint16_t offset;
+	uint8_t auth_req;
+	int32_t status;
+	uint16_t len;
+	uint8_t data[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP	0x024
+struct hal_cmd_gatt_client_scan_filter_setup {
+	int32_t client_if;
+	int32_t action;
+	int32_t filter_index;
+	int32_t features;
+	int32_t list_type;
+	int32_t filter_type;
+	int32_t rssi_hi;
+	int32_t rssi_lo;
+	int32_t delivery_mode;
+	int32_t found_timeout;
+	int32_t lost_timeout;
+	int32_t found_timeout_cnt;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE	0x025
+struct hal_cmd_gatt_client_scan_filter_add_remove {
+	int32_t client_if;
+	int32_t action;
+	int32_t filter_type;
+	int32_t filter_index;
+	int32_t company_id;
+	int32_t company_id_mask;
+	uint8_t uuid[16];
+	uint8_t uuid_mask[16];
+	uint8_t address[6];
+	uint8_t address_type;
+	int32_t data_len;
+	int32_t mask_len;
+	uint8_t data_mask[0]; /* common buffer for data and mask */
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR		0x26
+struct hal_cmd_gatt_client_scan_filter_clear {
+	int32_t client_if;
+	int32_t index;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE		0x27
+struct hal_cmd_gatt_client_scan_filter_enable {
+	int32_t client_if;
+	uint8_t enable;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_CONFIGURE_MTU		0x28
+struct hal_cmd_gatt_client_configure_mtu {
+	int32_t conn_id;
+	int32_t mtu;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE		0x29
+struct hal_cmd_gatt_client_conn_param_update {
+	uint8_t address[6];
+	int32_t min_interval;
+	int32_t max_interval;
+	int32_t latency;
+	int32_t timeout;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SET_SCAN_PARAM		0x2a
+struct hal_cmd_gatt_client_set_scan_param {
+	int32_t interval;
+	int32_t window;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV		0x2b
+struct hal_cmd_gatt_client_setup_multi_adv {
+	int32_t client_if;
+	int32_t min_interval;
+	int32_t max_interval;
+	int32_t type;
+	int32_t channel_map;
+	int32_t tx_power;
+	int32_t timeout;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV		0x2c
+struct hal_cmd_gatt_client_update_multi_adv {
+	int32_t client_if;
+	int32_t min_interval;
+	int32_t max_interval;
+	int32_t type;
+	int32_t channel_map;
+	int32_t tx_power;
+	int32_t timeout;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST		0x2d
+struct hal_cmd_gatt_client_setup_multi_adv_inst {
+	int32_t client_if;
+	uint8_t set_scan_rsp;
+	uint8_t include_name;
+	uint8_t include_tx_power;
+	int32_t appearance;
+	int32_t manufacturer_data_len;
+	int32_t service_data_len;
+	int32_t service_uuid_len;
+	uint8_t data_service_uuid[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST	0x2e
+struct hal_cmd_gatt_client_disable_multi_adv_inst {
+	int32_t client_if;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN		0x2f
+struct hal_cmd_gatt_client_configure_batchscan {
+	int32_t client_if;
+	int32_t full_max;
+	int32_t trunc_max;
+	int32_t notify_threshold;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN		0x30
+struct hal_cmd_gatt_client_enable_batchscan {
+	int32_t client_if;
+	int32_t mode;
+	int32_t interval;
+	int32_t window;
+	int32_t address_type;
+	int32_t discard_rule;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN		0x31
+struct hal_cmd_gatt_client_disable_batchscan {
+	int32_t client_if;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS	0x32
+struct hal_cmd_gatt_client_read_batchscan_reports {
+	int32_t client_if;
+	int32_t scan_mode;
+} __attribute__((packed));
+
+/* Handsfree client HAL API */
+
+#define HAL_OP_HF_CLIENT_CONNECT		0x01
+struct hal_cmd_hf_client_connect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_DISCONNECT		0x02
+struct hal_cmd_hf_client_disconnect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_CONNECT_AUDIO		0x03
+struct hal_cmd_hf_client_connect_audio {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_DISCONNECT_AUDIO	0x04
+struct hal_cmd_hf_client_disconnect_audio {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_START_VR		0x05
+#define HAL_OP_HF_CLIENT_STOP_VR		0x06
+
+#define HF_CLIENT_VOLUME_TYPE_SPEAKER	0x00
+#define HF_CLIENT_VOLUME_TYPE_MIC	0x01
+
+#define HAL_OP_HF_CLIENT_VOLUME_CONTROL		0x07
+struct hal_cmd_hf_client_volume_control {
+	uint8_t type;
+	uint8_t volume;
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_DIAL			0x08
+struct hal_cmd_hf_client_dial {
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_DIAL_MEMORY		0x09
+struct hal_cmd_hf_client_dial_memory {
+	int32_t location;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_ACTION_CHLD_0		0x00
+#define HAL_HF_CLIENT_ACTION_CHLD_1		0x01
+#define HAL_HF_CLIENT_ACTION_CHLD_2		0x02
+#define HAL_HF_CLIENT_ACTION_CHLD_3		0x03
+#define HAL_HF_CLIENT_ACTION_CHLD_4		0x04
+#define HAL_HF_CLIENT_ACTION_CHLD_1x		0x05
+#define HAL_HF_CLIENT_ACTION_CHLD_2x		0x06
+#define HAL_HF_CLIENT_ACTION_ATA		0x07
+#define HAL_HF_CLIENT_ACTION_CHUP		0x08
+#define HAL_HF_CLIENT_ACTION_BRTH_0		0x09
+#define HAL_HF_CLIENT_ACTION_BRTH_1		0x0a
+#define HAL_HF_CLIENT_ACTION_BRTH_2		0x0b
+
+#define HAL_OP_HF_CLIENT_CALL_ACTION		0x0a
+struct hal_cmd_hf_client_call_action {
+	uint8_t action;
+	int32_t index;
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS	0x0b
+#define HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME	0x0c
+#define HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO	0x0d
+
+#define HAL_OP_HF_CLIENT_SEND_DTMF		0x0e
+struct hal_cmd_hf_client_send_dtmf {
+	uint8_t tone;
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM	0x0f
+
+/* MAP CLIENT HAL API */
+
+#define HAL_OP_MAP_CLIENT_GET_INSTANCES	0x01
+struct hal_cmd_map_client_get_instances {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+/* Notifications and confirmations */
+
+#define HAL_POWER_OFF			0x00
+#define HAL_POWER_ON			0x01
+
+#define HAL_EV_ADAPTER_STATE_CHANGED	0x81
+struct hal_ev_adapter_state_changed {
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_ADAPTER_PROPS_CHANGED	0x82
+struct hal_property {
+	uint8_t  type;
+	uint16_t len;
+	uint8_t  val[0];
+} __attribute__((packed));
+struct hal_ev_adapter_props_changed {
+	uint8_t              status;
+	uint8_t              num_props;
+	struct  hal_property props[0];
+} __attribute__((packed));
+
+#define HAL_EV_REMOTE_DEVICE_PROPS	0x83
+struct hal_ev_remote_device_props {
+	uint8_t             status;
+	uint8_t             bdaddr[6];
+	uint8_t             num_props;
+	struct hal_property props[0];
+} __attribute__((packed));
+
+#define HAL_EV_DEVICE_FOUND		0x84
+struct hal_ev_device_found {
+	uint8_t             num_props;
+	struct hal_property props[0];
+} __attribute__((packed));
+
+#define HAL_DISCOVERY_STATE_STOPPED	0x00
+#define HAL_DISCOVERY_STATE_STARTED	0x01
+
+#define HAL_EV_DISCOVERY_STATE_CHANGED	0x85
+struct hal_ev_discovery_state_changed {
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_PIN_REQUEST		0x86
+struct hal_ev_pin_request {
+	uint8_t  bdaddr[6];
+	uint8_t  name[249];
+	uint32_t class_of_dev;
+} __attribute__((packed));
+
+#define HAL_EV_SSP_REQUEST		0x87
+struct hal_ev_ssp_request {
+	uint8_t  bdaddr[6];
+	uint8_t  name[249];
+	uint32_t class_of_dev;
+	uint8_t  pairing_variant;
+	uint32_t passkey;
+} __attribute__((packed));
+
+#define HAL_BOND_STATE_NONE 0
+#define HAL_BOND_STATE_BONDING 1
+#define HAL_BOND_STATE_BONDED 2
+
+#define HAL_EV_BOND_STATE_CHANGED	0x88
+struct hal_ev_bond_state_changed {
+	uint8_t status;
+	uint8_t bdaddr[6];
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_ACL_STATE_CONNECTED		0x00
+#define HAL_ACL_STATE_DISCONNECTED	0x01
+
+#define HAL_EV_ACL_STATE_CHANGED	0x89
+struct hal_ev_acl_state_changed {
+	uint8_t status;
+	uint8_t bdaddr[6];
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_DUT_MODE_RECEIVE		0x8a
+struct hal_ev_dut_mode_receive {
+	uint16_t opcode;
+	uint8_t  len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+#define HAL_EV_LE_TEST_MODE		0x8b
+struct hal_ev_le_test_mode {
+	uint8_t  status;
+	uint16_t num_packets;
+} __attribute__((packed));
+
+#define HAL_EV_ENERGY_INFO		0x8c
+struct hal_ev_energy_info {
+	uint8_t status;
+	uint8_t ctrl_state;
+	uint64_t tx_time;
+	uint64_t rx_time;
+	uint64_t idle_time;
+	uint64_t energy_used;
+} __attribute__((packed));
+
+#define HAL_HIDHOST_STATE_CONNECTED		0x00
+#define HAL_HIDHOST_STATE_CONNECTING	0x01
+#define HAL_HIDHOST_STATE_DISCONNECTED	0x02
+#define HAL_HIDHOST_STATE_DISCONNECTING	0x03
+#define HAL_HIDHOST_STATE_NO_HID		0x07
+#define HAL_HIDHOST_STATE_FAILED		0x08
+#define HAL_HIDHOST_STATE_UNKNOWN		0x09
+
+#define HAL_EV_HIDHOST_CONN_STATE		0x81
+struct hal_ev_hidhost_conn_state {
+	uint8_t bdaddr[6];
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_HIDHOST_STATUS_OK			0x00
+
+#define HAL_HIDHOST_HS_NOT_READY		0x01
+#define HAL_HIDHOST_HS_INVALID_RAPORT_ID	0x02
+#define HAL_HIDHOST_HS_TRANS_NOT_SUPPORTED	0x03
+#define HAL_HIDHOST_HS_INVALID_PARAM		0x04
+#define HAL_HIDHOST_HS_ERROR			0x05
+
+#define HAL_HIDHOST_GENERAL_ERROR		0x06
+#define HAL_HIDHOST_SDP_ERROR			0x07
+#define HAL_HIDHOST_PROTOCOL_ERROR		0x08
+#define HAL_HIDHOST_DB_ERROR			0x09
+#define HAL_HIDHOST_TOD_UNSUPPORTED_ERROR	0x0a
+#define HAL_HIDHOST_NO_RESOURCES_ERROR		0x0b
+#define HAL_HIDHOST_AUTH_FAILED_ERROR		0x0c
+#define HAL_HIDHOST_HDL_ERROR			0x0d
+
+#define HAL_EV_HIDHOST_INFO			0x82
+struct hal_ev_hidhost_info {
+	uint8_t  bdaddr[6];
+	uint8_t  attr;
+	uint8_t  subclass;
+	uint8_t  app_id;
+	uint16_t vendor;
+	uint16_t product;
+	uint16_t version;
+	uint8_t  country;
+	uint16_t descr_len;
+	uint8_t  descr[884];
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_PROTO_MODE		0x83
+struct hal_ev_hidhost_proto_mode {
+	uint8_t bdaddr[6];
+	uint8_t status;
+	uint8_t mode;
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_IDLE_TIME		0x84
+struct hal_ev_hidhost_idle_time {
+	uint8_t bdaddr[6];
+	uint8_t status;
+	uint32_t idle_rate;
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_GET_REPORT		0x85
+struct hal_ev_hidhost_get_report {
+	uint8_t  bdaddr[6];
+	uint8_t  status;
+	uint16_t len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_VIRTUAL_UNPLUG		0x86
+struct hal_ev_hidhost_virtual_unplug {
+	uint8_t  bdaddr[6];
+	uint8_t  status;
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_HANDSHAKE		0x87
+struct hal_ev_hidhost_handshake {
+	uint8_t  bdaddr[6];
+	uint8_t  status;
+} __attribute__((packed));
+
+#define HAL_EV_PAN_CTRL_STATE			0x81
+struct hal_ev_pan_ctrl_state {
+	uint8_t  state;
+	uint8_t  status;
+	uint8_t  local_role;
+	uint8_t  name[17];
+} __attribute__((packed));
+
+#define HAL_EV_PAN_CONN_STATE			0x82
+struct hal_ev_pan_conn_state {
+	uint8_t  state;
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+	uint8_t  local_role;
+	uint8_t  remote_role;
+} __attribute__((packed));
+
+#define HAL_HEALTH_APP_REG_SUCCESS		0x00
+#define HAL_HEALTH_APP_REG_FAILED		0x01
+#define HAL_HEALTH_APP_DEREG_SUCCESS		0x02
+#define HAL_HEALTH_APP_DEREG_FAILED		0x03
+
+#define HAL_HEALTH_CHANNEL_CONNECTING		0x00
+#define HAL_HEALTH_CHANNEL_CONNECTED		0x01
+#define HAL_HEALTH_CHANNEL_DISCONNECTING	0x02
+#define HAL_HEALTH_CHANNEL_DISCONNECTED		0x03
+#define HAL_HEALTH_CHANNEL_DESTROYED		0x04
+
+#define HAL_EV_HEALTH_APP_REG_STATE		0x81
+struct hal_ev_health_app_reg_state {
+	uint16_t id;
+	uint8_t  state;
+} __attribute__((packed));
+
+#define HAL_EV_HEALTH_CHANNEL_STATE		0x82
+struct hal_ev_health_channel_state {
+	uint16_t app_id;
+	uint8_t  bdaddr[6];
+	uint8_t  mdep_index;
+	uint16_t channel_id;
+	uint8_t  channel_state;
+} __attribute__((packed));
+
+#define HAL_A2DP_STATE_DISCONNECTED		0x00
+#define HAL_A2DP_STATE_CONNECTING		0x01
+#define HAL_A2DP_STATE_CONNECTED		0x02
+#define HAL_A2DP_STATE_DISCONNECTING		0x03
+
+#define HAL_EV_A2DP_CONN_STATE			0x81
+struct hal_ev_a2dp_conn_state {
+	uint8_t state;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_AUDIO_SUSPEND			0x00
+#define HAL_AUDIO_STOPPED			0x01
+#define HAL_AUDIO_STARTED			0x02
+
+#define HAL_EV_A2DP_AUDIO_STATE			0x82
+struct hal_ev_a2dp_audio_state {
+	uint8_t state;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_A2DP_AUDIO_CONFIG		0x83
+struct hal_ev_a2dp_audio_config {
+	uint8_t  bdaddr[6];
+	uint32_t sample_rate;
+	uint8_t  channel_count;
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED	0x00
+#define HAL_EV_HANDSFREE_CONN_STATE_CONNECTING		0x01
+#define HAL_EV_HANDSFREE_CONN_STATE_CONNECTED		0x02
+#define HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED	0x03
+#define HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING	0x04
+
+#define HAL_EV_HANDSFREE_CONN_STATE		0x81
+struct hal_ev_handsfree_conn_state {
+	uint8_t state;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED	0x00
+#define HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING		0x01
+#define HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED		0x02
+#define HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING	0x03
+
+#define HAL_EV_HANDSFREE_AUDIO_STATE		0x82
+struct hal_ev_handsfree_audio_state {
+	uint8_t state;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_VR_STOPPED	0x00
+#define HAL_HANDSFREE_VR_STARTED	0x01
+
+#define HAL_EV_HANDSFREE_VR		0x83
+struct hal_ev_handsfree_vr_state {
+	uint8_t state;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_ANSWER		0x84
+struct hal_ev_handsfree_answer {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_HANGUP		0x85
+struct hal_ev_handsfree_hangup {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_VOLUME		0x86
+struct hal_ev_handsfree_volume {
+	uint8_t type;
+	uint8_t volume;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_DIAL		0x87
+struct hal_ev_handsfree_dial {
+	uint8_t bdaddr[6];
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_DTMF		0x88
+struct hal_ev_handsfree_dtmf {
+	uint8_t tone;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_NREC_STOP		0x00
+#define HAL_HANDSFREE_NREC_START	0x01
+
+#define HAL_EV_HANDSFREE_NREC		0x89
+struct hal_ev_handsfree_nrec {
+	uint8_t nrec;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_CHLD_TYPE_RELEASEHELD			0x00
+#define HAL_HANDSFREE_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD	0x01
+#define HAL_HANDSFREE_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD		0x02
+#define HAL_HANDSFREE_CHLD_TYPE_ADDHELDTOCONF			0x03
+
+#define HAL_EV_HANDSFREE_CHLD		0x8A
+struct hal_ev_handsfree_chld {
+	uint8_t chld;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_CNUM		0x8B
+struct hal_ev_handsfree_cnum {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_CIND		0x8C
+struct hal_ev_handsfree_cind {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_COPS		0x8D
+struct hal_ev_handsfree_cops {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_CLCC		0x8E
+struct hal_ev_handsfree_clcc {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_UNKNOWN_AT	0x8F
+struct hal_ev_handsfree_unknown_at {
+	uint8_t bdaddr[6];
+	uint16_t len;
+	uint8_t buf[0];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_HSP_KEY_PRESS	0x90
+struct hal_ev_handsfree_hsp_key_press {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_WBS		0x91
+struct hal_ev_handsfree_wbs {
+	uint8_t wbs;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_AVRCP_FEATURE_NONE			0x00
+#define HAL_AVRCP_FEATURE_METADATA		0x01
+#define HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME	0x02
+#define HAL_AVRCP_FEATURE_BROWSE		0x04
+
+#define HAL_EV_AVRCP_REMOTE_FEATURES		0x81
+struct hal_ev_avrcp_remote_features {
+	uint8_t bdaddr[6];
+	uint8_t features;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAY_STATUS		0x82
+#define HAL_EV_AVRCP_LIST_PLAYER_ATTRS		0x83
+
+#define HAL_EV_AVRCP_LIST_PLAYER_VALUES		0x84
+struct hal_ev_avrcp_list_player_values {
+	uint8_t attr;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAYER_VALUES		0x85
+struct hal_ev_avrcp_get_player_values {
+	uint8_t number;
+	uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAYER_ATTRS_TEXT	0x86
+struct hal_ev_avrcp_get_player_attrs_text {
+	uint8_t number;
+	uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAYER_VALUES_TEXT	0x87
+struct hal_ev_avrcp_get_player_values_text {
+	uint8_t attr;
+	uint8_t number;
+	uint8_t values[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_SET_PLAYER_VALUES		0x88
+struct hal_ev_avrcp_set_player_values {
+	uint8_t number;
+	struct hal_avrcp_player_attr_value attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_ELEMENT_ATTRS		0x89
+struct hal_ev_avrcp_get_element_attrs {
+	uint8_t number;
+	uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_REGISTER_NOTIFICATION	0x8a
+struct hal_ev_avrcp_register_notification {
+	uint8_t event;
+	uint32_t param;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_VOLUME_CHANGED		0x8b
+struct hal_ev_avrcp_volume_changed {
+	uint8_t volume;
+	uint8_t type;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_PASSTHROUGH_CMD		0x8c
+struct hal_ev_avrcp_passthrough_cmd {
+	uint8_t id;
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_CTRL_CONN_STATE		0x81
+struct hal_ev_avrcp_ctrl_conn_state {
+	uint8_t state;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_CTRL_PASSTHROUGH_RSP	0x82
+struct hal_ev_avrcp_ctrl_passthrough_rsp {
+	uint8_t id;
+	uint8_t key_state;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_REGISTER_CLIENT	0x81
+struct hal_ev_gatt_client_register_client {
+	int32_t status;
+	int32_t client_if;
+	uint8_t app_uuid[16];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_SCAN_RESULT	0x82
+struct hal_ev_gatt_client_scan_result {
+	uint8_t  bda[6];
+	int32_t  rssi;
+	uint16_t len;
+	uint8_t  adv_data[0];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_CONNECT	0x83
+struct hal_ev_gatt_client_connect {
+	int32_t conn_id;
+	int32_t status;
+	int32_t client_if;
+	uint8_t bda[6];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_DISCONNECT	0x84
+struct hal_ev_gatt_client_disconnect {
+	int32_t conn_id;
+	int32_t status;
+	int32_t client_if;
+	uint8_t bda[6];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_SEARCH_COMPLETE	0x85
+struct hal_ev_gatt_client_search_complete {
+	int32_t conn_id;
+	int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_SEARCH_RESULT	0x86
+struct hal_ev_gatt_client_search_result {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC	0x87
+struct hal_ev_gatt_client_get_characteristic {
+	int32_t conn_id;
+	int32_t status;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	int32_t char_prop;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_GET_DESCRIPTOR	0x88
+struct hal_ev_gatt_client_get_descriptor {
+	int32_t conn_id;
+	int32_t status;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	struct hal_gatt_gatt_id descr_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_GET_INC_SERVICE	0X89
+struct hal_ev_gatt_client_get_inc_service {
+	int32_t conn_id;
+	int32_t status;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_srvc_id incl_srvc_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF	0x8a
+struct hal_ev_gatt_client_reg_for_notif {
+	int32_t conn_id;
+	int32_t registered;
+	int32_t status;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_NOTIFY		0x8b
+struct hal_ev_gatt_client_notify {
+	int32_t conn_id;
+	uint8_t bda[6];
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	uint8_t  is_notify;
+	uint16_t len;
+	uint8_t  value[0];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC	0x8c
+struct hal_gatt_read_params {
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	struct hal_gatt_gatt_id descr_id;
+	uint8_t  status;
+	uint16_t value_type;
+	uint16_t len;
+	uint8_t  value[0];
+} __attribute__((packed));
+
+struct hal_ev_gatt_client_read_characteristic {
+	int32_t conn_id;
+	int32_t status;
+	struct hal_gatt_read_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC	0x8d
+struct hal_gatt_write_params {
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	struct hal_gatt_gatt_id descr_id;
+	uint8_t status;
+} __attribute__((packed));
+
+struct hal_ev_gatt_client_write_characteristic {
+	int32_t conn_id;
+	int32_t status;
+	struct hal_gatt_write_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_READ_DESCRIPTOR	0x8e
+struct hal_ev_gatt_client_read_descriptor {
+	int32_t conn_id;
+	int32_t status;
+	struct hal_gatt_read_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR	0x8f
+struct hal_ev_gatt_client_write_descriptor {
+	int32_t conn_id;
+	int32_t status;
+	struct hal_gatt_write_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_EXEC_WRITE		0x90
+struct hal_ev_gatt_client_exec_write {
+	int32_t conn_id;
+	int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI	0x91
+struct hal_ev_gatt_client_read_remote_rssi {
+	int32_t client_if;
+	uint8_t address[6];
+	int32_t rssi;
+	int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_LISTEN		0x92
+struct hal_ev_gatt_client_listen {
+	int32_t status;
+	int32_t server_if;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REGISTER		0x93
+struct hal_ev_gatt_server_register {
+	int32_t status;
+	int32_t server_if;
+	uint8_t uuid[16];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_CONNECTION		0x94
+struct hal_ev_gatt_server_connection {
+	int32_t conn_id;
+	int32_t server_if;
+	int32_t connected;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_ADDED	0x95
+struct hal_ev_gatt_server_service_added {
+	int32_t status;
+	int32_t server_if;
+	struct hal_gatt_srvc_id srvc_id;
+	int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_INC_SRVC_ADDED	0x96
+struct hal_ev_gatt_server_inc_srvc_added {
+	int32_t status;
+	int32_t server_if;
+	int32_t srvc_handle;
+	int32_t incl_srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_CHAR_ADDED		0x97
+struct hal_ev_gatt_server_characteristic_added {
+	int32_t status;
+	int32_t server_if;
+	uint8_t uuid[16];
+	int32_t srvc_handle;
+	int32_t char_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED	0x98
+struct hal_ev_gatt_server_descriptor_added {
+	int32_t status;
+	int32_t server_if;
+	uint8_t uuid[16];
+	int32_t srvc_handle;
+	int32_t descr_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_STARTED	0x99
+struct hal_ev_gatt_server_service_started {
+	int32_t status;
+	int32_t server_if;
+	int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_STOPPED	0x9a
+struct hal_ev_gatt_server_service_stopped {
+	int32_t status;
+	int32_t server_if;
+	int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_DELETED	0x9b
+struct hal_ev_gatt_server_service_deleted {
+	int32_t status;
+	int32_t server_if;
+	int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REQUEST_READ		0x9c
+struct hal_ev_gatt_server_request_read {
+	int32_t conn_id;
+	int32_t trans_id;
+	uint8_t bdaddr[6];
+	int32_t attr_handle;
+	int32_t offset;
+	uint8_t is_long;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REQUEST_WRITE	0x9d
+struct hal_ev_gatt_server_request_write {
+	int32_t conn_id;
+	int32_t trans_id;
+	uint8_t bdaddr[6];
+	int32_t attr_handle;
+	int32_t offset;
+	int32_t length;
+	uint8_t need_rsp;
+	uint8_t is_prep;
+	uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE	0x9e
+struct hal_ev_gatt_server_request_exec_write {
+	int32_t conn_id;
+	int32_t trans_id;
+	uint8_t bdaddr[6];
+	int32_t exec_write;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_RSP_CONFIRMATION	0x9f
+struct hal_ev_gatt_server_rsp_confirmation {
+	int32_t status;
+	int32_t handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_CONFIGURE_MTU	0xa0
+struct hal_ev_gatt_client_configure_mtu {
+	int32_t conn_id;
+	int32_t status;
+	int32_t mtu;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_FILTER_CONFIG	0xa1
+struct hal_ev_gatt_client_filter_config {
+	int32_t action;
+	int32_t client_if;
+	int32_t status;
+	int32_t type;
+	int32_t space;
+}  __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_FILTER_PARAMS	0xa2
+struct hal_ev_gatt_client_filter_params {
+	int32_t action;
+	int32_t client_if;
+	int32_t status;
+	int32_t space;
+}  __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_FILTER_STATUS	0xa3
+struct hal_ev_gatt_client_filter_status {
+	int32_t enable;
+	int32_t client_if;
+	int32_t status;
+}  __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_MULTI_ADV_ENABLE	0xa4
+struct hal_ev_gatt_client_multi_adv_enable {
+	int32_t client_if;
+	int32_t status;
+} __attribute__((packed));
+
+
+#define HAL_EV_GATT_CLIENT_MULTI_ADV_UPDATE	0xa5
+struct hal_ev_gatt_client_multi_adv_update {
+	int32_t client_if;
+	int32_t status;
+} __attribute__((packed));
+
+
+#define HAL_EV_GATT_CLIENT_MULTI_ADV_DATA	0xa6
+struct hal_ev_gatt_client_multi_adv_data {
+	int32_t client_if;
+	int32_t status;
+} __attribute__((packed));
+
+
+#define HAL_EV_GATT_CLIENT_MULTI_ADV_DISABLE	0xa7
+struct hal_ev_gatt_client_multi_adv_disable {
+	int32_t client_if;
+	int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_CONGESTION		0xa8
+struct hal_ev_gatt_client_congestion {
+	int32_t conn_id;
+	uint8_t congested;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_CONFIG_BATCHSCAN	0xa9
+struct hal_ev_gatt_client_config_batchscan {
+	int32_t client_if;
+	int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_ENABLE_BATCHSCAN	0xaa
+struct hal_ev_gatt_client_enable_batchscan {
+	int32_t action;
+	int32_t client_if;
+	int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_BATCHSCAN_REPORTS	0xab
+struct hal_ev_gatt_client_batchscan_reports {
+	int32_t client_if;
+	int32_t status;
+	int32_t format;
+	int32_t num;
+	int32_t data_len;
+	uint8_t data[0];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_BATCHSCAN_THRESHOLD	0xac
+struct hal_ev_gatt_client_batchscan_threshold {
+	int32_t client_if;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_TRACK_ADV		0xad
+struct hal_ev_gatt_client_track_adv {
+	int32_t client_if;
+	int32_t filetr_index;
+	int32_t address_type;
+	uint8_t address[6];
+	int32_t state;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_INDICATION_SENT	0xae
+struct hal_ev_gatt_server_indication_sent {
+	int32_t conn_id;
+	int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_CONGESTION		0xaf
+struct hal_ev_gatt_server_congestion {
+	int32_t conn_id;
+	uint8_t congested;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_MTU_CHANGED		0xb0
+struct hal_ev_gatt_server_mtu_changed {
+	int32_t conn_id;
+	int32_t mtu;
+} __attribute__((packed));
+
+#define HAL_GATT_PERMISSION_READ			0x0001
+#define HAL_GATT_PERMISSION_READ_ENCRYPTED		0x0002
+#define HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM		0x0004
+#define HAL_GATT_PERMISSION_WRITE			0x0010
+#define HAL_GATT_PERMISSION_WRITE_ENCRYPTED		0x0020
+#define HAL_GATT_PERMISSION_WRITE_ENCRYPTED_MITM	0x0040
+#define HAL_GATT_PERMISSION_WRITE_SIGNED		0x0080
+#define HAL_GATT_PERMISSION_WRITE_SIGNED_MITM		0x0100
+
+#define HAL_GATT_AUTHENTICATION_NONE		0
+#define HAL_GATT_AUTHENTICATION_NO_MITM		1
+#define HAL_GATT_AUTHENTICATION_MITM		2
+
+#define HAL_HF_CLIENT_CONN_STATE_DISCONNECTED		0x00
+#define HAL_HF_CLIENT_CONN_STATE_CONNECTING		0x01
+#define HAL_HF_CLIENT_CONN_STATE_CONNECTED		0x02
+#define HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED		0x03
+#define HAL_HF_CLIENT_CONN_STATE_DISCONNECTING		0x04
+
+#define HAL_HF_CLIENT_PEER_FEAT_3WAY		0x00000001
+#define HAL_HF_CLIENT_PEER_FEAT_ECNR		0x00000002
+#define HAL_HF_CLIENT_PEER_FEAT_VREC		0x00000004
+#define HAL_HF_CLIENT_PEER_FEAT_INBAND		0x00000008
+#define HAL_HF_CLIENT_PEER_FEAT_VTAG		0x00000010
+#define HAL_HF_CLIENT_PEER_FEAT_REJECT		0x00000020
+#define HAL_HF_CLIENT_PEER_FEAT_ECS		0x00000040
+#define HAL_HF_CLIENT_PEER_FEAT_ECC		0x00000080
+#define HAL_HF_CLIENT_PEER_FEAT_EXTERR		0x00000100
+#define HAL_HF_CLIENT_PEER_FEAT_CODEC		0x00000200
+
+#define HAL_HF_CLIENT_CHLD_FEAT_REL		0x00000001
+#define HAL_HF_CLIENT_CHLD_FEAT_REL_ACC		0x00000002
+#define HAL_HF_CLIENT_CHLD_FEAT_REL_X		0x00000004
+#define HAL_HF_CLIENT_CHLD_FEAT_HOLD_ACC	0x00000008
+#define HAL_HF_CLIENT_CHLD_FEAT_PRIV_X		0x00000010
+#define HAL_HF_CLIENT_CHLD_FEAT_MERGE		0x00000020
+#define HAL_HF_CLIENT_CHLD_FEAT_MERGE_DETACH	0x00000040
+
+#define HAL_EV_HF_CLIENT_CONN_STATE			0x81
+struct hal_ev_hf_client_conn_state {
+	uint8_t state;
+	uint32_t peer_feat;
+	uint32_t chld_feat;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED		0x00
+#define HAL_HF_CLIENT_AUDIO_STATE_CONNECTING		0x01
+#define HAL_HF_CLIENT_AUDIO_STATE_CONNECTED		0x02
+#define HAL_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC	0x03
+
+#define HAL_EV_HF_CLIENT_AUDIO_STATE			0x82
+struct hal_ev_hf_client_audio_state {
+	uint8_t state;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_VR_STOPPED	0x00
+#define HAL_HF_CLIENT_VR_STARTED	0x01
+
+#define HAL_EV_HF_CLIENT_VR_STATE			0x83
+struct hal_ev_hf_client_vr_state {
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_NET_NOT_AVAILABLE		0x00
+#define HAL_HF_CLIENT_NET_AVAILABLE		0x01
+
+#define HAL_EV_HF_CLIENT_NET_STATE			0x84
+struct hal_ev_hf_client_net_state {
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_NET_ROAMING_TYPE_HOME		0x00
+#define HAL_HF_CLIENT_NET_ROAMING_TYPE_ROAMING		0x01
+
+#define HAL_EV_HF_CLIENT_NET_ROAMING_TYPE		0x85
+struct hal_ev_hf_client_net_roaming_type {
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_HF_CLIENT_NET_SIGNAL_STRENGTH		0x86
+struct hal_ev_hf_client_net_signal_strength {
+	uint8_t signal_strength;
+} __attribute__((packed));
+
+#define HAL_EV_HF_CLIENT_BATTERY_LEVEL			0x87
+struct hal_ev_hf_client_battery_level {
+	uint8_t battery_level;
+} __attribute__((packed));
+
+#define HAL_EV_HF_CLIENT_OPERATOR_NAME			0x88
+struct hal_ev_hf_client_operator_name {
+	uint16_t name_len;
+	uint8_t name[0];
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_CALL_IND_NO_CALL_IN_PROGERSS	0x00
+#define HAL_HF_CLIENT_CALL_IND_CALL_IN_PROGERSS		0x01
+
+#define HAL_EV_HF_CLIENT_CALL_INDICATOR			0x89
+struct hal_ev_hf_client_call_indicator {
+	uint8_t call;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_CALL_SETUP_NONE			0x00
+#define HAL_HF_CLIENT_CALL_SETUP_INCOMING		0x01
+#define HAL_HF_CLIENT_CALL_SETUP_OUTGOING		0x02
+#define HAL_HF_CLIENT_CALL_SETUP_ALERTING		0x03
+
+#define HAL_EV_HF_CLIENT_CALL_SETUP_INDICATOR		0x8a
+struct hal_ev_hf_client_call_setup_indicator {
+	uint8_t call_setup;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_CALL_HELD_IND_NONE		0x00
+#define HAL_HF_CLIENT_CALL_HELD_IND_HOLD_AND_ACTIVE	0x01
+#define HAL_HF_CLIENT_CALL_SETUP_IND_HOLD		0x02
+
+#define HAL_EV_HF_CLIENT_CALL_HELD_INDICATOR		0x8b
+struct hal_ev_hf_client_call_held_indicator {
+	uint8_t call_held;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_HELD		0x00
+#define HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_ACCEPT	0x01
+#define HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_REJECT	0x02
+
+#define HAL_EV_HF_CLIENT_RESPONSE_AND_HOLD_STATUS	0x8c
+struct hal_ev_hf_client_response_and_hold_status {
+	uint8_t status;
+} __attribute__((packed));
+
+#define HAL_EV_HF_CLIENT_CALLING_LINE_IDENT		0x8d
+struct hal_ev_hf_client_calling_line_ident {
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_EV_HF_CLIENT_CALL_WAITING			0x8e
+struct hal_ev_hf_client_call_waiting {
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_DIRECTION_OUTGOING	0x00
+#define HAL_HF_CLIENT_DIRECTION_INCOMING	0x01
+
+#define HAL_HF_CLIENT_CALL_STATE_ACTIVE			0x00
+#define HAL_HF_CLIENT_CALL_STATE_HELD			0x01
+#define HAL_HF_CLIENT_CALL_STATE_DIALING		0x02
+#define HAL_HF_CLIENT_CALL_STATE_ALERTING		0x03
+#define HAL_HF_CLIENT_CALL_STATE_INCOMING		0x04
+#define HAL_HF_CLIENT_CALL_STATE_WAITING		0x05
+#define HAL_HF_CLIENT_CALL_STATE_HELD_BY_RESP_AND_HOLD	0x06
+
+#define HAL_EV_HF_CLIENT_CURRENT_CALL			0x8f
+struct hal_ev_hf_client_current_call {
+	uint8_t index;
+	uint8_t direction;
+	uint8_t call_state;
+	uint8_t multiparty;
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_EV_CLIENT_VOLUME_CHANGED			0x90
+struct hal_ev_hf_client_volume_changed {
+	uint8_t type;
+	uint8_t volume;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_CMD_COMP_OK			0x00
+#define HAL_HF_CLIENT_CMD_COMP_ERR			0x01
+#define HAL_HF_CLIENT_CMD_COMP_ERR_NO_CARRIER		0x02
+#define HAL_HF_CLIENT_CMD_COMP_ERR_BUSY			0x03
+#define HAL_HF_CLIENT_CMD_COMP_ERR_NO_ANSWER		0x04
+#define HAL_HF_CLIENT_CMD_COMP_ERR_DELAYED		0x05
+#define HAL_HF_CLIENT_CMD_COMP_ERR_BACKLISTED		0x06
+#define HAL_HF_CLIENT_CMD_COMP_ERR_CME			0x07
+
+#define HAL_EV_CLIENT_COMMAND_COMPLETE			0x91
+struct hal_ev_hf_client_command_complete {
+	uint8_t type;
+	uint8_t cme;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_SUBSCR_TYPE_UNKNOWN	0x00
+#define HAL_HF_CLIENT_SUBSCR_TYPE_VOICE		0x01
+#define HAL_HF_CLIENT_SUBSCR_TYPE_FAX		0x02
+
+#define HAL_EV_CLIENT_SUBSCRIBER_SERVICE_INFO		0x92
+struct hal_ev_hf_client_subscriber_service_info {
+	uint8_t type;
+	uint16_t name_len;
+	uint8_t name[0];
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_INBAND_RINGTONE_NOT_PROVIDED	0x00
+#define HAL_HF_CLIENT_INBAND_RINGTONE_PROVIDED		0x01
+
+#define HAL_EV_CLIENT_INBAND_SETTINGS			0x93
+struct hal_ev_hf_client_inband_settings {
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_CLIENT_LAST_VOICE_CALL_TAG_NUM		0x94
+struct hal_ev_hf_client_last_void_call_tag_num {
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_EV_CLIENT_RING_INDICATION			0x95
+
+#define HAL_EV_MAP_CLIENT_REMOTE_MAS_INSTANCES	0x81
+struct hal_map_client_mas_instance {
+	int32_t id;
+	int32_t scn;
+	int32_t msg_types;
+	int32_t name_len;
+	uint8_t name[0];
+} __attribute__((packed));
+
+struct hal_ev_map_client_remote_mas_instances {
+	int8_t status;
+	uint8_t bdaddr[6];
+	int32_t num_instances;
+	struct hal_map_client_mas_instance instances[0];
+} __attribute__((packed));
diff --git a/repo/android/hal-pan.c b/repo/android/hal-pan.c
new file mode 100644
index 0000000..61d44a9
--- /dev/null
+++ b/repo/android/hal-pan.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "hal-utils.h"
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "hal-ipc.h"
+
+static const btpan_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_pan_conn_state *ev = buf;
+
+	if (cbs->connection_state_cb)
+		cbs->connection_state_cb(ev->state, ev->status,
+					(bt_bdaddr_t *) ev->bdaddr,
+					ev->local_role, ev->remote_role);
+}
+
+static void handle_ctrl_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_pan_ctrl_state *ev = buf;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	if (cbs->control_state_cb)
+		cbs->control_state_cb(ev->state, ev->local_role, ev->status,
+							(char *)ev->name);
+#else
+	/*
+	 * Callback declared in bt_pan.h is 'typedef void
+	 * (*btpan_control_state_callback)(btpan_control_state_t state,
+	 * bt_status_t error, int local_role, const char* ifname);
+	 * But PanService.Java defined it wrong way.
+	 * private void onControlStateChanged(int local_role, int state,
+	 * int error, String ifname).
+	 * First and third parameters are misplaced, so sending data according
+	 * to PanService.Java.
+	 */
+	if (cbs->control_state_cb)
+		cbs->control_state_cb(ev->local_role, ev->state, ev->status,
+							(char *)ev->name);
+#endif
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_PAN_CTRL_STATE */
+	{ handle_ctrl_state, false, sizeof(struct hal_ev_pan_ctrl_state) },
+	/* HAL_EV_PAN_CONN_STATE */
+	{ handle_conn_state, false, sizeof(struct hal_ev_pan_conn_state) },
+};
+
+static bt_status_t pan_enable(int local_role)
+{
+	struct hal_cmd_pan_enable cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.local_role = local_role;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int pan_get_local_role(void)
+{
+	struct hal_rsp_pan_get_role rsp;
+	size_t len = sizeof(rsp);
+	bt_status_t status;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BTPAN_ROLE_NONE;
+
+	status = hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE, 0, NULL,
+							&len, &rsp, NULL);
+	if (status != BT_STATUS_SUCCESS)
+		return BTPAN_ROLE_NONE;
+
+	return rsp.local_role;
+}
+
+static bt_status_t pan_connect(const bt_bdaddr_t *bd_addr, int local_role,
+					int remote_role)
+{
+	struct hal_cmd_pan_connect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	cmd.local_role = local_role;
+	cmd.remote_role = remote_role;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t pan_disconnect(const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_pan_disconnect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t pan_init(const btpan_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_PAN, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_PAN;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_PAN);
+	}
+
+	return ret;
+}
+
+static void pan_cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_PAN;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_PAN);
+
+	cbs = NULL;
+}
+
+static btpan_interface_t pan_if = {
+	.size = sizeof(pan_if),
+	.init = pan_init,
+	.enable = pan_enable,
+	.get_local_role = pan_get_local_role,
+	.connect = pan_connect,
+	.disconnect = pan_disconnect,
+	.cleanup = pan_cleanup
+};
+
+btpan_interface_t *bt_get_pan_interface(void)
+{
+	return &pan_if;
+}
diff --git a/repo/android/hal-sco.c b/repo/android/hal-sco.c
new file mode 100644
index 0000000..2c95866
--- /dev/null
+++ b/repo/android/hal-sco.c
@@ -0,0 +1,1530 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <hardware/audio.h>
+#include <hardware/hardware.h>
+#include <audio_utils/resampler.h>
+
+#include "hal-utils.h"
+#include "sco-msg.h"
+#include "ipc-common.h"
+#include "hal-log.h"
+#include "hal.h"
+
+#define AUDIO_STREAM_DEFAULT_RATE	44100
+#define AUDIO_STREAM_SCO_RATE		8000
+#define AUDIO_STREAM_DEFAULT_FORMAT	AUDIO_FORMAT_PCM_16_BIT
+
+#define OUT_BUFFER_SIZE			2560
+#define OUT_STREAM_FRAMES		2560
+#define IN_STREAM_FRAMES		5292
+
+#define SOCKET_POLL_TIMEOUT_MS		500
+
+static int listen_sk = -1;
+static int ipc_sk = -1;
+
+static int sco_fd = -1;
+static uint16_t sco_mtu = 0;
+static pthread_mutex_t sco_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static pthread_t ipc_th = 0;
+static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static struct sco_stream_in *sco_stream_in = NULL;
+static struct sco_stream_out *sco_stream_out = NULL;
+
+struct sco_audio_config {
+	uint32_t rate;
+	uint32_t channels;
+	uint32_t frame_num;
+	audio_format_t format;
+};
+
+struct sco_stream_out {
+	struct audio_stream_out stream;
+
+	struct sco_audio_config cfg;
+
+	uint8_t *downmix_buf;
+	uint8_t *cache;
+	size_t cache_len;
+
+	size_t samples;
+	struct timespec start;
+
+	struct resampler_itfe *resampler;
+	int16_t *resample_buf;
+	uint32_t resample_frame_num;
+
+	bt_bdaddr_t bd_addr;
+};
+
+static void sco_close_socket(void)
+{
+	DBG("sco fd %d", sco_fd);
+
+	if (sco_fd < 0)
+		return;
+
+	shutdown(sco_fd, SHUT_RDWR);
+	close(sco_fd);
+	sco_fd = -1;
+}
+
+struct sco_stream_in {
+	struct audio_stream_in stream;
+
+	struct sco_audio_config cfg;
+
+	struct resampler_itfe *resampler;
+	int16_t *resample_buf;
+	uint32_t resample_frame_num;
+
+	bt_bdaddr_t bd_addr;
+};
+
+struct sco_dev {
+	struct audio_hw_device dev;
+	struct sco_stream_out *out;
+	struct sco_stream_in *in;
+};
+
+/*
+ * return the minimum frame numbers from resampling between BT stack's rate
+ * and audio flinger's. For output stream, 'output' shall be true, otherwise
+ * false for input streams at audio flinger side.
+ */
+static size_t get_resample_frame_num(uint32_t sco_rate, uint32_t rate,
+						size_t frame_num, bool output)
+{
+	size_t resample_frames_num = frame_num * sco_rate / rate + output;
+
+	DBG("resampler: sco_rate %d frame_num %zd rate %d resample frames %zd",
+				sco_rate, frame_num, rate, resample_frames_num);
+
+	return resample_frames_num;
+}
+
+/* SCO IPC functions */
+
+static int sco_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len,
+			void *param, size_t *rsp_len, void *rsp, int *fd)
+{
+	ssize_t ret;
+	struct msghdr msg;
+	struct iovec iv[2];
+	struct ipc_hdr cmd;
+	char cmsgbuf[CMSG_SPACE(sizeof(int))];
+	struct ipc_status s;
+	size_t s_len = sizeof(s);
+
+	pthread_mutex_lock(&sk_mutex);
+
+	if (ipc_sk < 0) {
+		error("sco: Invalid cmd socket passed to sco_ipc_cmd");
+		goto failed;
+	}
+
+	if (!rsp || !rsp_len) {
+		memset(&s, 0, s_len);
+		rsp_len = &s_len;
+		rsp = &s;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.service_id = service_id;
+	cmd.opcode = opcode;
+	cmd.len = len;
+
+	iv[0].iov_base = &cmd;
+	iv[0].iov_len = sizeof(cmd);
+
+	iv[1].iov_base = param;
+	iv[1].iov_len = len;
+
+	msg.msg_iov = iv;
+	msg.msg_iovlen = 2;
+
+	ret = sendmsg(ipc_sk, &msg, 0);
+	if (ret < 0) {
+		error("sco: Sending command failed:%s", strerror(errno));
+		goto failed;
+	}
+
+	/* socket was shutdown */
+	if (ret == 0) {
+		error("sco: Command socket closed");
+		goto failed;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&cmd, 0, sizeof(cmd));
+
+	iv[0].iov_base = &cmd;
+	iv[0].iov_len = sizeof(cmd);
+
+	iv[1].iov_base = rsp;
+	iv[1].iov_len = *rsp_len;
+
+	msg.msg_iov = iv;
+	msg.msg_iovlen = 2;
+
+	if (fd) {
+		memset(cmsgbuf, 0, sizeof(cmsgbuf));
+		msg.msg_control = cmsgbuf;
+		msg.msg_controllen = sizeof(cmsgbuf);
+	}
+
+	ret = recvmsg(ipc_sk, &msg, 0);
+	if (ret < 0) {
+		error("sco: Receiving command response failed:%s",
+							strerror(errno));
+		goto failed;
+	}
+
+	if (ret < (ssize_t) sizeof(cmd)) {
+		error("sco: Too small response received(%zd bytes)", ret);
+		goto failed;
+	}
+
+	if (cmd.service_id != service_id) {
+		error("sco: Invalid service id (%u vs %u)", cmd.service_id,
+								service_id);
+		goto failed;
+	}
+
+	if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
+		error("sco: Malformed response received(%zd bytes)", ret);
+		goto failed;
+	}
+
+	if (cmd.opcode != opcode && cmd.opcode != SCO_OP_STATUS) {
+		error("sco: Invalid opcode received (%u vs %u)",
+						cmd.opcode, opcode);
+		goto failed;
+	}
+
+	if (cmd.opcode == SCO_OP_STATUS) {
+		struct ipc_status *s = rsp;
+
+		if (sizeof(*s) != cmd.len) {
+			error("sco: Invalid status length");
+			goto failed;
+		}
+
+		if (s->code == SCO_STATUS_SUCCESS) {
+			error("sco: Invalid success status response");
+			goto failed;
+		}
+
+		pthread_mutex_unlock(&sk_mutex);
+
+		return s->code;
+	}
+
+	pthread_mutex_unlock(&sk_mutex);
+
+	/* Receive auxiliary data in msg */
+	if (fd) {
+		struct cmsghdr *cmsg;
+
+		*fd = -1;
+
+		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+					cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+			if (cmsg->cmsg_level == SOL_SOCKET
+					&& cmsg->cmsg_type == SCM_RIGHTS) {
+				memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+				break;
+			}
+		}
+
+		if (*fd < 0)
+			goto failed;
+	}
+
+	*rsp_len = cmd.len;
+
+	return SCO_STATUS_SUCCESS;
+
+failed:
+	/* Some serious issue happen on IPC - recover */
+	shutdown(ipc_sk, SHUT_RDWR);
+	pthread_mutex_unlock(&sk_mutex);
+
+	return SCO_STATUS_FAILED;
+}
+
+static int ipc_get_sco_fd(bt_bdaddr_t *bd_addr)
+{
+	int ret = SCO_STATUS_SUCCESS;
+
+	pthread_mutex_lock(&sco_mutex);
+
+	if (sco_fd < 0) {
+		struct sco_cmd_get_fd cmd;
+		struct sco_rsp_get_fd rsp;
+		size_t rsp_len = sizeof(rsp);
+
+		DBG("Getting SCO fd");
+
+		memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+		ret = sco_ipc_cmd(SCO_SERVICE_ID, SCO_OP_GET_FD, sizeof(cmd),
+						&cmd, &rsp_len, &rsp, &sco_fd);
+
+		/* Sometimes mtu returned is wrong */
+		sco_mtu = /* rsp.mtu */ 48;
+	}
+
+	pthread_mutex_unlock(&sco_mutex);
+
+	return ret;
+}
+
+/* Audio stream functions */
+
+static void downmix_to_mono(struct sco_stream_out *out, const uint8_t *buffer,
+							size_t frame_num)
+{
+	const int16_t *input = (const void *) buffer;
+	int16_t *output = (void *) out->downmix_buf;
+	size_t i;
+
+	for (i = 0; i < frame_num; i++) {
+		int16_t l = get_le16(&input[i * 2]);
+		int16_t r = get_le16(&input[i * 2 + 1]);
+
+		put_le16((l + r) / 2, &output[i]);
+	}
+}
+
+static uint64_t timespec_diff_us(struct timespec *a, struct timespec *b)
+{
+	struct timespec res;
+
+	res.tv_sec = a->tv_sec - b->tv_sec;
+	res.tv_nsec = a->tv_nsec - b->tv_nsec;
+
+	if (res.tv_nsec < 0) {
+		res.tv_sec--;
+		res.tv_nsec += 1000000000ll; /* 1sec */
+	}
+
+	return res.tv_sec * 1000000ll + res.tv_nsec / 1000ll;
+}
+
+static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
+								size_t bytes)
+{
+	struct pollfd pfd;
+	size_t len, written = 0;
+	int ret;
+	uint8_t *p;
+	uint64_t audio_sent_us, audio_passed_us;
+
+	pfd.fd = sco_fd;
+	pfd.events = POLLOUT | POLLHUP | POLLNVAL;
+
+	while (bytes > written) {
+		struct timespec now;
+
+		/* poll for sending */
+		if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) {
+			DBG("timeout fd %d", sco_fd);
+			return false;
+		}
+
+		if (pfd.revents & (POLLHUP | POLLNVAL)) {
+			error("error fd %d, events 0x%x", sco_fd, pfd.revents);
+			return false;
+		}
+
+		len = bytes - written > sco_mtu ? sco_mtu : bytes - written;
+
+		clock_gettime(CLOCK_REALTIME, &now);
+		/* Mark start of the stream */
+		if (!out->samples)
+			memcpy(&out->start, &now, sizeof(out->start));
+
+		audio_sent_us = out->samples * 1000000ll / AUDIO_STREAM_SCO_RATE;
+		audio_passed_us = timespec_diff_us(&now, &out->start);
+		if ((int) (audio_sent_us - audio_passed_us) > 1500) {
+			struct timespec timeout = {0,
+						(audio_sent_us -
+						audio_passed_us) * 1000};
+			DBG("Sleeping for %d ms",
+					(int) (audio_sent_us - audio_passed_us));
+			nanosleep(&timeout, NULL);
+		} else if ((int)(audio_passed_us - audio_sent_us) > 50000) {
+			DBG("\n\nResync\n\n");
+			out->samples = 0;
+			memcpy(&out->start, &now, sizeof(out->start));
+		}
+
+		if (out->cache_len) {
+			DBG("First packet cache_len %zd", out->cache_len);
+			memcpy(out->cache + out->cache_len, buffer,
+						sco_mtu - out->cache_len);
+			p = out->cache;
+		} else {
+			if (bytes - written >= sco_mtu)
+				p = (void *) buffer + written;
+			else {
+				memcpy(out->cache, buffer + written,
+							bytes - written);
+				out->cache_len = bytes - written;
+				DBG("Last packet, cache %zd bytes",
+							bytes - written);
+				written += bytes - written;
+				continue;
+			}
+		}
+
+		ret = write(sco_fd, p, len);
+		if (ret > 0) {
+			if (out->cache_len) {
+				written = sco_mtu - out->cache_len;
+				out->cache_len = 0;
+			} else
+				written += ret;
+
+			out->samples += ret / 2;
+
+			DBG("written %d samples %zd total %zd bytes",
+					ret, out->samples, written);
+			continue;
+		}
+
+		if (errno == EAGAIN) {
+			ret = errno;
+			warn("write failed (%d)", ret);
+			continue;
+		}
+
+		if (errno != EINTR) {
+			ret = errno;
+			error("write failed (%d) fd %d bytes %zd", ret, sco_fd,
+									bytes);
+			return false;
+		}
+	}
+
+	DBG("written %zd bytes", bytes);
+
+	return true;
+}
+
+static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
+								size_t bytes)
+{
+	struct sco_stream_out *out = (struct sco_stream_out *) stream;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	size_t frame_num = bytes / audio_stream_out_frame_size(stream);
+#else
+	size_t frame_num = bytes / audio_stream_frame_size(&out->stream.common);
+#endif
+	size_t output_frame_num = frame_num;
+	void *send_buf = out->downmix_buf;
+	size_t total;
+
+	DBG("write to fd %d bytes %zu", sco_fd, bytes);
+
+	if (ipc_get_sco_fd(&out->bd_addr) != SCO_STATUS_SUCCESS)
+		return -1;
+
+	if (!out->downmix_buf) {
+		error("sco: downmix buffer not initialized");
+		return -1;
+	}
+
+	downmix_to_mono(out, buffer, frame_num);
+
+	if (out->resampler) {
+		int ret;
+
+		/* limit resampler's output within what resample buf can hold */
+		output_frame_num = out->resample_frame_num;
+
+		ret = out->resampler->resample_from_input(out->resampler,
+							send_buf,
+							&frame_num,
+							out->resample_buf,
+							&output_frame_num);
+		if (ret) {
+			error("Failed to resample frames: %zd input %zd (%s)",
+				frame_num, output_frame_num, strerror(ret));
+			return -1;
+		}
+
+		send_buf = out->resample_buf;
+
+		DBG("Resampled: frame_num %zd, output_frame_num %zd",
+						frame_num, output_frame_num);
+	}
+
+	total = output_frame_num * sizeof(int16_t) * 1;
+
+	DBG("total %zd", total);
+
+	if (!write_data(out, send_buf, total))
+		return -1;
+
+	return bytes;
+}
+
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+	struct sco_stream_out *out = (struct sco_stream_out *) stream;
+
+	DBG("rate %u", out->cfg.rate);
+
+	return out->cfg.rate;
+}
+
+static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+	DBG("rate %u", rate);
+
+	return 0;
+}
+
+static size_t out_get_buffer_size(const struct audio_stream *stream)
+{
+	struct sco_stream_out *out = (struct sco_stream_out *) stream;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	size_t size = audio_stream_out_frame_size(&out->stream) *
+							out->cfg.frame_num;
+#else
+	size_t size = audio_stream_frame_size(&out->stream.common) *
+							out->cfg.frame_num;
+#endif
+
+	/* buffer size without resampling */
+	if (out->cfg.rate == AUDIO_STREAM_SCO_RATE)
+		size = 576 * 2;
+
+	DBG("buf size %zd", size);
+
+	return size;
+}
+
+static uint32_t out_get_channels(const struct audio_stream *stream)
+{
+	struct sco_stream_out *out = (struct sco_stream_out *) stream;
+
+	DBG("channels num: %u", popcount(out->cfg.channels));
+
+	return out->cfg.channels;
+}
+
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+	struct sco_stream_out *out = (struct sco_stream_out *) stream;
+
+	DBG("format: %u", out->cfg.format);
+
+	return out->cfg.format;
+}
+
+static int out_set_format(struct audio_stream *stream, audio_format_t format)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+	DBG("");
+
+	return 0;
+}
+
+static int out_dump(const struct audio_stream *stream, int fd)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+	DBG("%s", kvpairs);
+
+	return 0;
+}
+
+static char *out_get_parameters(const struct audio_stream *stream,
+							const char *keys)
+{
+	DBG("");
+
+	return strdup("");
+}
+
+static uint32_t out_get_latency(const struct audio_stream_out *stream)
+{
+	DBG("");
+
+	return 0;
+}
+
+static int out_set_volume(struct audio_stream_out *stream, float left,
+								float right)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int out_get_render_position(const struct audio_stream_out *stream,
+							uint32_t *dsp_frames)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int out_add_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int out_remove_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int sco_open_output_stream_real(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					audio_output_flags_t flags,
+					struct audio_config *config,
+					struct audio_stream_out **stream_out,
+					const char *address)
+{
+	struct sco_dev *adev = (struct sco_dev *) dev;
+	struct sco_stream_out *out;
+	int chan_num, ret;
+	size_t resample_size;
+
+	DBG("config %p device flags 0x%02x", config, devices);
+
+	if (sco_stream_out) {
+		DBG("stream_out already open");
+		return -EIO;
+	}
+
+	out = calloc(1, sizeof(struct sco_stream_out));
+	if (!out)
+		return -ENOMEM;
+
+	DBG("stream %p sco fd %d mtu %u", out, sco_fd, sco_mtu);
+
+	out->stream.common.get_sample_rate = out_get_sample_rate;
+	out->stream.common.set_sample_rate = out_set_sample_rate;
+	out->stream.common.get_buffer_size = out_get_buffer_size;
+	out->stream.common.get_channels = out_get_channels;
+	out->stream.common.get_format = out_get_format;
+	out->stream.common.set_format = out_set_format;
+	out->stream.common.standby = out_standby;
+	out->stream.common.dump = out_dump;
+	out->stream.common.set_parameters = out_set_parameters;
+	out->stream.common.get_parameters = out_get_parameters;
+	out->stream.common.add_audio_effect = out_add_audio_effect;
+	out->stream.common.remove_audio_effect = out_remove_audio_effect;
+	out->stream.get_latency = out_get_latency;
+	out->stream.set_volume = out_set_volume;
+	out->stream.write = out_write;
+	out->stream.get_render_position = out_get_render_position;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	if (address) {
+		DBG("address %s", address);
+
+		str2bt_bdaddr_t(address, &out->bd_addr);
+	}
+#endif
+
+	if (ipc_get_sco_fd(&out->bd_addr) != SCO_STATUS_SUCCESS)
+		DBG("SCO is not connected yet; get fd on write()");
+
+	if (config) {
+		DBG("config: rate %u chan mask %x format %d offload %p",
+				config->sample_rate, config->channel_mask,
+				config->format, &config->offload_info);
+
+		out->cfg.format = config->format;
+		out->cfg.channels = config->channel_mask;
+		out->cfg.rate = config->sample_rate;
+	} else {
+		out->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT;
+		out->cfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+		out->cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
+	}
+
+	out->cfg.frame_num = OUT_STREAM_FRAMES;
+
+	out->downmix_buf = malloc(out_get_buffer_size(&out->stream.common));
+	if (!out->downmix_buf) {
+		free(out);
+		return -ENOMEM;
+	}
+
+	out->cache = malloc(sco_mtu);
+	if (!out->cache) {
+		free(out->downmix_buf);
+		free(out);
+		return -ENOMEM;
+	}
+
+	if (out->cfg.rate == AUDIO_STREAM_SCO_RATE)
+		goto skip_resampler;
+
+	/* Channel numbers for resampler */
+	chan_num = 1;
+
+	ret = create_resampler(out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num,
+						RESAMPLER_QUALITY_DEFAULT, NULL,
+						&out->resampler);
+	if (ret) {
+		error("Failed to create resampler (%s)", strerror(-ret));
+		goto failed;
+	}
+
+	out->resample_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE,
+							out->cfg.rate,
+							out->cfg.frame_num, 1);
+
+	if (!out->resample_frame_num) {
+		error("frame num is too small to resample, discard it");
+		goto failed;
+	}
+
+	resample_size = sizeof(int16_t) * chan_num * out->resample_frame_num;
+
+	out->resample_buf = malloc(resample_size);
+	if (!out->resample_buf) {
+		error("failed to allocate resample buffer for %u frames",
+						out->resample_frame_num);
+		goto failed;
+	}
+
+	DBG("Resampler: input %d output %d chan %d frames %u size %zd",
+				out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num,
+				out->resample_frame_num, resample_size);
+skip_resampler:
+	*stream_out = &out->stream;
+	adev->out = out;
+	sco_stream_out = out;
+
+	return 0;
+failed:
+	if (out->resampler)
+		release_resampler(out->resampler);
+
+	free(out->cache);
+	free(out->downmix_buf);
+	free(out);
+	*stream_out = NULL;
+	adev->out = NULL;
+	sco_stream_out = NULL;
+
+	return ret;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int sco_open_output_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					audio_output_flags_t flags,
+					struct audio_config *config,
+					struct audio_stream_out **stream_out,
+					const char *address)
+{
+	return  sco_open_output_stream_real(dev, handle, devices, flags,
+						config, stream_out, address);
+}
+#else
+static int sco_open_output_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					audio_output_flags_t flags,
+					struct audio_config *config,
+					struct audio_stream_out **stream_out)
+{
+	return sco_open_output_stream_real(dev, handle, devices, flags,
+						config, stream_out, NULL);
+}
+#endif
+
+static void sco_close_output_stream(struct audio_hw_device *dev,
+					struct audio_stream_out *stream_out)
+{
+	struct sco_dev *sco_dev = (struct sco_dev *) dev;
+	struct sco_stream_out *out = (struct sco_stream_out *) stream_out;
+
+	DBG("dev %p stream %p fd %d", dev, out, sco_fd);
+
+	if (out->resampler) {
+		release_resampler(out->resampler);
+		free(out->resample_buf);
+	}
+
+	free(out->cache);
+	free(out->downmix_buf);
+	free(out);
+	sco_dev->out = NULL;
+
+	pthread_mutex_lock(&sco_mutex);
+
+	sco_stream_out = NULL;
+
+	if (!sco_stream_in)
+		sco_close_socket();
+
+	pthread_mutex_unlock(&sco_mutex);
+}
+
+static int sco_set_parameters(struct audio_hw_device *dev,
+							const char *kvpairs)
+{
+	DBG("%s", kvpairs);
+
+	return 0;
+}
+
+static char *sco_get_parameters(const struct audio_hw_device *dev,
+							const char *keys)
+{
+	DBG("");
+
+	return strdup("");
+}
+
+static int sco_init_check(const struct audio_hw_device *dev)
+{
+	DBG("");
+
+	return 0;
+}
+
+static int sco_set_voice_volume(struct audio_hw_device *dev, float volume)
+{
+	DBG("%f", volume);
+
+	return 0;
+}
+
+static int sco_set_master_volume(struct audio_hw_device *dev, float volume)
+{
+	DBG("%f", volume);
+
+	return 0;
+}
+
+static int sco_set_mode(struct audio_hw_device *dev, int mode)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int sco_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int sco_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static size_t sco_get_input_buffer_size(const struct audio_hw_device *dev,
+					const struct audio_config *config)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static uint32_t in_get_sample_rate(const struct audio_stream *stream)
+{
+	struct sco_stream_in *in = (struct sco_stream_in *) stream;
+
+	DBG("rate %u", in->cfg.rate);
+
+	return in->cfg.rate;
+}
+
+static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+	DBG("rate %u", rate);
+
+	return 0;
+}
+
+static size_t in_get_buffer_size(const struct audio_stream *stream)
+{
+	struct sco_stream_in *in = (struct sco_stream_in *) stream;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	size_t size = audio_stream_in_frame_size(&in->stream) *
+							in->cfg.frame_num;
+#else
+	size_t size = audio_stream_frame_size(&in->stream.common) *
+							in->cfg.frame_num;
+#endif
+
+	/* buffer size without resampling */
+	if (in->cfg.rate == AUDIO_STREAM_SCO_RATE)
+		size = 576;
+
+	DBG("buf size %zd", size);
+
+	return size;
+}
+
+static uint32_t in_get_channels(const struct audio_stream *stream)
+{
+	struct sco_stream_in *in = (struct sco_stream_in *) stream;
+
+	DBG("channels num: %u", popcount(in->cfg.channels));
+
+	return in->cfg.channels;
+}
+
+static audio_format_t in_get_format(const struct audio_stream *stream)
+{
+	struct sco_stream_in *in = (struct sco_stream_in *) stream;
+
+	DBG("format: %u", in->cfg.format);
+
+	return in->cfg.format;
+}
+
+static int in_set_format(struct audio_stream *stream, audio_format_t format)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int in_standby(struct audio_stream *stream)
+{
+	DBG("");
+
+	return 0;
+}
+
+static int in_dump(const struct audio_stream *stream, int fd)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+	DBG("%s", kvpairs);
+
+	return 0;
+}
+
+static char *in_get_parameters(const struct audio_stream *stream,
+							const char *keys)
+{
+	DBG("");
+
+	return strdup("");
+}
+
+static int in_add_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int in_remove_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int in_set_gain(struct audio_stream_in *stream, float gain)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static bool read_data(struct sco_stream_in *in, char *buffer, size_t bytes)
+{
+	struct pollfd pfd;
+	size_t len, read_bytes = 0;
+
+	pfd.fd = sco_fd;
+	pfd.events = POLLIN | POLLHUP | POLLNVAL;
+
+	while (bytes > read_bytes) {
+		int ret;
+
+		/* poll for reading */
+		if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) {
+			DBG("timeout fd %d", sco_fd);
+			return false;
+		}
+
+		if (pfd.revents & (POLLHUP | POLLNVAL)) {
+			error("error fd %d, events 0x%x", sco_fd, pfd.revents);
+			return false;
+		}
+
+		len = bytes - read_bytes > sco_mtu ? sco_mtu :
+							bytes - read_bytes;
+
+		ret = read(sco_fd, buffer + read_bytes, len);
+		if (ret > 0) {
+			read_bytes += ret;
+			DBG("read %d total %zd", ret, read_bytes);
+			continue;
+		}
+
+		if (errno == EAGAIN) {
+			ret = errno;
+			warn("read failed (%d)", ret);
+			continue;
+		}
+
+		if (errno != EINTR) {
+			ret = errno;
+			error("read failed (%d) fd %d bytes %zd", ret, sco_fd,
+									bytes);
+			return false;
+		}
+	}
+
+	DBG("read %zd bytes", read_bytes);
+
+	return true;
+}
+
+static ssize_t in_read(struct audio_stream_in *stream, void *buffer,
+								size_t bytes)
+{
+	struct sco_stream_in *in = (struct sco_stream_in *) stream;
+	size_t frame_size, frame_num, input_frame_num;
+	void *read_buf = buffer;
+	size_t total = bytes;
+	int ret;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	frame_size = audio_stream_in_frame_size(&in->stream);
+#else
+	frame_size = audio_stream_frame_size(&stream->common);
+#endif
+
+	if (!frame_size)
+		return -1;
+
+	frame_num = bytes / frame_size;
+	input_frame_num = frame_num;
+
+	DBG("Read from fd %d bytes %zu", sco_fd, bytes);
+
+	if (ipc_get_sco_fd(&in->bd_addr) != SCO_STATUS_SUCCESS)
+		return -1;
+
+	if (!in->resampler && in->cfg.rate != AUDIO_STREAM_SCO_RATE) {
+		error("Cannot find resampler");
+		return -1;
+	}
+
+	if (in->resampler) {
+		input_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE,
+							in->cfg.rate,
+							frame_num, 0);
+		if (input_frame_num > in->resample_frame_num) {
+			DBG("resize input frames from %zd to %d",
+				input_frame_num, in->resample_frame_num);
+			input_frame_num = in->resample_frame_num;
+		}
+
+		read_buf = in->resample_buf;
+
+		total = input_frame_num * sizeof(int16_t) * 1;
+	}
+
+	if(!read_data(in, read_buf, total))
+		return -1;
+
+	if (in->resampler) {
+		ret = in->resampler->resample_from_input(in->resampler,
+							in->resample_buf,
+							&input_frame_num,
+							(int16_t *) buffer,
+							&frame_num);
+		if (ret) {
+			error("Failed to resample frames: %zd input %zd (%s)",
+					frame_num, input_frame_num,
+					strerror(ret));
+			return -1;
+		}
+
+		DBG("resampler: remain %zd output %zd frames", input_frame_num,
+								frame_num);
+	}
+
+	return bytes;
+}
+
+static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int sco_open_input_stream_real(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					struct audio_config *config,
+					struct audio_stream_in **stream_in,
+					audio_input_flags_t flags,
+					const char *address,
+					audio_source_t source)
+{
+	struct sco_dev *sco_dev = (struct sco_dev *) dev;
+	struct sco_stream_in *in;
+	int chan_num, ret;
+	size_t resample_size;
+
+	DBG("config %p device flags 0x%02x", config, devices);
+
+	if (sco_stream_in) {
+		DBG("stream_in already open");
+		ret = -EIO;
+		goto failed2;
+	}
+
+	in = calloc(1, sizeof(struct sco_stream_in));
+	if (!in)
+		return -ENOMEM;
+
+	DBG("stream %p sco fd %d mtu %u", in, sco_fd, sco_mtu);
+
+	in->stream.common.get_sample_rate = in_get_sample_rate;
+	in->stream.common.set_sample_rate = in_set_sample_rate;
+	in->stream.common.get_buffer_size = in_get_buffer_size;
+	in->stream.common.get_channels = in_get_channels;
+	in->stream.common.get_format = in_get_format;
+	in->stream.common.set_format = in_set_format;
+	in->stream.common.standby = in_standby;
+	in->stream.common.dump = in_dump;
+	in->stream.common.set_parameters = in_set_parameters;
+	in->stream.common.get_parameters = in_get_parameters;
+	in->stream.common.add_audio_effect = in_add_audio_effect;
+	in->stream.common.remove_audio_effect = in_remove_audio_effect;
+	in->stream.set_gain = in_set_gain;
+	in->stream.read = in_read;
+	in->stream.get_input_frames_lost = in_get_input_frames_lost;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	if (address) {
+		DBG("address %s", address);
+
+		str2bt_bdaddr_t(address, &in->bd_addr);
+	}
+#endif
+
+	if (config) {
+		DBG("config: rate %u chan mask %x format %d offload %p",
+				config->sample_rate, config->channel_mask,
+				config->format, &config->offload_info);
+
+		in->cfg.format = config->format;
+		in->cfg.channels = config->channel_mask;
+		in->cfg.rate = config->sample_rate;
+	} else {
+		in->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT;
+		in->cfg.channels = AUDIO_CHANNEL_OUT_MONO;
+		in->cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
+	}
+
+	in->cfg.frame_num = IN_STREAM_FRAMES;
+
+	if (in->cfg.rate == AUDIO_STREAM_SCO_RATE)
+		goto skip_resampler;
+
+	/* Channel numbers for resampler */
+	chan_num = 1;
+
+	ret = create_resampler(AUDIO_STREAM_SCO_RATE, in->cfg.rate, chan_num,
+						RESAMPLER_QUALITY_DEFAULT, NULL,
+						&in->resampler);
+	if (ret) {
+		error("Failed to create resampler (%s)", strerror(-ret));
+		goto failed;
+	}
+
+	in->resample_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE,
+							in->cfg.rate,
+							in->cfg.frame_num, 0);
+
+	resample_size = sizeof(int16_t) * chan_num * in->resample_frame_num;
+
+	in->resample_buf = malloc(resample_size);
+	if (!in->resample_buf) {
+		error("failed to allocate resample buffer for %d frames",
+							in->resample_frame_num);
+		goto failed;
+	}
+
+	DBG("Resampler: input %d output %d chan %d frames %u size %zd",
+				AUDIO_STREAM_SCO_RATE, in->cfg.rate, chan_num,
+				in->resample_frame_num, resample_size);
+skip_resampler:
+	*stream_in = &in->stream;
+	sco_dev->in = in;
+	sco_stream_in = in;
+
+	return 0;
+failed:
+	if (in->resampler)
+		release_resampler(in->resampler);
+	free(in);
+failed2:
+	*stream_in = NULL;
+	sco_dev->in = NULL;
+	sco_stream_in = NULL;
+
+	return ret;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int sco_open_input_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					struct audio_config *config,
+					struct audio_stream_in **stream_in,
+					audio_input_flags_t flags,
+					const char *address,
+					audio_source_t source)
+{
+	return sco_open_input_stream_real(dev, handle, devices, config,
+						stream_in, flags, address,
+						source);
+}
+#else
+static int sco_open_input_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					struct audio_config *config,
+					struct audio_stream_in **stream_in)
+{
+	return sco_open_input_stream_real(dev, handle, devices, config,
+						stream_in, 0, NULL, 0);
+}
+#endif
+
+static void sco_close_input_stream(struct audio_hw_device *dev,
+					struct audio_stream_in *stream_in)
+{
+	struct sco_dev *sco_dev = (struct sco_dev *) dev;
+	struct sco_stream_in *in = (struct sco_stream_in *) stream_in;
+
+	DBG("dev %p stream %p fd %d", dev, in, sco_fd);
+
+	if (in->resampler) {
+		release_resampler(in->resampler);
+		free(in->resample_buf);
+	}
+
+	free(in);
+	sco_dev->in = NULL;
+
+	pthread_mutex_lock(&sco_mutex);
+
+	sco_stream_in = NULL;
+
+	if (!sco_stream_out)
+		sco_close_socket();
+
+	pthread_mutex_unlock(&sco_mutex);
+}
+
+static int sco_dump(const audio_hw_device_t *device, int fd)
+{
+	DBG("");
+
+	return 0;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int set_master_mute(struct audio_hw_device *dev, bool mute)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int get_master_mute(struct audio_hw_device *dev, bool *mute)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int create_audio_patch(struct audio_hw_device *dev,
+					unsigned int num_sources,
+					const struct audio_port_config *sources,
+					unsigned int num_sinks,
+					const struct audio_port_config *sinks,
+					audio_patch_handle_t *handle)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int release_audio_patch(struct audio_hw_device *dev,
+					audio_patch_handle_t handle)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int get_audio_port(struct audio_hw_device *dev, struct audio_port *port)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int set_audio_port_config(struct audio_hw_device *dev,
+					const struct audio_port_config *config)
+{
+	DBG("");
+	return -ENOSYS;
+}
+#endif
+
+static int sco_close(hw_device_t *device)
+{
+	DBG("");
+
+	free(device);
+
+	return 0;
+}
+
+static void *ipc_handler(void *data)
+{
+	bool done = false;
+	struct pollfd pfd;
+	int sk;
+
+	DBG("");
+
+	while (!done) {
+		DBG("Waiting for connection ...");
+
+		sk = accept(listen_sk, NULL, NULL);
+		if (sk < 0) {
+			int err = errno;
+
+			if (err == EINTR)
+				continue;
+
+			if (err != ECONNABORTED && err != EINVAL)
+				error("sco: Failed to accept socket: %d (%s)",
+							err, strerror(err));
+
+			break;
+		}
+
+		pthread_mutex_lock(&sk_mutex);
+		ipc_sk = sk;
+		pthread_mutex_unlock(&sk_mutex);
+
+		DBG("SCO IPC: Connected");
+
+		memset(&pfd, 0, sizeof(pfd));
+		pfd.fd = ipc_sk;
+		pfd.events = POLLHUP | POLLERR | POLLNVAL;
+
+		/* Check if socket is still alive. Empty while loop.*/
+		while (poll(&pfd, 1, -1) < 0 && errno == EINTR);
+
+		info("SCO HAL: Socket closed");
+
+		pthread_mutex_lock(&sk_mutex);
+		close(ipc_sk);
+		ipc_sk = -1;
+		pthread_mutex_unlock(&sk_mutex);
+	}
+
+	info("Closing SCO IPC thread");
+	return NULL;
+}
+
+static int sco_ipc_init(void)
+{
+	struct sockaddr_un addr;
+	int err;
+	int sk;
+
+	DBG("");
+
+	sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+	if (sk < 0) {
+		err = -errno;
+		error("sco: Failed to create socket: %d (%s)", -err,
+								strerror(-err));
+		return err;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+
+	memcpy(addr.sun_path, BLUEZ_SCO_SK_PATH, sizeof(BLUEZ_SCO_SK_PATH));
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		err = -errno;
+		error("sco: Failed to bind socket: %d (%s)", -err,
+								strerror(-err));
+		goto failed;
+	}
+
+	if (listen(sk, 1) < 0) {
+		err = -errno;
+		error("sco: Failed to listen on the socket: %d (%s)", -err,
+								strerror(-err));
+		goto failed;
+	}
+
+	listen_sk = sk;
+
+	err = pthread_create(&ipc_th, NULL, ipc_handler, NULL);
+	if (err) {
+		err = -err;
+		ipc_th = 0;
+		error("sco: Failed to start IPC thread: %d (%s)",
+							-err, strerror(-err));
+		goto failed;
+	}
+
+	return 0;
+
+failed:
+	close(sk);
+	return err;
+}
+
+static int sco_open(const hw_module_t *module, const char *name,
+							hw_device_t **device)
+{
+	struct sco_dev *dev;
+	int err;
+
+	DBG("");
+
+	if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) {
+		error("SCO: interface %s not matching [%s]", name,
+						AUDIO_HARDWARE_INTERFACE);
+		return -EINVAL;
+	}
+
+	err = sco_ipc_init();
+	if (err < 0)
+		return err;
+
+	dev = calloc(1, sizeof(struct sco_dev));
+	if (!dev)
+		return -ENOMEM;
+
+	dev->dev.common.tag = HARDWARE_DEVICE_TAG;
+	dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
+	dev->dev.common.module = (struct hw_module_t *) module;
+	dev->dev.common.close = sco_close;
+
+	dev->dev.init_check = sco_init_check;
+	dev->dev.set_voice_volume = sco_set_voice_volume;
+	dev->dev.set_master_volume = sco_set_master_volume;
+	dev->dev.set_mode = sco_set_mode;
+	dev->dev.set_mic_mute = sco_set_mic_mute;
+	dev->dev.get_mic_mute = sco_get_mic_mute;
+	dev->dev.set_parameters = sco_set_parameters;
+	dev->dev.get_parameters = sco_get_parameters;
+	dev->dev.get_input_buffer_size = sco_get_input_buffer_size;
+	dev->dev.open_output_stream = sco_open_output_stream;
+	dev->dev.close_output_stream = sco_close_output_stream;
+	dev->dev.open_input_stream = sco_open_input_stream;
+	dev->dev.close_input_stream = sco_close_input_stream;
+	dev->dev.dump = sco_dump;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	dev->dev.set_master_mute = set_master_mute;
+	dev->dev.get_master_mute = get_master_mute;
+	dev->dev.create_audio_patch = create_audio_patch;
+	dev->dev.release_audio_patch = release_audio_patch;
+	dev->dev.get_audio_port = get_audio_port;
+	dev->dev.set_audio_port_config = set_audio_port_config;
+#endif
+
+	*device = &dev->dev.common;
+
+	return 0;
+}
+
+static struct hw_module_methods_t hal_module_methods = {
+	.open = sco_open,
+};
+
+struct audio_module HAL_MODULE_INFO_SYM = {
+	.common = {
+		.tag = HARDWARE_MODULE_TAG,
+		.version_major = 1,
+		.version_minor = 0,
+		.id = AUDIO_HARDWARE_MODULE_ID,
+		.name = "SCO Audio HW HAL",
+		.author = "Intel Corporation",
+		.methods = &hal_module_methods,
+	},
+};
diff --git a/repo/android/hal-socket.c b/repo/android/hal-socket.c
new file mode 100644
index 0000000..cfd50d1
--- /dev/null
+++ b/repo/android/hal-socket.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "hal-ipc.h"
+#include "hal-log.h"
+#include "hal-msg.h"
+#include "hal-utils.h"
+#include "hal.h"
+
+static bt_status_t socket_listen(btsock_type_t type, const char *service_name,
+					const uint8_t *uuid, int chan,
+					int *sock, int flags)
+{
+	struct hal_cmd_socket_listen cmd;
+
+	if (!sock)
+		return BT_STATUS_PARM_INVALID;
+
+	DBG("uuid %s chan %d sock %p type %d service_name %s flags 0x%02x",
+		btuuid2str(uuid), chan, sock, type, service_name, flags);
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	/* type match IPC type */
+	cmd.type = type;
+	cmd.flags = flags;
+	cmd.channel = chan;
+
+	if (uuid)
+		memcpy(cmd.uuid, uuid, sizeof(cmd.uuid));
+
+	if (service_name)
+		memcpy(cmd.name, service_name, strlen(service_name));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN,
+				sizeof(cmd), &cmd, NULL, NULL, sock);
+}
+
+static bt_status_t socket_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type,
+					const uint8_t *uuid, int chan,
+					int *sock, int flags)
+{
+	struct hal_cmd_socket_connect cmd;
+
+	if (!sock)
+		return BT_STATUS_PARM_INVALID;
+
+	DBG("bdaddr %s uuid %s chan %d sock %p type %d flags 0x%02x",
+		bdaddr2str(bdaddr), btuuid2str(uuid), chan, sock, type, flags);
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	/* type match IPC type */
+	cmd.type = type;
+	cmd.flags = flags;
+	cmd.channel = chan;
+
+	if (uuid)
+		memcpy(cmd.uuid, uuid, sizeof(cmd.uuid));
+
+	if (bdaddr)
+		memcpy(cmd.bdaddr, bdaddr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, sock);
+}
+
+static btsock_interface_t socket_if = {
+	sizeof(socket_if),
+	socket_listen,
+	socket_connect
+};
+
+btsock_interface_t *bt_get_socket_interface(void)
+{
+	return &socket_if;
+}
diff --git a/repo/android/hal-utils.c b/repo/android/hal-utils.c
new file mode 100644
index 0000000..e45f6e4
--- /dev/null
+++ b/repo/android/hal-utils.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <cutils/properties.h>
+
+#include "hal.h"
+#include "hal-utils.h"
+
+/*
+ * converts uuid to string
+ * buf should be at least 39 bytes
+ *
+ * returns string representation of uuid
+ */
+const char *bt_uuid_t2str(const uint8_t *uuid, char *buf)
+{
+	int shift = 0;
+	unsigned int i;
+	int is_bt;
+
+	if (!uuid)
+		return strcpy(buf, "NULL");
+
+	is_bt = !memcmp(&uuid[4], &BT_BASE_UUID[4], HAL_UUID_LEN - 4);
+
+	for (i = 0; i < HAL_UUID_LEN; i++) {
+		if (i == 4 && is_bt)
+			break;
+
+		if (i == 4 || i == 6 || i == 8 || i == 10) {
+			buf[i * 2 + shift] = '-';
+			shift++;
+		}
+		sprintf(buf + i * 2 + shift, "%02x", uuid[i]);
+	}
+
+	return buf;
+}
+
+const char *btuuid2str(const uint8_t *uuid)
+{
+	static char buf[MAX_UUID_STR_LEN];
+
+	return bt_uuid_t2str(uuid, buf);
+}
+
+INTMAP(bt_status_t, -1, "(unknown)")
+	DELEMENT(BT_STATUS_SUCCESS),
+	DELEMENT(BT_STATUS_FAIL),
+	DELEMENT(BT_STATUS_NOT_READY),
+	DELEMENT(BT_STATUS_NOMEM),
+	DELEMENT(BT_STATUS_BUSY),
+	DELEMENT(BT_STATUS_DONE),
+	DELEMENT(BT_STATUS_UNSUPPORTED),
+	DELEMENT(BT_STATUS_PARM_INVALID),
+	DELEMENT(BT_STATUS_UNHANDLED),
+	DELEMENT(BT_STATUS_AUTH_FAILURE),
+	DELEMENT(BT_STATUS_RMT_DEV_DOWN),
+ENDMAP
+
+INTMAP(bt_state_t, -1, "(unknown)")
+	DELEMENT(BT_STATE_OFF),
+	DELEMENT(BT_STATE_ON),
+ENDMAP
+
+INTMAP(bt_device_type_t, -1, "(unknown)")
+	DELEMENT(BT_DEVICE_DEVTYPE_BREDR),
+	DELEMENT(BT_DEVICE_DEVTYPE_BLE),
+	DELEMENT(BT_DEVICE_DEVTYPE_DUAL),
+ENDMAP
+
+INTMAP(bt_scan_mode_t, -1, "(unknown)")
+	DELEMENT(BT_SCAN_MODE_NONE),
+	DELEMENT(BT_SCAN_MODE_CONNECTABLE),
+	DELEMENT(BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE),
+ENDMAP
+
+INTMAP(bt_discovery_state_t, -1, "(unknown)")
+	DELEMENT(BT_DISCOVERY_STOPPED),
+	DELEMENT(BT_DISCOVERY_STARTED),
+ENDMAP
+
+INTMAP(bt_acl_state_t, -1, "(unknown)")
+	DELEMENT(BT_ACL_STATE_CONNECTED),
+	DELEMENT(BT_ACL_STATE_DISCONNECTED),
+ENDMAP
+
+INTMAP(bt_bond_state_t, -1, "(unknown)")
+	DELEMENT(BT_BOND_STATE_NONE),
+	DELEMENT(BT_BOND_STATE_BONDING),
+	DELEMENT(BT_BOND_STATE_BONDED),
+ENDMAP
+
+INTMAP(bt_ssp_variant_t, -1, "(unknown)")
+	DELEMENT(BT_SSP_VARIANT_PASSKEY_CONFIRMATION),
+	DELEMENT(BT_SSP_VARIANT_PASSKEY_ENTRY),
+	DELEMENT(BT_SSP_VARIANT_CONSENT),
+	DELEMENT(BT_SSP_VARIANT_PASSKEY_NOTIFICATION),
+ENDMAP
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+INTMAP(bt_property_type_t, -1, "(unknown)")
+	DELEMENT(BT_PROPERTY_BDNAME),
+	DELEMENT(BT_PROPERTY_BDADDR),
+	DELEMENT(BT_PROPERTY_UUIDS),
+	DELEMENT(BT_PROPERTY_CLASS_OF_DEVICE),
+	DELEMENT(BT_PROPERTY_TYPE_OF_DEVICE),
+	DELEMENT(BT_PROPERTY_SERVICE_RECORD),
+	DELEMENT(BT_PROPERTY_ADAPTER_SCAN_MODE),
+	DELEMENT(BT_PROPERTY_ADAPTER_BONDED_DEVICES),
+	DELEMENT(BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT),
+	DELEMENT(BT_PROPERTY_REMOTE_FRIENDLY_NAME),
+	DELEMENT(BT_PROPERTY_REMOTE_RSSI),
+	DELEMENT(BT_PROPERTY_REMOTE_VERSION_INFO),
+	DELEMENT(BT_PROPERTY_LOCAL_LE_FEATURES),
+	DELEMENT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP),
+ENDMAP
+#else
+INTMAP(bt_property_type_t, -1, "(unknown)")
+	DELEMENT(BT_PROPERTY_BDNAME),
+	DELEMENT(BT_PROPERTY_BDADDR),
+	DELEMENT(BT_PROPERTY_UUIDS),
+	DELEMENT(BT_PROPERTY_CLASS_OF_DEVICE),
+	DELEMENT(BT_PROPERTY_TYPE_OF_DEVICE),
+	DELEMENT(BT_PROPERTY_SERVICE_RECORD),
+	DELEMENT(BT_PROPERTY_ADAPTER_SCAN_MODE),
+	DELEMENT(BT_PROPERTY_ADAPTER_BONDED_DEVICES),
+	DELEMENT(BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT),
+	DELEMENT(BT_PROPERTY_REMOTE_FRIENDLY_NAME),
+	DELEMENT(BT_PROPERTY_REMOTE_RSSI),
+	DELEMENT(BT_PROPERTY_REMOTE_VERSION_INFO),
+	DELEMENT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP),
+ENDMAP
+#endif
+
+INTMAP(bt_cb_thread_evt, -1, "(unknown)")
+	DELEMENT(ASSOCIATE_JVM),
+	DELEMENT(DISASSOCIATE_JVM),
+ENDMAP
+
+/* Find first index of given value in table m */
+int int2str_findint(int v, const struct int2str m[])
+{
+	int i;
+
+	for (i = 0; m[i].str; ++i) {
+		if (m[i].val == v)
+			return i;
+	}
+	return -1;
+}
+
+/* Find first index of given string in table m */
+int int2str_findstr(const char *str, const struct int2str m[])
+{
+	int i;
+
+	for (i = 0; m[i].str; ++i) {
+		if (strcmp(m[i].str, str) == 0)
+			return i;
+	}
+	return -1;
+}
+
+/*
+ * convert bd_addr to string
+ * buf must be at least 18 char long
+ *
+ * returns buf
+ */
+const char *bt_bdaddr_t2str(const bt_bdaddr_t *bd_addr, char *buf)
+{
+	const uint8_t *p;
+
+	if (!bd_addr)
+		return strcpy(buf, "NULL");
+
+	p = bd_addr->address;
+
+	snprintf(buf, MAX_ADDR_STR_LEN, "%02x:%02x:%02x:%02x:%02x:%02x",
+					p[0], p[1], p[2], p[3], p[4], p[5]);
+
+	return buf;
+}
+
+/* converts string to bt_bdaddr_t */
+void str2bt_bdaddr_t(const char *str, bt_bdaddr_t *bd_addr)
+{
+	uint8_t *p = bd_addr->address;
+
+	sscanf(str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+				&p[0], &p[1], &p[2], &p[3], &p[4], &p[5]);
+}
+
+/* converts string to uuid */
+void str2bt_uuid_t(const char *str, bt_uuid_t *uuid)
+{
+	int i = 0;
+
+	memcpy(uuid, BT_BASE_UUID, sizeof(bt_uuid_t));
+
+	while (*str && i < (int) sizeof(bt_uuid_t)) {
+		while (*str == '-')
+			str++;
+
+		if (sscanf(str, "%02hhx", &uuid->uu[i]) != 1)
+			break;
+
+		i++;
+		str += 2;
+	}
+}
+
+const char *enum_defines(void *v, int i)
+{
+	const struct int2str *m = v;
+
+	return m[i].str != NULL ? m[i].str : NULL;
+}
+
+const char *enum_strings(void *v, int i)
+{
+	const char **m = v;
+
+	return m[i] != NULL ? m[i] : NULL;
+}
+
+const char *enum_one_string(void *v, int i)
+{
+	const char *m = v;
+
+	return (i == 0) && (m[0] != 0) ? m : NULL;
+}
+
+const char *bdaddr2str(const bt_bdaddr_t *bd_addr)
+{
+	static char buf[MAX_ADDR_STR_LEN];
+
+	return bt_bdaddr_t2str(bd_addr, buf);
+}
+
+static void bonded_devices2string(char *str, void *prop, int prop_len)
+{
+	int count = prop_len / sizeof(bt_bdaddr_t);
+	bt_bdaddr_t *addr = prop;
+
+	strcat(str, "{");
+
+	while (count--) {
+		strcat(str, bdaddr2str(addr));
+		if (count)
+			strcat(str, ", ");
+		addr++;
+	}
+
+	strcat(str, "}");
+}
+
+static void uuids2string(char *str, void *prop, int prop_len)
+{
+	int count = prop_len / sizeof(bt_uuid_t);
+	bt_uuid_t *uuid = prop;
+
+	strcat(str, "{");
+
+	while (count--) {
+		strcat(str, btuuid2str(uuid->uu));
+		if (count)
+			strcat(str, ", ");
+		uuid++;
+	}
+
+	strcat(str, "}");
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void local_le_feat2string(char *str, const bt_local_le_features_t *f)
+{
+	uint16_t scan_num;
+
+	str += sprintf(str, "{\n");
+
+	str += sprintf(str, "Privacy supported: %s,\n",
+				f->local_privacy_enabled ? "TRUE" : "FALSE");
+
+	str += sprintf(str, "Num of advertising instances: %u,\n",
+							f->max_adv_instance);
+
+	str += sprintf(str, "PRA offloading support: %s,\n",
+				f->rpa_offload_supported ? "TRUE" : "FALSE");
+
+	str += sprintf(str, "Num of offloaded IRKs: %u,\n",
+							f->max_irk_list_size);
+
+	str += sprintf(str, "Num of offloaded scan filters: %u,\n",
+						f->max_adv_filter_supported);
+
+	scan_num = (f->scan_result_storage_size_hibyte << 8) +
+					f->scan_result_storage_size_lobyte;
+
+	str += sprintf(str, "Num of offloaded scan results: %u,\n", scan_num);
+
+	str += sprintf(str, "Activity & energy report support: %s\n",
+			f->activity_energy_info_supported ? "TRUE" : "FALSE");
+
+	sprintf(str, "}");
+}
+#endif
+
+const char *btproperty2str(const bt_property_t *property)
+{
+	bt_service_record_t *rec;
+	static char buf[4096];
+	char *p;
+
+	p = buf + sprintf(buf, "type=%s len=%d val=",
+					bt_property_type_t2str(property->type),
+					property->len);
+
+	switch (property->type) {
+	case BT_PROPERTY_BDNAME:
+	case BT_PROPERTY_REMOTE_FRIENDLY_NAME:
+		snprintf(p, property->len + 1, "%s",
+					((bt_bdname_t *) property->val)->name);
+		break;
+	case BT_PROPERTY_BDADDR:
+		sprintf(p, "%s", bdaddr2str((bt_bdaddr_t *) property->val));
+		break;
+	case BT_PROPERTY_CLASS_OF_DEVICE:
+		sprintf(p, "%06x", *((int *) property->val));
+		break;
+	case BT_PROPERTY_TYPE_OF_DEVICE:
+		sprintf(p, "%s", bt_device_type_t2str(
+					*((bt_device_type_t *) property->val)));
+		break;
+	case BT_PROPERTY_REMOTE_RSSI:
+		sprintf(p, "%d", *((char *) property->val));
+		break;
+	case BT_PROPERTY_ADAPTER_SCAN_MODE:
+		sprintf(p, "%s",
+			bt_scan_mode_t2str(*((bt_scan_mode_t *) property->val)));
+		break;
+	case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
+		sprintf(p, "%d", *((int *) property->val));
+		break;
+	case BT_PROPERTY_ADAPTER_BONDED_DEVICES:
+		bonded_devices2string(p, property->val, property->len);
+		break;
+	case BT_PROPERTY_UUIDS:
+		uuids2string(p, property->val, property->len);
+		break;
+	case BT_PROPERTY_SERVICE_RECORD:
+		rec = property->val;
+		sprintf(p, "{%s, %d, %s}", btuuid2str(rec->uuid.uu),
+						rec->channel, rec->name);
+		break;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	case BT_PROPERTY_LOCAL_LE_FEATURES:
+		local_le_feat2string(p, property->val);
+		break;
+#endif
+	case BT_PROPERTY_REMOTE_VERSION_INFO:
+	case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP:
+	default:
+		sprintf(p, "%p", property->val);
+		break;
+	}
+
+	return buf;
+}
+
+#define PROP_PREFIX "persist.sys.bluetooth."
+#define PROP_PREFIX_RO "ro.bluetooth."
+
+int get_config(const char *config_key, char *value, const char *fallback)
+{
+	char key[PROPERTY_KEY_MAX];
+	int ret;
+
+	if (strlen(config_key) + sizeof(PROP_PREFIX) > sizeof(key))
+		return 0;
+
+	snprintf(key, sizeof(key), PROP_PREFIX"%s", config_key);
+
+	ret = property_get(key, value, "");
+	if (ret > 0)
+		return ret;
+
+	snprintf(key, sizeof(key), PROP_PREFIX_RO"%s", config_key);
+
+	ret = property_get(key, value, "");
+	if (ret > 0)
+		return ret;
+
+	if (!fallback)
+		return 0;
+
+	return property_get(fallback, value, "");
+}
diff --git a/repo/android/hal-utils.h b/repo/android/hal-utils.h
new file mode 100644
index 0000000..9c59948
--- /dev/null
+++ b/repo/android/hal-utils.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <endian.h>
+
+#include <hardware/bluetooth.h>
+
+#define MAX_UUID_STR_LEN	37
+#define HAL_UUID_LEN		16
+#define MAX_ADDR_STR_LEN	18
+
+static const char BT_BASE_UUID[] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+	0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb
+};
+
+const char *bt_uuid_t2str(const uint8_t *uuid, char *buf);
+const char *btuuid2str(const uint8_t *uuid);
+const char *bt_bdaddr_t2str(const bt_bdaddr_t *bd_addr, char *buf);
+void str2bt_bdaddr_t(const char *str, bt_bdaddr_t *bd_addr);
+void str2bt_uuid_t(const char *str, bt_uuid_t *uuid);
+const char *btproperty2str(const bt_property_t *property);
+const char *bdaddr2str(const bt_bdaddr_t *bd_addr);
+
+int get_config(const char *config_key, char *value, const char *fallback);
+
+/*
+ * Begin mapping section
+ *
+ * There are some mappings between integer values (enums) and strings
+ * to be presented to user. To make it easier to convert between those two
+ * set of macros is given. It is specially useful when we want to have
+ * strings that match constants from header files like:
+ *  BT_STATUS_SUCCESS (0) and corresponding "BT_STATUS_SUCCESS"
+ * Example of usage:
+ *
+ * INTMAP(int, -1, "invalid")
+ *   DELEMENT(BT_STATUS_SUCCESS)
+ *   DELEMENT(BT_STATUS_FAIL)
+ *   MELEMENT(123, "Some strange value")
+ * ENDMAP
+ *
+ * Just by doing this we have mapping table plus two functions:
+ *  int str2int(const char *str);
+ *  const char *int2str(int v);
+ *
+ * second argument to INTMAP specifies value to be returned from
+ * str2int function when there is not mapping for such number
+ * third argument specifies default value to be returned from int2str
+ *
+ * If same mapping is to be used in several source files put
+ * INTMAP in c file and DECINTMAP in h file.
+ *
+ * For mappings that are to be used in single file only
+ * use SINTMAP which will create the same but everything will be marked
+ * as static.
+ */
+
+struct int2str {
+	int val;		/* int value */
+	const char *str;	/* corresponding string */
+};
+
+int int2str_findint(int v, const struct int2str m[]);
+int int2str_findstr(const char *str, const struct int2str m[]);
+const char *enum_defines(void *v, int i);
+const char *enum_strings(void *v, int i);
+const char *enum_one_string(void *v, int i);
+
+#define TYPE_ENUM(type) ((void *) &__##type##2str[0])
+#define DECINTMAP(type) \
+extern struct int2str __##type##2str[]; \
+const char *type##2##str(type v); \
+type str##2##type(const char *str); \
+
+#define INTMAP(type, deft, defs) \
+const char *type##2##str(type v) \
+{ \
+	int i = int2str_findint((int) v, __##type##2str); \
+	return (i < 0) ? defs : __##type##2str[i].str; \
+} \
+type str##2##type(const char *str) \
+{ \
+	int i = int2str_findstr(str, __##type##2str); \
+	return (i < 0) ? (type) deft : (type) (__##type##2str[i].val); \
+} \
+struct int2str __##type##2str[] = {
+
+#define SINTMAP(type, deft, defs) \
+static struct int2str __##type##2str[]; \
+static inline const char *type##2##str(type v) \
+{ \
+	int i = int2str_findint((int) v, __##type##2str); \
+	return (i < 0) ? defs : __##type##2str[i].str; \
+} \
+static inline type str##2##type(const char *str) \
+{ \
+	int i = int2str_findstr(str, __##type##2str); \
+	return (i < 0) ? (type) deft : (type) (__##type##2str[i].val); \
+} \
+static struct int2str __##type##2str[] = {
+
+#define ENDMAP {0, NULL} };
+
+/* use this to generate string from header file constant */
+#define MELEMENT(v, s) {v, s}
+/* use this to have arbitrary mapping from int to string */
+#define DELEMENT(s) {s, #s}
+/* End of mapping section */
+
+DECINTMAP(bt_status_t);
+DECINTMAP(bt_state_t);
+DECINTMAP(bt_device_type_t);
+DECINTMAP(bt_scan_mode_t);
+DECINTMAP(bt_discovery_state_t);
+DECINTMAP(bt_acl_state_t);
+DECINTMAP(bt_bond_state_t);
+DECINTMAP(bt_ssp_variant_t);
+DECINTMAP(bt_property_type_t);
+DECINTMAP(bt_cb_thread_evt);
+
+static inline uint16_t get_le16(const void *src)
+{
+	const struct __attribute__((packed)) {
+		uint16_t le16;
+	} *p = src;
+
+	return le16toh(p->le16);
+}
+
+static inline void put_le16(uint16_t val, void *dst)
+{
+	struct __attribute__((packed)) {
+		uint16_t le16;
+	} *p = dst;
+
+	p->le16 = htole16(val);
+}
diff --git a/repo/android/hal.h b/repo/android/hal.h
new file mode 100644
index 0000000..709c197
--- /dev/null
+++ b/repo/android/hal.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_sock.h>
+#include <hardware/bt_hh.h>
+#include <hardware/bt_pan.h>
+#include <hardware/bt_av.h>
+#include <hardware/bt_rc.h>
+#include <hardware/bt_hf.h>
+#include <hardware/bt_gatt.h>
+#include <hardware/bt_gatt_client.h>
+#include <hardware/bt_gatt_server.h>
+#include <hardware/bt_hl.h>
+
+#define PLATFORM_VER(a, b, c) ((a << 16) | ( b << 8) | (c))
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+#include <hardware/bt_hf_client.h>
+#include <hardware/bt_mce.h>
+#endif
+
+btsock_interface_t *bt_get_socket_interface(void);
+bthh_interface_t *bt_get_hidhost_interface(void);
+btpan_interface_t *bt_get_pan_interface(void);
+btav_interface_t *bt_get_a2dp_interface(void);
+btrc_interface_t *bt_get_avrcp_interface(void);
+bthf_interface_t *bt_get_handsfree_interface(void);
+btgatt_interface_t *bt_get_gatt_interface(void);
+bthl_interface_t *bt_get_health_interface(void);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+btrc_ctrl_interface_t *bt_get_avrcp_ctrl_interface(void);
+bthf_client_interface_t *bt_get_hf_client_interface(void);
+btmce_interface_t *bt_get_map_client_interface(void);
+btav_interface_t *bt_get_a2dp_sink_interface(void);
+#endif
+
+void bt_thread_associate(void);
+void bt_thread_disassociate(void);
diff --git a/repo/android/handsfree-client.c b/repo/android/handsfree-client.c
new file mode 100644
index 0000000..65659b8
--- /dev/null
+++ b/repo/android/handsfree-client.c
@@ -0,0 +1,2204 @@
+/*
+ *
+ *  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/shared/hfp.h"
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+#include "btio/btio.h"
+#include "ipc.h"
+#include "ipc-common.h"
+#include "src/log.h"
+#include "utils.h"
+
+#include "bluetooth.h"
+#include "hal-msg.h"
+#include "handsfree-client.h"
+#include "sco.h"
+
+#define HFP_HF_CHANNEL 7
+
+#define HFP_HF_FEAT_ECNR	0x00000001
+#define HFP_HF_FEAT_3WAY	0x00000002
+#define HFP_HF_FEAT_CLI		0x00000004
+#define HFP_HF_FEAT_VR		0x00000008
+#define HFP_HF_FEAT_RVC		0x00000010
+#define HFP_HF_FEAT_ECS		0x00000020
+#define HFP_HF_FEAT_ECC		0x00000040
+#define HFP_HF_FEAT_CODEC	0x00000080
+#define HFP_HF_FEAT_HF_IND	0x00000100
+#define HFP_HF_FEAT_ESCO_S4_T2	0x00000200
+
+#define HFP_AG_FEAT_3WAY	0x00000001
+#define HFP_AG_FEAT_ECNR	0x00000002
+#define HFP_AG_FEAT_VR		0x00000004
+#define HFP_AG_FEAT_INBAND	0x00000008
+#define HFP_AG_FEAT_VTAG	0x00000010
+#define HFP_AG_FEAT_REJ_CALL	0x00000020
+#define HFP_AG_FEAT_ECS		0x00000040
+#define HFP_AG_FEAT_ECC		0x00000080
+#define HFP_AG_FEAT_EXT_ERR	0x00000100
+#define HFP_AG_FEAT_CODEC	0x00000200
+
+#define HFP_HF_FEATURES (HFP_HF_FEAT_ECNR | HFP_HF_FEAT_3WAY |\
+				HFP_HF_FEAT_CLI | HFP_HF_FEAT_VR |\
+				HFP_HF_FEAT_RVC | HFP_HF_FEAT_ECS |\
+				HFP_HF_FEAT_ECC)
+
+#define CVSD_OFFSET 0
+#define MSBC_OFFSET 1
+#define CODECS_COUNT (MSBC_OFFSET + 1)
+
+#define CODEC_ID_CVSD 0x01
+#define CODEC_ID_MSBC 0x02
+
+#define MAX_NUMBER_LEN 33
+#define MAX_OPERATOR_NAME_LEN 17
+
+enum hfp_indicator {
+	HFP_INDICATOR_SERVICE = 0,
+	HFP_INDICATOR_CALL,
+	HFP_INDICATOR_CALLSETUP,
+	HFP_INDICATOR_CALLHELD,
+	HFP_INDICATOR_SIGNAL,
+	HFP_INDICATOR_ROAM,
+	HFP_INDICATOR_BATTCHG,
+	HFP_INDICATOR_LAST
+};
+
+typedef void (*ciev_func_t)(uint8_t val);
+
+struct indicator {
+	uint8_t index;
+	uint32_t min;
+	uint32_t max;
+	uint32_t val;
+	ciev_func_t cb;
+};
+
+struct hfp_codec {
+	uint8_t type;
+	bool local_supported;
+	bool remote_supported;
+};
+
+struct device {
+	bdaddr_t bdaddr;
+	struct hfp_hf *hf;
+	uint8_t state;
+	uint8_t audio_state;
+
+	uint8_t negotiated_codec;
+	uint32_t features;
+	struct hfp_codec codecs[2];
+
+	struct indicator ag_ind[HFP_INDICATOR_LAST];
+
+	uint32_t chld_features;
+};
+
+static const struct hfp_codec codecs_defaults[] = {
+	{ CODEC_ID_CVSD, true, false},
+	{ CODEC_ID_MSBC, false, false},
+};
+
+static bdaddr_t adapter_addr;
+
+static struct ipc *hal_ipc = NULL;
+
+static uint32_t hfp_hf_features = 0;
+static uint32_t hfp_hf_record_id = 0;
+static struct queue *devices = NULL;
+static GIOChannel *hfp_hf_server = NULL;
+
+static struct bt_sco *sco = NULL;
+
+static struct device *find_default_device(void)
+{
+	return queue_peek_head(devices);
+}
+
+static bool match_by_bdaddr(const void *data, const void *user_data)
+{
+	const bdaddr_t *addr1 = data;
+	const bdaddr_t *addr2 = user_data;
+
+	return !bacmp(addr1, addr2);
+}
+
+static struct device *find_device(const bdaddr_t *addr)
+{
+	return queue_find(devices, match_by_bdaddr, addr);
+}
+
+static void init_codecs(struct device *dev)
+{
+	memcpy(&dev->codecs, codecs_defaults, sizeof(dev->codecs));
+
+	if (hfp_hf_features & HFP_HF_FEAT_CODEC)
+		dev->codecs[MSBC_OFFSET].local_supported = true;
+}
+
+static struct device *device_create(const bdaddr_t *bdaddr)
+{
+	struct device *dev;
+
+	dev = new0(struct device, 1);
+
+	bacpy(&dev->bdaddr, bdaddr);
+	dev->state = HAL_HF_CLIENT_CONN_STATE_DISCONNECTED;
+	dev->audio_state = HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED;
+
+	init_codecs(dev);
+
+	queue_push_tail(devices, dev);
+
+	return dev;
+}
+
+static struct device *get_device(const bdaddr_t *addr)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (dev)
+		return dev;
+
+	/* We do support only one device as for now */
+	if (queue_isempty(devices))
+		return device_create(addr);
+
+	return NULL;
+}
+
+static void device_set_state(struct device *dev, uint8_t state)
+{
+	struct hal_ev_hf_client_conn_state ev;
+	char address[18];
+
+	if (dev->state == state)
+		return;
+
+	memset(&ev, 0, sizeof(ev));
+
+	dev->state = state;
+
+	ba2str(&dev->bdaddr, address);
+	DBG("device %s state %u", address, state);
+
+	bdaddr2android(&dev->bdaddr, ev.bdaddr);
+	ev.state = state;
+
+	ev.chld_feat = dev->chld_features;
+	ev.peer_feat = dev->features;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_EV_HF_CLIENT_CONN_STATE, sizeof(ev), &ev);
+}
+
+static void device_destroy(struct device *dev)
+{
+	device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_DISCONNECTED);
+	queue_remove(devices, dev);
+
+	if (dev->hf)
+		hfp_hf_unref(dev->hf);
+
+	free(dev);
+}
+
+static void handle_disconnect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_disconnect *cmd = buf;
+	struct device *dev;
+	uint32_t status;
+	bdaddr_t bdaddr;
+	char addr[18];
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+	ba2str(&bdaddr, addr);
+	DBG("Disconnect %s", addr);
+
+	dev = get_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (dev->state == HAL_HF_CLIENT_CONN_STATE_DISCONNECTED) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (dev->state == HAL_HF_CLIENT_CONN_STATE_DISCONNECTING) {
+		status = HAL_STATUS_SUCCESS;
+		goto done;
+	}
+
+	if (dev->state == HAL_HF_CLIENT_CONN_STATE_CONNECTING) {
+		device_destroy(dev);
+		status = HAL_STATUS_SUCCESS;
+		goto done;
+	}
+
+	status = hfp_hf_disconnect(dev->hf) ? HAL_STATUS_SUCCESS :
+							HAL_STATUS_FAILED;
+
+	if (status)
+		device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_DISCONNECTING);
+
+done:
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_DISCONNECT, status);
+}
+
+static void set_audio_state(struct device *dev, uint8_t state)
+{
+	struct hal_ev_hf_client_audio_state ev;
+	char address[18];
+
+	if (dev->audio_state == state)
+		return;
+
+	dev->audio_state = state;
+
+	ba2str(&dev->bdaddr, address);
+	DBG("device %s audio state %u", address, state);
+
+	bdaddr2android(&dev->bdaddr, ev.bdaddr);
+	ev.state = state;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_EV_HF_CLIENT_AUDIO_STATE, sizeof(ev), &ev);
+}
+
+static void bcc_cb(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	struct device *dev = user_data;
+
+	if (result != HFP_RESULT_OK)
+		set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED);
+}
+
+static bool codec_negotiation_supported(struct device *dev)
+{
+	return (dev->features & HFP_AG_FEAT_CODEC) &&
+			(hfp_hf_features & HFP_HF_FEAT_CODEC);
+}
+
+static bool connect_sco(struct device *dev)
+{
+	if (codec_negotiation_supported(dev))
+		return hfp_hf_send_command(dev->hf, bcc_cb, dev,
+								"AT+BCC");
+
+	return bt_sco_connect(sco, &dev->bdaddr, BT_VOICE_CVSD_16BIT);
+}
+
+static void handle_connect_audio(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_connect_audio *cmd = (void *) buf;
+	struct device *dev;
+	uint8_t status;
+	bdaddr_t bdaddr;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev || dev->state != HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED ||
+		dev->audio_state != HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED) {
+		error("hf-client: Cannot create SCO, check SLC or audio state");
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (connect_sco(dev)) {
+		status = HAL_STATUS_SUCCESS;
+		set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_CONNECTING);
+	} else {
+		status = HAL_STATUS_FAILED;
+	}
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_CONNECT_AUDIO, status);
+}
+
+static void handle_disconnect_audio(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_disconnect_audio *cmd = (void *) buf;
+	struct device *dev;
+	uint8_t status;
+	bdaddr_t bdaddr;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev ||
+		dev->audio_state == HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED) {
+		error("hf-client: Device not found or audio not connected");
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	bt_sco_disconnect(sco);
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_DISCONNECT_AUDIO, status);
+}
+
+static void cmd_complete_cb(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	struct hal_ev_hf_client_command_complete ev;
+
+	DBG("");
+	memset(&ev, 0, sizeof(ev));
+
+	switch (result) {
+	case HFP_RESULT_OK:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_OK;
+		break;
+	case HFP_RESULT_NO_CARRIER:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_CARRIER;
+		break;
+	case HFP_RESULT_ERROR:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
+		break;
+	case HFP_RESULT_BUSY:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BUSY;
+		break;
+	case HFP_RESULT_NO_ANSWER:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_ANSWER;
+		break;
+	case HFP_RESULT_DELAYED:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_DELAYED;
+		break;
+	case HFP_RESULT_BLACKLISTED:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BACKLISTED;
+		break;
+	case HFP_RESULT_CME_ERROR:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_CME;
+		ev.cme = cme_err;
+		break;
+	case HFP_RESULT_CONNECT:
+	case HFP_RESULT_RING:
+	case HFP_RESULT_NO_DIALTONE:
+	default:
+		error("hf-client: Unknown error code %d", result);
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
+		break;
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_EV_CLIENT_COMMAND_COMPLETE, sizeof(ev), &ev);
+}
+
+static void handle_start_vr(const void *buf, uint16_t len)
+{
+	struct device *dev;
+	uint8_t status;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BVRA=1"))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_START_VR, status);
+}
+
+static void handle_stop_vr(const void *buf, uint16_t len)
+{
+	struct device *dev;
+	uint8_t status;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BVRA=0"))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_STOP_VR, status);
+}
+
+static void handle_volume_control(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_volume_control *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+	uint8_t vol;
+	bool ret;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	/*
+	 * Volume is in the range 0-15. Make sure we send correct value
+	 * to remote device
+	 */
+	vol = cmd->volume > 15 ? 15 : cmd->volume;
+
+	switch (cmd->type) {
+	case HF_CLIENT_VOLUME_TYPE_SPEAKER:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+							"AT+VGS=%u", vol);
+		break;
+	case HF_CLIENT_VOLUME_TYPE_MIC:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+							"AT+VGM=%u", vol);
+		break;
+	default:
+		ret = false;
+		break;
+	}
+
+	status = ret ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_VOLUME_CONTROL,
+					status);
+}
+
+static void handle_dial(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_dial *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+	bool ret;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + cmd->number_len)
+		goto failed;
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (cmd->number_len > 0) {
+		if (cmd->number[cmd->number_len - 1] != '\0')
+			goto failed;
+
+		DBG("Dialing %s", cmd->number);
+
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+							"ATD%s;", cmd->number);
+	} else {
+		DBG("Redialing");
+
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+BLDN");
+	}
+
+	status =  ret ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+						HAL_OP_HF_CLIENT_DIAL, status);
+
+	return;
+
+failed:
+	error("Malformed number data, size (%u bytes), terminating", len);
+	raise(SIGTERM);
+}
+
+static void handle_dial_memory(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_dial_memory *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	/* For some reason location in BT HAL is int. Therefore that check */
+	if (cmd->location < 0) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL , "ATD>%d;",
+								cmd->location))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_DIAL_MEMORY, status);
+}
+
+static void handle_call_action(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_call_action *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+	bool ret;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	switch (cmd->action) {
+	case HAL_HF_CLIENT_ACTION_CHLD_0:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+CHLD=0");
+		break;
+	case HAL_HF_CLIENT_ACTION_CHLD_1:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+CHLD=1");
+		break;
+	case HAL_HF_CLIENT_ACTION_CHLD_2:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb,
+							NULL, "AT+CHLD=2");
+		break;
+	case HAL_HF_CLIENT_ACTION_CHLD_3:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+CHLD=3");
+		break;
+	case HAL_HF_CLIENT_ACTION_CHLD_4:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+CHLD=4");
+		break;
+	case HAL_HF_CLIENT_ACTION_CHLD_1x:
+		/* Index is int in BT HAL. Let's be paranoid here */
+		if (cmd->index <= 0)
+			ret = false;
+		else
+			ret = hfp_hf_send_command(dev->hf, cmd_complete_cb,
+					NULL, "AT+CHLD=1%d", cmd->index);
+		break;
+	case HAL_HF_CLIENT_ACTION_CHLD_2x:
+		/* Index is int in BT HAL. Let's be paranoid here */
+		if (cmd->index <= 0)
+			ret = false;
+		else
+			ret = hfp_hf_send_command(dev->hf, cmd_complete_cb,
+					NULL, "AT+CHLD=2%d", cmd->index);
+		break;
+	case HAL_HF_CLIENT_ACTION_ATA:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+									"ATA");
+		break;
+	case HAL_HF_CLIENT_ACTION_CHUP:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+CHUP");
+		break;
+	case HAL_HF_CLIENT_ACTION_BRTH_0:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+BTRH=0");
+		break;
+	case HAL_HF_CLIENT_ACTION_BRTH_1:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+BTRH=1");
+		break;
+	case HAL_HF_CLIENT_ACTION_BRTH_2:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+BTRH=2");
+		break;
+	default:
+		error("hf-client: Unknown action %d", cmd->action);
+		ret = false;
+		break;
+	}
+
+	status = ret ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_CALL_ACTION, status);
+}
+
+static void handle_query_current_calls(const void *buf, uint16_t len)
+{
+	struct device *dev;
+	uint8_t status;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CLCC"))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS,
+					status);
+}
+
+static void handle_query_operator_name(const void *buf, uint16_t len)
+{
+	struct device *dev;
+	uint8_t status;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+COPS?"))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME,
+					status);
+}
+
+static void handle_retrieve_subscr_info(const void *buf, uint16_t len)
+{
+	struct device *dev;
+	uint8_t status;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CNUM"))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO,
+					status);
+}
+
+static void handle_send_dtmf(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_send_dtmf *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+VTS=%c",
+							(char) cmd->tone))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_SEND_DTMF, status);
+}
+
+static void handle_get_last_vc_tag_num(const void *buf, uint16_t len)
+{
+	struct device *dev;
+	uint8_t status;
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BINP=1"))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM, status);
+}
+
+static void disconnect_watch(void *user_data)
+{
+	DBG("");
+
+	device_destroy(user_data);
+}
+
+static void slc_error(struct device *dev)
+{
+	error("hf-client: Could not create SLC - dropping connection");
+	hfp_hf_disconnect(dev->hf);
+}
+
+static void set_chld_feat(struct device *dev, char *feat)
+{
+	DBG(" %s", feat);
+
+	if (strcmp(feat, "0") == 0)
+		dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_REL;
+	else if (strcmp(feat, "1") == 0)
+		dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_REL_ACC;
+	else if (strcmp(feat, "1x") == 0)
+		dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_REL_X;
+	else if (strcmp(feat, "2") == 0)
+		dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_HOLD_ACC;
+	else if (strcmp(feat, "2x") == 0)
+		dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_PRIV_X;
+	else if (strcmp(feat, "3") == 0)
+		dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_MERGE;
+	else if (strcmp(feat, "4") == 0)
+		dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_MERGE_DETACH;
+}
+
+static void get_local_codecs_string(struct device *dev, char *buf,
+								uint8_t len)
+{
+	int i;
+	uint8_t offset;
+
+	memset(buf, 0, len);
+	offset = 0;
+
+	for (i = 0; i < CODECS_COUNT; i++) {
+		char c[8];
+		int l;
+
+		if (!dev->codecs[i].local_supported)
+			continue;
+
+		memset(c, 0, sizeof(c));
+
+		l = sprintf(c, "%d,", dev->codecs[i].type);
+
+		if (l > (len - offset - 1)) {
+			error("hf-client: Codecs cannot fit into buffer");
+			return;
+		}
+
+		strcat(&buf[offset], c);
+		offset += l;
+	}
+}
+
+static void bvra_cb(struct hfp_context *context, void *user_data)
+{
+	struct hal_ev_hf_client_vr_state ev;
+	unsigned int val;
+
+	if (!hfp_context_get_number(context, &val) || val > 1)
+		return;
+
+	ev.state = val ? HAL_HF_CLIENT_VR_STARTED : HAL_HF_CLIENT_VR_STOPPED;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_EV_HF_CLIENT_VR_STATE, sizeof(ev), &ev);
+}
+
+static void vgm_cb(struct hfp_context *context, void *user_data)
+{
+	struct hal_ev_hf_client_volume_changed ev;
+	unsigned int val;
+
+	if (!hfp_context_get_number(context, &val) || val > 15)
+		return;
+
+	ev.type = HF_CLIENT_VOLUME_TYPE_MIC;
+	ev.volume = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_EV_HF_CLIENT_VR_STATE, sizeof(ev), &ev);
+}
+
+static void vgs_cb(struct hfp_context *context, void *user_data)
+{
+	struct hal_ev_hf_client_volume_changed ev;
+	unsigned int val;
+
+	if (!hfp_context_get_number(context, &val) || val > 15)
+		return;
+
+	ev.type = HF_CLIENT_VOLUME_TYPE_SPEAKER;
+	ev.volume = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_EV_CLIENT_VOLUME_CHANGED, sizeof(ev), &ev);
+}
+
+static void brth_cb(struct hfp_context *context, void *user_data)
+{
+	struct hal_ev_hf_client_response_and_hold_status ev;
+	unsigned int val;
+
+	DBG("");
+
+	if (!hfp_context_get_number(context, &val) ||
+			val > HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_REJECT) {
+		error("hf-client: incorrect BTRH response ");
+		return;
+	}
+
+	ev.status = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_EV_HF_CLIENT_RESPONSE_AND_HOLD_STATUS,
+				sizeof(ev), &ev);
+}
+
+static void clcc_cb(struct hfp_context *context, void *user_data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_hf_client_current_call *ev = (void *) buf;
+	unsigned int val;
+
+	DBG("");
+
+	memset(buf, 0, sizeof(buf));
+
+	if (!hfp_context_get_number(context, &val)) {
+		error("hf-client: Could not get index");
+		return;
+	}
+
+	ev->index = val;
+
+	if (!hfp_context_get_number(context, &val) ||
+				val > HAL_HF_CLIENT_DIRECTION_INCOMING) {
+		error("hf-client: Could not get direction");
+		return;
+	}
+
+	ev->direction = val;
+
+	if (!hfp_context_get_number(context, &val) ||
+			val > HAL_HF_CLIENT_CALL_STATE_HELD_BY_RESP_AND_HOLD) {
+		error("hf-client: Could not get callstate");
+		return;
+	}
+
+	ev->call_state = val;
+
+	/* Next field is MODE but Android is not interested in this. Skip it */
+	if (!hfp_context_get_number(context, &val)) {
+		error("hf-client: Could not get mode");
+		return;
+	}
+
+	if (!hfp_context_get_number(context, &val) || val > 1) {
+		error("hf-client: Could not get multiparty");
+		return;
+	}
+
+	ev->multiparty = val;
+
+	if (hfp_context_get_string(context, (char *) &ev->number[0],
+								MAX_NUMBER_LEN))
+		ev->number_len = strlen((char *) ev->number) + 1;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_HF_CLIENT_CURRENT_CALL,
+					sizeof(*ev) + ev->number_len, ev);
+}
+
+static void ciev_cb(struct hfp_context *context, void *user_data)
+{
+	struct device *dev = user_data;
+	unsigned int index, val;
+	int i;
+
+	DBG("");
+
+	if (!hfp_context_get_number(context, &index))
+		return;
+
+	if (!hfp_context_get_number(context, &val))
+		return;
+
+	for (i = 0; i < HFP_INDICATOR_LAST; i++) {
+		if (dev->ag_ind[i].index != index)
+			continue;
+
+		if (dev->ag_ind[i].cb) {
+			dev->ag_ind[i].val = val;
+			dev->ag_ind[i].cb(val);
+			return;
+		}
+	}
+}
+
+static void cnum_cb(struct hfp_context *context, void *user_data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_hf_client_subscriber_service_info *ev = (void *) buf;
+	unsigned int service;
+
+	DBG("");
+
+	/* Alpha field is empty string, just skip it */
+	hfp_context_skip_field(context);
+
+	if (!hfp_context_get_string(context, (char *) &ev->name[0],
+							MAX_NUMBER_LEN)) {
+		error("hf-client: Could not get number");
+		return;
+	}
+
+	ev->name_len = strlen((char *) &ev->name[0]) + 1;
+
+	/* Type is not used in Android */
+	hfp_context_skip_field(context);
+
+	/* Speed field is empty string, just skip it */
+	hfp_context_skip_field(context);
+
+	if (!hfp_context_get_number(context, &service))
+		return;
+
+	switch (service) {
+	case 4:
+		ev->type = HAL_HF_CLIENT_SUBSCR_TYPE_VOICE;
+		break;
+	case 5:
+		ev->type = HAL_HF_CLIENT_SUBSCR_TYPE_FAX;
+		break;
+	default:
+		ev->type = HAL_HF_CLIENT_SUBSCR_TYPE_UNKNOWN;
+		break;
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_CLIENT_SUBSCRIBER_SERVICE_INFO,
+					sizeof(*ev) + ev->name_len, ev);
+}
+
+static void cops_cb(struct hfp_context *context, void *user_data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_hf_client_operator_name *ev = (void *) buf;
+	unsigned int format;
+
+	DBG("");
+
+	/* Not interested in mode */
+	hfp_context_skip_field(context);
+
+	if (!hfp_context_get_number(context, &format))
+		return;
+
+	if (format != 0)
+		info("hf-client: Not correct string format in +COSP");
+
+	if (!hfp_context_get_string(context, (char *) &ev->name[0],
+						MAX_OPERATOR_NAME_LEN)) {
+		error("hf-client: incorrect COPS response");
+		return;
+	}
+
+	ev->name_len = strlen((char *) &ev->name[0]) + 1;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_HF_CLIENT_OPERATOR_NAME,
+					sizeof(*ev) + ev->name_len, ev);
+}
+
+static void binp_cb(struct hfp_context *context, void *user_data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_hf_client_last_void_call_tag_num *ev = (void *) buf;
+	char number[33];
+
+	DBG("");
+
+	if (!hfp_context_get_string(context, number, sizeof(number))) {
+		error("hf-client: incorrect COPS response");
+		return;
+	}
+
+	ev->number_len = strlen(number) + 1;
+	memcpy(ev->number, number, ev->number_len);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_CLIENT_LAST_VOICE_CALL_TAG_NUM,
+					sizeof(*ev) + ev->number_len, ev);
+}
+
+static bool is_codec_supported_localy(struct device *dev, uint8_t codec)
+{
+	int i;
+
+	for (i = 0; i < CODECS_COUNT; i++) {
+		if (dev->codecs[i].type != codec)
+			continue;
+
+		return dev->codecs[i].local_supported;
+	}
+
+	return false;
+}
+
+static void bcs_resp(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	if (result != HFP_RESULT_OK)
+		error("hf-client: Error on AT+BCS (err=%u)", result);
+}
+
+static void bcs_cb(struct hfp_context *context, void *user_data)
+{
+	struct device *dev = user_data;
+	unsigned int codec;
+	char codecs_string[8];
+
+	DBG("");
+
+	if (!hfp_context_get_number(context, &codec))
+		goto failed;
+
+	if (!is_codec_supported_localy(dev, codec))
+		goto failed;
+
+	dev->negotiated_codec = codec;
+
+	hfp_hf_send_command(dev->hf, bcs_resp, dev, "AT+BCS=%u", codec);
+
+	return;
+
+failed:
+	error("hf-client: Could not get codec");
+
+	get_local_codecs_string(dev, codecs_string, sizeof(codecs_string));
+
+	hfp_hf_send_command(dev->hf, bcs_resp, dev, "AT+BCS=%s", codecs_string);
+}
+
+static void slc_completed(struct device *dev)
+{
+	int i;
+	struct indicator *ag_ind;
+
+	DBG("");
+
+	ag_ind = dev->ag_ind;
+
+	device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED);
+
+	/* Notify Android with indicators */
+	for (i = 0; i < HFP_INDICATOR_LAST; i++) {
+		if (!ag_ind[i].cb)
+			continue;
+
+		ag_ind[i].cb(ag_ind[i].val);
+	}
+
+	/* TODO: register unsolicited results handlers */
+
+	hfp_hf_register(dev->hf, bvra_cb, "+BRVA", dev, NULL);
+	hfp_hf_register(dev->hf, vgm_cb, "+VGM", dev, NULL);
+	hfp_hf_register(dev->hf, vgs_cb, "+VGS", dev, NULL);
+	hfp_hf_register(dev->hf, brth_cb, "+BTRH", dev, NULL);
+	hfp_hf_register(dev->hf, clcc_cb, "+CLCC", dev, NULL);
+	hfp_hf_register(dev->hf, ciev_cb, "+CIEV", dev, NULL);
+	hfp_hf_register(dev->hf, cops_cb, "+COPS", dev, NULL);
+	hfp_hf_register(dev->hf, cnum_cb, "+CNUM", dev, NULL);
+	hfp_hf_register(dev->hf, binp_cb, "+BINP", dev, NULL);
+	hfp_hf_register(dev->hf, bcs_cb, "+BCS", dev, NULL);
+
+	if (!hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+COPS=3,0"))
+		info("hf-client: Could not send AT+COPS=3,0");
+}
+
+static void slc_chld_cb(struct hfp_context *context, void *user_data)
+{
+	struct device *dev = user_data;
+	char feat[3];
+
+	if (!hfp_context_open_container(context))
+		goto failed;
+
+	while (hfp_context_get_unquoted_string(context, feat, sizeof(feat)))
+		set_chld_feat(dev, feat);
+
+	if (!hfp_context_close_container(context))
+		goto failed;
+
+	return;
+
+failed:
+	error("hf-client: Error on CHLD response");
+	slc_error(dev);
+}
+
+static void slc_chld_resp(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	struct device *dev = user_data;
+
+	DBG("");
+
+	hfp_hf_unregister(dev->hf, "+CHLD");
+
+	if (result != HFP_RESULT_OK) {
+		error("hf-client: CHLD error: %d", result);
+		slc_error(dev);
+		return;
+	}
+
+	slc_completed(dev);
+}
+
+static void slc_cmer_resp(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	struct device *dev = user_data;
+
+	DBG("");
+
+	if (result != HFP_RESULT_OK) {
+		error("hf-client: CMER error: %d", result);
+		goto failed;
+	}
+
+	/* Continue with SLC creation */
+	if (!(dev->features & HFP_AG_FEAT_3WAY)) {
+		slc_completed(dev);
+		return;
+	}
+
+	if (!hfp_hf_register(dev->hf, slc_chld_cb, "+CHLD", dev, NULL)) {
+		error("hf-client: Could not register +CHLD");
+		goto failed;
+	}
+
+	if (!hfp_hf_send_command(dev->hf, slc_chld_resp, dev, "AT+CHLD=?")) {
+		error("hf-client: Could not send AT+CHLD");
+		goto failed;
+	}
+
+	return;
+
+failed:
+	slc_error(dev);
+}
+
+static void set_indicator_value(uint8_t index, unsigned int val,
+						struct indicator *ag_ind)
+{
+	int i;
+
+	for (i = 0; i < HFP_INDICATOR_LAST; i++) {
+		if (index != ag_ind[i].index)
+			continue;
+
+		ag_ind[i].val = val;
+		ag_ind[i].cb(val);
+		return;
+	}
+}
+
+static void slc_cind_status_cb(struct hfp_context *context,
+							void *user_data)
+{
+	struct device *dev = user_data;
+	uint8_t index = 1;
+
+	DBG("");
+
+	while (hfp_context_has_next(context)) {
+		uint32_t val;
+
+		if (!hfp_context_get_number(context, &val)) {
+			error("hf-client: Error on CIND status response");
+			return;
+		}
+
+		set_indicator_value(index++, val, dev->ag_ind);
+	}
+}
+
+static void slc_cind_status_resp(enum hfp_result result,
+							enum hfp_error cme_err,
+							void *user_data)
+{
+	struct device *dev = user_data;
+
+	DBG("");
+
+	hfp_hf_unregister(dev->hf, "+CIND");
+
+	if (result != HFP_RESULT_OK) {
+		error("hf-client: CIND error: %d", result);
+		goto failed;
+	}
+
+	/* Continue with SLC creation */
+	if (!hfp_hf_send_command(dev->hf, slc_cmer_resp, dev,
+							"AT+CMER=3,0,0,1")) {
+		error("hf-client: Counld not send AT+CMER");
+		goto failed;
+	}
+
+	return;
+
+failed:
+	slc_error(dev);
+}
+
+static void slc_cind_resp(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	struct device *dev = user_data;
+
+	DBG("");
+
+	hfp_hf_unregister(dev->hf, "+CIND");
+
+	if (result != HFP_RESULT_OK) {
+		error("hf-client: CIND error: %d", result);
+		goto failed;
+	}
+
+	/* Continue with SLC creation */
+	if (!hfp_hf_register(dev->hf, slc_cind_status_cb, "+CIND", dev,
+								NULL)) {
+		error("hf-client: Counld not register +CIND");
+		goto failed;
+	}
+
+	if (!hfp_hf_send_command(dev->hf, slc_cind_status_resp, dev,
+								"AT+CIND?")) {
+		error("hf-client: Counld not send AT+CIND?");
+		goto failed;
+	}
+
+	return;
+
+failed:
+	slc_error(dev);
+}
+
+static void ciev_service_cb(uint8_t val)
+{
+	struct hal_ev_hf_client_net_state ev;
+
+	DBG("");
+
+	if (val > HAL_HF_CLIENT_NET_ROAMING_TYPE_ROAMING) {
+		error("hf-client: Incorrect state %u:", val);
+		return;
+	}
+
+	ev.state = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_EV_HF_CLIENT_NET_STATE, sizeof(ev), &ev);
+}
+
+static void ciev_call_cb(uint8_t val)
+{
+	struct hal_ev_hf_client_call_indicator ev;
+
+	DBG("");
+
+	if (val > HAL_HF_CLIENT_CALL_IND_CALL_IN_PROGERSS) {
+		error("hf-client: Incorrect call state %u:", val);
+		return;
+	}
+
+	ev.call = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_EV_HF_CLIENT_CALL_INDICATOR, sizeof(ev), &ev);
+}
+
+static void ciev_callsetup_cb(uint8_t val)
+{
+	struct hal_ev_hf_client_call_setup_indicator ev;
+
+	DBG("");
+
+	if (val > HAL_HF_CLIENT_CALL_SETUP_ALERTING) {
+		error("hf-client: Incorrect call setup state %u:", val);
+		return;
+	}
+
+	ev.call_setup = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_HF_CLIENT_CALL_SETUP_INDICATOR,
+					sizeof(ev), &ev);
+}
+
+static void ciev_callheld_cb(uint8_t val)
+{
+	struct hal_ev_hf_client_call_held_indicator ev;
+
+	DBG("");
+
+	if (val > HAL_HF_CLIENT_CALL_SETUP_IND_HOLD) {
+		error("hf-client: Incorrect call held state %u:", val);
+		return;
+	}
+
+	ev.call_held = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_HF_CLIENT_CALL_HELD_INDICATOR,
+					sizeof(ev), &ev);
+}
+
+static void ciev_signal_cb(uint8_t val)
+{
+	struct hal_ev_hf_client_net_signal_strength ev;
+
+	DBG("");
+
+	if (val > 5) {
+		error("hf-client: Incorrect signal value %u:", val);
+		return;
+	}
+
+	ev.signal_strength = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_HF_CLIENT_NET_SIGNAL_STRENGTH,
+					sizeof(ev), &ev);
+}
+
+static void ciev_roam_cb(uint8_t val)
+{
+	struct hal_ev_hf_client_net_roaming_type ev;
+
+	DBG("");
+
+	if (val > HAL_HF_CLIENT_NET_ROAMING_TYPE_ROAMING) {
+		error("hf-client: Incorrect roaming state %u:", val);
+		return;
+	}
+
+	ev.state = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_HF_CLIENT_NET_ROAMING_TYPE,
+					sizeof(ev), &ev);
+}
+
+static void ciev_battchg_cb(uint8_t val)
+{
+	struct hal_ev_hf_client_battery_level ev;
+
+	DBG("");
+
+	if (val > 5) {
+		error("hf-client: Incorrect battery charge value %u:", val);
+		return;
+	}
+
+	ev.battery_level = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_EV_HF_CLIENT_BATTERY_LEVEL, sizeof(ev), &ev);
+}
+
+static void set_indicator_parameters(uint8_t index, const char *indicator,
+						unsigned int min,
+						unsigned int max,
+						struct indicator *ag_ind)
+{
+	DBG("%s, %i", indicator, index);
+
+	/* TODO: Verify min/max values ? */
+
+	if (strcmp("service", indicator) == 0) {
+		ag_ind[HFP_INDICATOR_SERVICE].index = index;
+		ag_ind[HFP_INDICATOR_SERVICE].min = min;
+		ag_ind[HFP_INDICATOR_SERVICE].max = max;
+		ag_ind[HFP_INDICATOR_SERVICE].cb = ciev_service_cb;
+		return;
+	}
+
+	if (strcmp("call", indicator) == 0) {
+		ag_ind[HFP_INDICATOR_CALL].index = index;
+		ag_ind[HFP_INDICATOR_CALL].min = min;
+		ag_ind[HFP_INDICATOR_CALL].max = max;
+		ag_ind[HFP_INDICATOR_CALL].cb = ciev_call_cb;
+		return;
+	}
+
+	if (strcmp("callsetup", indicator) == 0) {
+		ag_ind[HFP_INDICATOR_CALLSETUP].index = index;
+		ag_ind[HFP_INDICATOR_CALLSETUP].min = min;
+		ag_ind[HFP_INDICATOR_CALLSETUP].max = max;
+		ag_ind[HFP_INDICATOR_CALLSETUP].cb = ciev_callsetup_cb;
+		return;
+	}
+
+	if (strcmp("callheld", indicator) == 0) {
+		ag_ind[HFP_INDICATOR_CALLHELD].index = index;
+		ag_ind[HFP_INDICATOR_CALLHELD].min = min;
+		ag_ind[HFP_INDICATOR_CALLHELD].max = max;
+		ag_ind[HFP_INDICATOR_CALLHELD].cb = ciev_callheld_cb;
+		return;
+	}
+
+	if (strcmp("signal", indicator) == 0) {
+		ag_ind[HFP_INDICATOR_SIGNAL].index = index;
+		ag_ind[HFP_INDICATOR_SIGNAL].min = min;
+		ag_ind[HFP_INDICATOR_SIGNAL].max = max;
+		ag_ind[HFP_INDICATOR_SIGNAL].cb = ciev_signal_cb;
+		return;
+	}
+
+	if (strcmp("roam", indicator) == 0) {
+		ag_ind[HFP_INDICATOR_ROAM].index = index;
+		ag_ind[HFP_INDICATOR_ROAM].min = min;
+		ag_ind[HFP_INDICATOR_ROAM].max = max;
+		ag_ind[HFP_INDICATOR_ROAM].cb = ciev_roam_cb;
+		return;
+	}
+
+	if (strcmp("battchg", indicator) == 0) {
+		ag_ind[HFP_INDICATOR_BATTCHG].index = index;
+		ag_ind[HFP_INDICATOR_BATTCHG].min = min;
+		ag_ind[HFP_INDICATOR_BATTCHG].max = max;
+		ag_ind[HFP_INDICATOR_BATTCHG].cb = ciev_battchg_cb;
+		return;
+	}
+
+	error("hf-client: Unknown indicator: %s", indicator);
+}
+
+static void slc_cind_cb(struct hfp_context *context, void *user_data)
+{
+	struct device *dev = user_data;
+	int index = 1;
+
+	DBG("");
+
+	while (hfp_context_has_next(context)) {
+		char name[255];
+		unsigned int min, max;
+
+		/* e.g ("callsetup",(0-3)) */
+		if (!hfp_context_open_container(context))
+			break;
+
+		if (!hfp_context_get_string(context, name, sizeof(name))) {
+			error("hf-client: Could not get string");
+			goto failed;
+		}
+
+		if (!hfp_context_open_container(context)) {
+			error("hf-client: Could not open container");
+			goto failed;
+		}
+
+		if (!hfp_context_get_range(context, &min, &max)) {
+			if (!hfp_context_get_number(context, &min)) {
+				error("hf-client: Could not get number");
+				goto failed;
+			}
+
+			if (!hfp_context_get_number(context, &max)) {
+				error("hf-client: Could not get number");
+				goto failed;
+			}
+		}
+
+		if (!hfp_context_close_container(context)) {
+			error("hf-client: Could not close container");
+			goto failed;
+		}
+
+		if (!hfp_context_close_container(context)) {
+			error("hf-client: Could not close container");
+			goto failed;
+		}
+
+		set_indicator_parameters(index, name, min, max, dev->ag_ind);
+		index++;
+	}
+
+	return;
+
+failed:
+	error("hf-client: Error on CIND response");
+	slc_error(dev);
+}
+
+static void slc_bac_resp(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	struct device *dev = user_data;
+
+	DBG("");
+
+	if (result != HFP_RESULT_OK)
+		goto failed;
+
+	/* Continue with SLC creation */
+	if (!hfp_hf_register(dev->hf, slc_cind_cb, "+CIND", dev, NULL)) {
+		error("hf-client: Could not register for +CIND");
+		goto failed;
+	}
+
+	if (!hfp_hf_send_command(dev->hf, slc_cind_resp, dev, "AT+CIND=?"))
+		goto failed;
+
+	return;
+
+failed:
+	error("hf-client: Error on BAC response");
+	slc_error(dev);
+}
+
+static bool send_supported_codecs(struct device *dev)
+{
+	char codecs_string[8];
+	char bac[16];
+
+	memset(bac, 0, sizeof(bac));
+
+	strcpy(bac, "AT+BAC=");
+
+	get_local_codecs_string(dev, codecs_string, sizeof(codecs_string));
+	strcat(bac, codecs_string);
+
+	return hfp_hf_send_command(dev->hf, slc_bac_resp, dev, bac);
+}
+
+static void slc_brsf_cb(struct hfp_context *context, void *user_data)
+{
+	unsigned int feat;
+	struct device *dev = user_data;
+
+	DBG("");
+
+	if (hfp_context_get_number(context, &feat))
+		dev->features = feat;
+}
+
+static void slc_brsf_resp(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	struct device *dev = user_data;
+
+	hfp_hf_unregister(dev->hf, "+BRSF");
+
+	if (result != HFP_RESULT_OK) {
+		error("hf-client: BRSF error: %d", result);
+		goto failed;
+	}
+
+	/* Continue with SLC creation */
+	if (codec_negotiation_supported(dev)) {
+		if (send_supported_codecs(dev))
+			return;
+
+		error("hf-client: Could not send BAC command");
+		goto failed;
+	}
+
+	/* No WBS on remote side. Continue with indicators */
+	if (!hfp_hf_register(dev->hf, slc_cind_cb, "+CIND", dev, NULL)) {
+		error("hf-client: Could not register for +CIND");
+		goto failed;
+	}
+
+	if (!hfp_hf_send_command(dev->hf, slc_cind_resp, dev, "AT+CIND=?")) {
+		error("hf-client: Could not send AT+CIND command");
+		goto failed;
+	}
+
+	return;
+
+failed:
+	slc_error(dev);
+}
+
+static bool create_slc(struct device *dev)
+{
+	DBG("");
+
+	if (!hfp_hf_register(dev->hf, slc_brsf_cb, "+BRSF", dev, NULL))
+		return false;
+
+	return hfp_hf_send_command(dev->hf, slc_brsf_resp, dev, "AT+BRSF=%u",
+							hfp_hf_features);
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct device *dev = user_data;
+
+	DBG("");
+
+	if (err) {
+		error("hf-client: connect failed (%s)", err->message);
+		goto failed;
+	}
+
+	dev->hf = hfp_hf_new(g_io_channel_unix_get_fd(chan));
+	if (!dev->hf) {
+		error("hf-client: Could not create hfp io");
+		goto failed;
+	}
+
+	g_io_channel_set_close_on_unref(chan, FALSE);
+
+	hfp_hf_set_close_on_unref(dev->hf, true);
+	hfp_hf_set_disconnect_handler(dev->hf, disconnect_watch, dev, NULL);
+
+	if (!create_slc(dev)) {
+		error("hf-client: Could not start SLC creation");
+		hfp_hf_disconnect(dev->hf);
+		goto failed;
+	}
+
+	device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_CONNECTED);
+
+	return;
+
+failed:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+	device_destroy(dev);
+}
+
+static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	sdp_list_t *protos, *classes;
+	struct device *dev = data;
+	GError *gerr = NULL;
+	GIOChannel *io;
+	uuid_t uuid;
+	int channel;
+
+	DBG("");
+
+	if (err < 0) {
+		error("hf-client: unable to get SDP record: %s",
+							strerror(-err));
+		goto failed;
+	}
+
+	if (!recs || !recs->data) {
+		info("hf-client: no HFP SDP records found");
+		goto failed;
+	}
+
+	if (sdp_get_service_classes(recs->data, &classes) < 0 || !classes) {
+		error("hf-client: unable to get service classes from record");
+		goto failed;
+	}
+
+	/* TODO read remote version? */
+
+	memcpy(&uuid, classes->data, sizeof(uuid));
+	sdp_list_free(classes, free);
+
+	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
+			uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
+		error("hf-client: invalid service record or not HFP");
+		goto failed;
+	}
+
+	if (sdp_get_access_protos(recs->data, &protos) < 0) {
+		error("hf-client: unable to get access protocols from record");
+		sdp_list_free(classes, free);
+		goto failed;
+	}
+
+	channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+	sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+	sdp_list_free(protos, NULL);
+	if (channel <= 0) {
+		error("hf-client: unable to get RFCOMM channel from record");
+		goto failed;
+	}
+
+	io = bt_io_connect(connect_cb, dev, NULL, &gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_CHANNEL, channel,
+				BT_IO_OPT_INVALID);
+	if (!io) {
+		error("hf-client: unable to connect: %s", gerr->message);
+		g_error_free(gerr);
+		goto failed;
+	}
+
+	g_io_channel_unref(io);
+	return;
+
+failed:
+	device_destroy(dev);
+}
+
+static int sdp_search_hfp(struct device *dev)
+{
+	uuid_t uuid;
+
+	sdp_uuid16_create(&uuid, HANDSFREE_AGW_SVCLASS_ID);
+
+	return bt_search_service(&adapter_addr, &dev->bdaddr, &uuid,
+					sdp_hfp_search_cb, dev, NULL, 0);
+}
+
+static void handle_connect(const void *buf, uint16_t len)
+{
+	struct device *dev;
+	const struct hal_cmd_hf_client_connect *cmd = buf;
+	uint32_t status;
+	bdaddr_t bdaddr;
+	char addr[18];
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+	ba2str(&bdaddr, addr);
+	DBG("connecting to %s", addr);
+
+	dev = get_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (dev->state != HAL_HF_CLIENT_CONN_STATE_DISCONNECTED) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (sdp_search_hfp(dev) < 0) {
+		status = HAL_STATUS_FAILED;
+		device_destroy(dev);
+		goto done;
+	}
+
+	device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_CONNECTING);
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_CONNECT, status);
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+	struct device *dev;
+	char address[18];
+	bdaddr_t bdaddr;
+	GError *err = NULL;
+
+	bt_io_get(chan, &err,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_DEST_BDADDR, &bdaddr,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("hf-client: confirm failed (%s)", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	DBG("Incoming connection from %s", address);
+
+	dev = get_device(&bdaddr);
+	if (!dev) {
+		error("hf-client: There is other AG connected");
+		goto drop;
+	}
+
+	if (dev->state != HAL_HF_CLIENT_CONN_STATE_DISCONNECTED) {
+		/* TODO: Handle colision */
+		error("hf-client: Connections is up or ongoing ?");
+		goto drop;
+	}
+
+	device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_CONNECTING);
+
+	if (!bt_io_accept(chan, connect_cb, dev, NULL, NULL)) {
+		error("hf-client: failed to accept connection");
+		device_destroy(dev);
+		goto drop;
+	}
+
+	return;
+
+drop:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_HF_CLIENT_CONNECT */
+	{ handle_connect, false,
+				sizeof(struct hal_cmd_hf_client_connect) },
+	/* HAL_OP_HF_CLIENT_DISCONNECT */
+	{ handle_disconnect, false,
+				sizeof(struct hal_cmd_hf_client_disconnect) },
+	/* HAL_OP_HF_CLIENT_CONNECT_AUDIO */
+	{ handle_connect_audio, false,
+			sizeof(struct hal_cmd_hf_client_connect_audio) },
+	/* HAL_OP_HF_CLIENT_DISCONNECT_AUDIO */
+	{ handle_disconnect_audio, false,
+			sizeof(struct hal_cmd_hf_client_disconnect_audio) },
+	/* define HAL_OP_HF_CLIENT_START_VR */
+	{ handle_start_vr, false, 0 },
+	/* define HAL_OP_HF_CLIENT_STOP_VR */
+	{ handle_stop_vr, false, 0 },
+	/* HAL_OP_HF_CLIENT_VOLUME_CONTROL */
+	{ handle_volume_control, false,
+			sizeof(struct hal_cmd_hf_client_volume_control) },
+	/* HAL_OP_HF_CLIENT_DIAL */
+	{ handle_dial, true, sizeof(struct hal_cmd_hf_client_dial) },
+	/* HAL_OP_HF_CLIENT_DIAL_MEMORY */
+	{ handle_dial_memory, false,
+				sizeof(struct hal_cmd_hf_client_dial_memory) },
+	/* HAL_OP_HF_CLIENT_CALL_ACTION */
+	{ handle_call_action, false,
+				sizeof(struct hal_cmd_hf_client_call_action) },
+	/* HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS */
+	{ handle_query_current_calls, false, 0 },
+	/* HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME */
+	{ handle_query_operator_name, false, 0 },
+	/* HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO */
+	{ handle_retrieve_subscr_info, false, 0 },
+	/* HAL_OP_HF_CLIENT_SEND_DTMF */
+	{ handle_send_dtmf, false,
+				sizeof(struct hal_cmd_hf_client_send_dtmf) },
+	/* HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM */
+	{ handle_get_last_vc_tag_num, false, 0 },
+};
+
+static sdp_record_t *hfp_hf_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+	uuid_t l2cap_uuid, rfcomm_uuid;
+	sdp_profile_desc_t profile;
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t *record;
+	sdp_data_t *channel, *features;
+	uint16_t sdpfeat;
+	uint8_t ch = HFP_HF_CHANNEL;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &svclass_uuid);
+	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+	profile.version = 0x0106;
+	pfseq = sdp_list_append(NULL, &profile);
+	sdp_set_profile_descs(record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &ch);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	/* Codec Negotiation bit in SDP feature is different then in BRSF */
+	sdpfeat = hfp_hf_features & 0x0000003F;
+	if (hfp_hf_features & HFP_HF_FEAT_CODEC)
+		sdpfeat |= 0x00000020;
+	else
+		sdpfeat &= ~0x00000020;
+
+	features = sdp_data_alloc(SDP_UINT16, &sdpfeat);
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_set_info_attr(record, "Hands-Free unit", NULL, NULL);
+
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static bool enable_hf_client(void)
+{
+	sdp_record_t *rec;
+	GError *err = NULL;
+
+	hfp_hf_server =  bt_io_listen(NULL, confirm_cb, NULL, NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_CHANNEL, HFP_HF_CHANNEL,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_INVALID);
+	if (!hfp_hf_server) {
+		error("hf-client: Failed to listen on Handsfree rfcomm: %s",
+								err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	hfp_hf_features = HFP_HF_FEATURES;
+
+	rec = hfp_hf_record();
+	if (!rec) {
+		error("hf-client: Could not create service record");
+		goto failed;
+	}
+
+	if (bt_adapter_add_record(rec, 0) < 0) {
+		error("hf-client: Failed to register service record");
+		sdp_record_free(rec);
+		goto failed;
+	}
+
+	hfp_hf_record_id = rec->handle;
+
+	return true;
+
+failed:
+	g_io_channel_shutdown(hfp_hf_server, TRUE, NULL);
+	g_io_channel_unref(hfp_hf_server);
+	hfp_hf_server = NULL;
+
+	return false;
+}
+
+static void cleanup_hfp_hf(void)
+{
+	if (hfp_hf_server) {
+		g_io_channel_shutdown(hfp_hf_server, TRUE, NULL);
+		g_io_channel_unref(hfp_hf_server);
+		hfp_hf_server = NULL;
+	}
+
+	if (hfp_hf_record_id > 0) {
+		bt_adapter_remove_record(hfp_hf_record_id);
+		hfp_hf_record_id = 0;
+	}
+
+	if (sco) {
+		bt_sco_unref(sco);
+		sco = NULL;
+	}
+}
+
+static bool confirm_sco_cb(const bdaddr_t *addr, uint16_t *voice_settings)
+{
+	struct device *dev;
+
+	DBG("");
+
+	dev = find_device(addr);
+	if (!dev || dev->state != HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED) {
+		error("hf-client: No device or SLC not ready");
+		return false;
+	}
+
+	set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_CONNECTING);
+
+	if (codec_negotiation_supported(dev) &&
+			dev->negotiated_codec != CODEC_ID_CVSD)
+		*voice_settings = BT_VOICE_TRANSPARENT;
+	else
+		*voice_settings = BT_VOICE_CVSD_16BIT;
+
+	return true;
+}
+
+static void connect_sco_cb(enum sco_status status, const bdaddr_t *addr)
+{
+	struct device *dev;
+	uint8_t audio_state;
+
+	DBG("SCO Status %u", status);
+
+	/* Device shall be there, just sanity check */
+	dev = find_device(addr);
+	if (!dev) {
+		error("hf-client: There is no device?");
+		return;
+	}
+
+	if (status != SCO_STATUS_OK) {
+		audio_state = HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED;
+		goto done;
+	}
+
+	if (dev->negotiated_codec == CODEC_ID_MSBC)
+		audio_state = HAL_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC;
+	else
+		audio_state = HAL_HF_CLIENT_AUDIO_STATE_CONNECTED;
+
+done:
+	set_audio_state(dev, audio_state);
+}
+
+static void disconnect_sco_cb(const bdaddr_t *addr)
+{
+	struct device *dev;
+
+	DBG("");
+
+	dev = find_device(addr);
+	if (!dev) {
+		error("hf-client: No device");
+		return;
+	}
+
+	set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED);
+}
+
+bool bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr)
+{
+	DBG("");
+
+	devices = queue_new();
+
+	bacpy(&adapter_addr, addr);
+
+	if (!enable_hf_client())
+		goto failed;
+
+	sco = bt_sco_new(addr);
+	if (!sco) {
+		error("hf-client: Cannot create SCO. HFP AG is in use ?");
+		goto failed;
+	}
+
+	bt_sco_set_confirm_cb(sco, confirm_sco_cb);
+	bt_sco_set_connect_cb(sco, connect_sco_cb);
+	bt_sco_set_disconnect_cb(sco, disconnect_sco_cb);
+
+	hal_ipc = ipc;
+	ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+
+failed:
+	cleanup_hfp_hf();
+	queue_destroy(devices, free);
+	devices = NULL;
+
+	return false;
+}
+
+void bt_hf_client_unregister(void)
+{
+	DBG("");
+
+	cleanup_hfp_hf();
+
+	queue_destroy(devices, (void *) device_destroy);
+	devices = NULL;
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_HANDSFREE);
+	hal_ipc = NULL;
+}
diff --git a/repo/android/handsfree-client.h b/repo/android/handsfree-client.h
new file mode 100644
index 0000000..1eb69ff
--- /dev/null
+++ b/repo/android/handsfree-client.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  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
+ *
+ */
+
+bool bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr);
+void bt_hf_client_unregister(void);
diff --git a/repo/android/handsfree.c b/repo/android/handsfree.c
new file mode 100644
index 0000000..cb348ab
--- /dev/null
+++ b/repo/android/handsfree.c
@@ -0,0 +1,3031 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/uuid-helper.h"
+#include "src/shared/hfp.h"
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+#include "btio/btio.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "handsfree.h"
+#include "bluetooth.h"
+#include "src/log.h"
+#include "utils.h"
+#include "sco-msg.h"
+#include "sco.h"
+
+#define HSP_AG_CHANNEL 12
+#define HFP_AG_CHANNEL 13
+
+#define HFP_AG_FEAT_3WAY	0x00000001
+#define HFP_AG_FEAT_ECNR	0x00000002
+#define HFP_AG_FEAT_VR		0x00000004
+#define HFP_AG_FEAT_INBAND	0x00000008
+#define HFP_AG_FEAT_VTAG	0x00000010
+#define HFP_AG_FEAT_REJ_CALL	0x00000020
+#define HFP_AG_FEAT_ECS		0x00000040
+#define HFP_AG_FEAT_ECC		0x00000080
+#define HFP_AG_FEAT_EXT_ERR	0x00000100
+#define HFP_AG_FEAT_CODEC	0x00000200
+
+#define HFP_HF_FEAT_ECNR	0x00000001
+#define HFP_HF_FEAT_3WAY	0x00000002
+#define HFP_HF_FEAT_CLI		0x00000004
+#define HFP_HF_FEAT_VR		0x00000008
+#define HFP_HF_FEAT_RVC		0x00000010
+#define HFP_HF_FEAT_ECS		0x00000020
+#define HFP_HF_FEAT_ECC		0x00000040
+#define HFP_HF_FEAT_CODEC	0x00000080
+
+#define HFP_AG_FEATURES (HFP_AG_FEAT_3WAY | HFP_AG_FEAT_ECNR |\
+				HFP_AG_FEAT_VR | HFP_AG_FEAT_REJ_CALL |\
+				HFP_AG_FEAT_ECS | HFP_AG_FEAT_EXT_ERR)
+
+#define HFP_AG_CHLD "0,1,2,3"
+
+/* offsets in indicators table, should be incremented when sending CIEV */
+#define IND_SERVICE	0
+#define IND_CALL	1
+#define IND_CALLSETUP	2
+#define IND_CALLHELD	3
+#define IND_SIGNAL	4
+#define IND_ROAM	5
+#define IND_BATTCHG	6
+#define IND_COUNT	(IND_BATTCHG + 1)
+
+#define RING_TIMEOUT 2
+
+#define CVSD_OFFSET 0
+#define MSBC_OFFSET 1
+#define CODECS_COUNT (MSBC_OFFSET + 1)
+
+#define CODEC_ID_CVSD 0x01
+#define CODEC_ID_MSBC 0x02
+
+struct indicator {
+	const char *name;
+	int min;
+	int max;
+	int val;
+	bool always_active;
+	bool active;
+};
+
+struct hfp_codec {
+	uint8_t type;
+	bool local_supported;
+	bool remote_supported;
+};
+
+struct hf_device {
+	bdaddr_t bdaddr;
+	uint8_t state;
+	uint8_t audio_state;
+	uint32_t features;
+
+	bool clip_enabled;
+	bool cmee_enabled;
+	bool ccwa_enabled;
+	bool indicators_enabled;
+	struct indicator inds[IND_COUNT];
+	int num_active;
+	int num_held;
+	int setup_state;
+	guint call_hanging_up;
+
+	uint8_t negotiated_codec;
+	uint8_t proposed_codec;
+	struct hfp_codec codecs[CODECS_COUNT];
+
+	guint ring;
+	char *clip;
+	bool hsp;
+
+	struct hfp_gw *gw;
+	guint delay_sco;
+};
+
+static const struct indicator inds_defaults[] = {
+		{ "service",   0, 1, 0, false, true },
+		{ "call",      0, 1, 0, true, true },
+		{ "callsetup", 0, 3, 0, true, true },
+		{ "callheld",  0, 2, 0, true, true },
+		{ "signal",    0, 5, 0, false, true },
+		{ "roam",      0, 1, 0, false, true },
+		{ "battchg",   0, 5, 0, false, true },
+};
+
+static const struct hfp_codec codecs_defaults[] = {
+	{ CODEC_ID_CVSD, true, false},
+	{ CODEC_ID_MSBC, false, false},
+};
+
+static struct queue *devices = NULL;
+
+static uint32_t hfp_ag_features = 0;
+
+static bdaddr_t adapter_addr;
+
+static struct ipc *hal_ipc = NULL;
+static struct ipc *sco_ipc = NULL;
+
+static uint32_t hfp_record_id = 0;
+static GIOChannel *hfp_server = NULL;
+
+static uint32_t hsp_record_id = 0;
+static GIOChannel *hsp_server = NULL;
+
+static struct bt_sco *sco = NULL;
+
+static unsigned int max_hfp_clients = 0;
+
+static void set_state(struct hf_device *dev, uint8_t state)
+{
+	struct hal_ev_handsfree_conn_state ev;
+	char address[18];
+
+	if (dev->state == state)
+		return;
+
+	dev->state = state;
+
+	ba2str(&dev->bdaddr, address);
+	DBG("device %s state %u", address, state);
+
+	bdaddr2android(&dev->bdaddr, ev.bdaddr);
+	ev.state = state;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_EV_HANDSFREE_CONN_STATE, sizeof(ev), &ev);
+}
+
+static void set_audio_state(struct hf_device *dev, uint8_t state)
+{
+	struct hal_ev_handsfree_audio_state ev;
+	char address[18];
+
+	if (dev->audio_state == state)
+		return;
+
+	dev->audio_state = state;
+
+	ba2str(&dev->bdaddr, address);
+	DBG("device %s audio state %u", address, state);
+
+	bdaddr2android(&dev->bdaddr, ev.bdaddr);
+	ev.state = state;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_EV_HANDSFREE_AUDIO_STATE, sizeof(ev), &ev);
+}
+
+static void init_codecs(struct hf_device *dev)
+{
+	memcpy(dev->codecs, codecs_defaults, sizeof(dev->codecs));
+
+	if (hfp_ag_features & HFP_AG_FEAT_CODEC)
+		dev->codecs[MSBC_OFFSET].local_supported = true;
+}
+
+static struct hf_device *device_create(const bdaddr_t *bdaddr)
+{
+	struct hf_device *dev;
+
+	dev = new0(struct hf_device, 1);
+
+	bacpy(&dev->bdaddr, bdaddr);
+	dev->setup_state = HAL_HANDSFREE_CALL_STATE_IDLE;
+	dev->state = HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED;
+	dev->audio_state = HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED;
+
+	memcpy(dev->inds, inds_defaults, sizeof(dev->inds));
+
+	init_codecs(dev);
+
+	queue_push_head(devices, dev);
+
+	return dev;
+}
+
+static void device_destroy(struct hf_device *dev)
+{
+	hfp_gw_unref(dev->gw);
+
+	if (dev->delay_sco)
+		g_source_remove(dev->delay_sco);
+
+	if (dev->audio_state == HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED)
+		bt_sco_disconnect(sco);
+
+	if (dev->ring)
+		g_source_remove(dev->ring);
+
+	g_free(dev->clip);
+
+	if (dev->call_hanging_up)
+		g_source_remove(dev->call_hanging_up);
+
+	set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
+	set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED);
+
+	queue_remove(devices, dev);
+	free(dev);
+}
+
+static bool match_by_bdaddr(const void *data, const void *match_data)
+{
+	const struct hf_device *dev = data;
+	const bdaddr_t *addr = match_data;
+
+	return !bacmp(&dev->bdaddr, addr);
+}
+
+static struct hf_device *find_device(const bdaddr_t *bdaddr)
+{
+	if (!bacmp(bdaddr, BDADDR_ANY))
+		return queue_peek_head(devices);
+
+	return queue_find(devices, match_by_bdaddr, bdaddr);
+}
+
+static struct hf_device *get_device(const bdaddr_t *bdaddr)
+{
+	struct hf_device *dev;
+
+	dev = find_device(bdaddr);
+	if (dev)
+		return dev;
+
+	if (queue_length(devices) == max_hfp_clients)
+		return NULL;
+
+	return device_create(bdaddr);
+}
+
+static void disconnect_watch(void *user_data)
+{
+	struct hf_device *dev = user_data;
+
+	DBG("");
+
+	device_destroy(dev);
+}
+
+static void at_cmd_unknown(const char *command, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_handsfree_unknown_at *ev = (void *) buf;
+
+	bdaddr2android(&dev->bdaddr, ev->bdaddr);
+
+	/* copy while string including terminating NULL */
+	ev->len = strlen(command) + 1;
+
+	if (ev->len > IPC_MTU - sizeof(*ev)) {
+		hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+		return;
+	}
+
+	memcpy(ev->buf, command, ev->len);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+			HAL_EV_HANDSFREE_UNKNOWN_AT, sizeof(*ev) + ev->len, ev);
+}
+
+static void at_cmd_vgm(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_volume ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(context, &val) || val > 15)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		ev.type = HAL_HANDSFREE_VOLUME_TYPE_MIC;
+		ev.volume = val;
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_EV_HANDSFREE_VOLUME, sizeof(ev), &ev);
+
+		/* Framework is not replying with result for AT+VGM */
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_vgs(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_volume ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(context, &val) || val > 15)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		ev.type = HAL_HANDSFREE_VOLUME_TYPE_SPEAKER;
+		ev.volume = val;
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_EV_HANDSFREE_VOLUME, sizeof(ev), &ev);
+
+		/* Framework is not replying with result for AT+VGS */
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cops(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_cops ev;
+	unsigned int val;
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(context, &val) || val != 3)
+			break;
+
+		if (!hfp_context_get_number(context, &val) || val != 0)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_COPS, sizeof(ev), &ev);
+		return;
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bia(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	unsigned int val, i, def;
+	bool tmp[IND_COUNT];
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		for (i = 0; i < IND_COUNT; i++)
+			tmp[i] = dev->inds[i].active;
+
+		i = 0;
+
+		do {
+			def = (i < IND_COUNT) ? dev->inds[i].active : 0;
+
+			if (!hfp_context_get_number_default(context, &val, def))
+				goto failed;
+
+			if (val > 1)
+				goto failed;
+
+			if (i < IND_COUNT) {
+				tmp[i] = val || dev->inds[i].always_active;
+				i++;
+			}
+		} while (hfp_context_has_next(context));
+
+		for (i = 0; i < IND_COUNT; i++)
+			dev->inds[i].active = tmp[i];
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+failed:
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_a(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_answer ev;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (hfp_context_has_next(context))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_EV_HANDSFREE_ANSWER, sizeof(ev), &ev);
+
+		/* Framework is not replying with result for ATA */
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_SET:
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_d(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	char buf[IPC_MTU];
+	struct hal_ev_handsfree_dial *ev = (void *) buf;
+	int cnt;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_unquoted_string(context,
+						(char *) ev->number, 255))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev->bdaddr);
+
+		ev->number_len = strlen((char *) ev->number);
+
+		if (ev->number[ev->number_len - 1] != ';')
+			break;
+
+		if (ev->number[0] == '>')
+			cnt = strspn((char *) ev->number + 1, "0123456789") + 1;
+		else
+			cnt = strspn((char *) ev->number, "0123456789ABC*#+");
+
+		if (cnt != ev->number_len - 1)
+			break;
+
+		ev->number_len++;
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_DIAL,
+					sizeof(*ev) + ev->number_len, ev);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_ccwa(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(context, &val) || val > 1)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		dev->ccwa_enabled = val;
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_chup(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_hangup ev;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (hfp_context_has_next(context))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_EV_HANDSFREE_HANGUP, sizeof(ev), &ev);
+
+		/* Framework is not replying with result for AT+CHUP */
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_SET:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_clcc(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_clcc ev;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (hfp_context_has_next(context))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_CLCC, sizeof(ev), &ev);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_SET:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cmee(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(context, &val) || val > 1)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		dev->cmee_enabled = val;
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_clip(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(context, &val) || val > 1)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		dev->clip_enabled = val;
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_vts(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_dtmf ev;
+	char str[2];
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_unquoted_string(context, str, 2))
+			break;
+
+		if (!((str[0] >= '0' && str[0] <= '9') ||
+				(str[0] >= 'A' && str[0] <= 'D') ||
+				str[0] == '*' || str[0] == '#'))
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+		ev.tone = str[0];
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_DTMF, sizeof(ev), &ev);
+
+		/* Framework is not replying with result for AT+VTS */
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cnum(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_cnum ev;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (hfp_context_has_next(context))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_CNUM, sizeof(ev), &ev);
+		return;
+	case HFP_GW_CMD_TYPE_SET:
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_binp(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+
+	DBG("");
+
+	/* TODO */
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bldn(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_dial ev;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (hfp_context_has_next(context))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+		ev.number_len = 0;
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_DIAL, sizeof(ev), &ev);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_SET:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bvra(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_vr_state ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(context, &val) || val > 1)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		if (val)
+			ev.state = HAL_HANDSFREE_VR_STARTED;
+		else
+			ev.state = HAL_HANDSFREE_VR_STOPPED;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_VR, sizeof(ev), &ev);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_nrec(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_nrec ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		/*
+		 * Android HAL defines start and stop parameter for NREC
+		 * callback, but spec allows HF to only disable AG's NREC
+		 * feature for SLC duration. Follow spec here.
+		 */
+		if (!hfp_context_get_number(context, &val) || val != 0)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		ev.nrec = HAL_HANDSFREE_NREC_STOP;
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_NREC, sizeof(ev), &ev);
+
+		/* Framework is not replying with context for AT+NREC */
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bsir(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+
+	DBG("");
+
+	/* TODO */
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_btrh(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+
+	DBG("");
+
+	/* TODO */
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void disconnect_sco_cb(const bdaddr_t *addr)
+{
+	struct hf_device *dev;
+
+	DBG("");
+
+	dev = find_device(addr);
+	if (!dev) {
+		error("handsfree: Could not find device");
+		return;
+	}
+
+	set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
+}
+
+static void select_codec(struct hf_device *dev, uint8_t codec_type)
+{
+	uint8_t type = CODEC_ID_CVSD;
+	int i;
+
+	if (codec_type > 0) {
+		type = codec_type;
+		goto done;
+	}
+
+	for (i = CODECS_COUNT - 1; i >= CVSD_OFFSET; i--) {
+		if (!dev->codecs[i].local_supported)
+			continue;
+
+		if (!dev->codecs[i].remote_supported)
+			continue;
+
+		type = dev->codecs[i].type;
+		break;
+	}
+
+done:
+	dev->proposed_codec = type;
+
+	hfp_gw_send_info(dev->gw, "+BCS: %u", type);
+}
+
+static bool codec_negotiation_supported(struct hf_device *dev)
+{
+	return (dev->features & HFP_HF_FEAT_CODEC) &&
+			(hfp_ag_features & HFP_AG_FEAT_CODEC);
+}
+
+static void connect_sco_cb(enum sco_status status, const bdaddr_t *addr)
+{
+	struct hf_device *dev;
+
+	dev = find_device(addr);
+	if (!dev) {
+		error("handsfree: Connect sco failed, no device?");
+		return;
+	}
+
+	if (status == SCO_STATUS_OK) {
+		set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED);
+		return;
+	}
+
+	/* Try fallback to CVSD first */
+	if (codec_negotiation_supported(dev) &&
+				dev->negotiated_codec != CODEC_ID_CVSD) {
+		info("handsfree: trying fallback with CVSD");
+		select_codec(dev, CODEC_ID_CVSD);
+		return;
+	}
+
+	error("handsfree: audio connect failed");
+	set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
+}
+
+static bool connect_sco(struct hf_device *dev)
+{
+	uint16_t voice_settings;
+
+	if (codec_negotiation_supported(dev) &&
+			dev->negotiated_codec != CODEC_ID_CVSD)
+		voice_settings = BT_VOICE_TRANSPARENT;
+	else
+		voice_settings = BT_VOICE_CVSD_16BIT;
+
+	if (!bt_sco_connect(sco, &dev->bdaddr, voice_settings))
+		return false;
+
+	set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING);
+
+	return true;
+}
+
+static gboolean connect_sco_delayed(void *data)
+{
+	struct hf_device *dev = data;
+
+	DBG("");
+
+	dev->delay_sco = 0;
+
+	if (connect_sco(dev))
+		return FALSE;
+
+	/*
+	 * we try connect to negotiated codec. If it fails, and it isn't
+	 * CVSD codec, try connect CVSD
+	 */
+	if (dev->negotiated_codec != CODEC_ID_CVSD)
+		select_codec(dev, CODEC_ID_CVSD);
+
+	return FALSE;
+}
+
+static void at_cmd_bcc(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (!codec_negotiation_supported(dev))
+			break;
+
+		if (hfp_context_has_next(result))
+			break;
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+		/* we haven't negotiated codec, start selection */
+		if (!dev->negotiated_codec) {
+			select_codec(dev, 0);
+			return;
+		}
+
+		/* Delay SCO connection so that OK response is send first */
+		if (dev->delay_sco == 0)
+			dev->delay_sco = g_idle_add(connect_sco_delayed, dev);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_SET:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bcs(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_wbs ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(result, &val))
+			break;
+
+		if (hfp_context_has_next(result))
+			break;
+
+		/* Remote replied with other codec. Reply with error */
+		if (dev->proposed_codec != val) {
+			dev->proposed_codec = 0;
+			break;
+		}
+
+		ev.wbs = val;
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_WBS, sizeof(ev), &ev);
+
+		dev->proposed_codec = 0;
+		dev->negotiated_codec = val;
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+		/*
+		 * Delay SCO connection so that OK response is send first,
+		 * then connect with negotiated parameters.
+		 */
+		if (dev->delay_sco == 0)
+			dev->delay_sco = g_idle_add(connect_sco_delayed, dev);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_ckpd(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_hsp_key_press ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(result, &val) || val != 200)
+			break;
+
+		if (hfp_context_has_next(result))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+						HAL_EV_HANDSFREE_HSP_KEY_PRESS,
+						sizeof(ev), &ev);
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void register_post_slc_at(struct hf_device *dev)
+{
+	hfp_gw_set_command_handler(dev->gw, at_cmd_unknown, dev, NULL);
+
+	if (dev->hsp) {
+		hfp_gw_register(dev->gw, at_cmd_ckpd, "+CKPD", dev, NULL);
+		hfp_gw_register(dev->gw, at_cmd_vgs, "+VGS", dev, NULL);
+		hfp_gw_register(dev->gw, at_cmd_vgm, "+VGM", dev, NULL);
+		return;
+	}
+
+	hfp_gw_register(dev->gw, at_cmd_a, "A", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_d, "D", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_ccwa, "+CCWA", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_chup, "+CHUP", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_clcc, "+CLCC", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_cops, "+COPS", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_cmee, "+CMEE", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_clip, "+CLIP", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_vts, "+VTS", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_cnum, "+CNUM", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_bia, "+BIA", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_binp, "+BINP", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_bldn, "+BLDN", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_bvra, "+BVRA", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_nrec, "+NREC", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_vgs, "+VGS", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_vgm, "+VGM", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_bsir, "+BSIR", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_btrh, "+BTRH", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_bcc, "+BCC", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_bcs, "+BCS", dev, NULL);
+}
+
+static void at_cmd_cmer(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+	unsigned int val;
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		/* mode must be =3 */
+		if (!hfp_context_get_number(result, &val) || val != 3)
+			break;
+
+		/* keyp is don't care */
+		if (!hfp_context_get_number(result, &val))
+			break;
+
+		/* disp is don't care */
+		if (!hfp_context_get_number(result, &val))
+			break;
+
+		/* ind must be 0 or 1 */
+		if (!hfp_context_get_number(result, &val) || val > 1)
+			break;
+
+		dev->indicators_enabled = val;
+
+		/* skip bfr if present */
+		hfp_context_get_number(result, &val);
+
+		if (hfp_context_has_next(result))
+			break;
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+		if (dev->features & HFP_HF_FEAT_3WAY)
+			return;
+
+		register_post_slc_at(dev);
+		set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED);
+		return;
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+	if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED)
+		hfp_gw_disconnect(dev->gw);
+}
+
+static void at_cmd_cind(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_cind ev;
+	char *buf, *ptr;
+	int len;
+	unsigned int i;
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_TEST:
+
+		/*
+		 * If device supports Codec Negotiation, AT+BAC should be
+		 * received first
+		 */
+		if (codec_negotiation_supported(dev) &&
+				!dev->codecs[CVSD_OFFSET].remote_supported)
+			break;
+
+		len = strlen("+CIND:") + 1;
+
+		for (i = 0; i < IND_COUNT; i++) {
+			len += strlen("(\"\",(X,X)),");
+			len += strlen(dev->inds[i].name);
+		}
+
+		buf = g_malloc(len);
+
+		ptr = buf + sprintf(buf, "+CIND:");
+
+		for (i = 0; i < IND_COUNT; i++) {
+			ptr += sprintf(ptr, "(\"%s\",(%d%c%d)),",
+					dev->inds[i].name,
+					dev->inds[i].min,
+					dev->inds[i].max == 1 ? ',' : '-',
+					dev->inds[i].max);
+		}
+
+		ptr--;
+		*ptr = '\0';
+
+		hfp_gw_send_info(dev->gw, "%s", buf);
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+		g_free(buf);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_CIND, sizeof(ev), &ev);
+		return;
+	case HFP_GW_CMD_TYPE_SET:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+	if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED)
+		hfp_gw_disconnect(dev->gw);
+}
+
+static void at_cmd_brsf(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+	unsigned int feat;
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(result, &feat))
+			break;
+
+		if (hfp_context_has_next(result))
+			break;
+
+		/* TODO verify features */
+		dev->features = feat;
+
+		hfp_gw_send_info(dev->gw, "+BRSF: %u", hfp_ag_features);
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+	if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED)
+		hfp_gw_disconnect(dev->gw);
+}
+
+static void at_cmd_chld(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_chld ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(result, &val) || val > 3)
+			break;
+
+		/* No ECC support */
+		if (hfp_context_has_next(result))
+			break;
+
+		/* value match HAL type */
+		ev.chld = val;
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_CHLD, sizeof(ev), &ev);
+		return;
+	case HFP_GW_CMD_TYPE_TEST:
+		hfp_gw_send_info(dev->gw, "+CHLD: (%s)", HFP_AG_CHLD);
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+		register_post_slc_at(dev);
+		set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+	if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED)
+		hfp_gw_disconnect(dev->gw);
+}
+
+static struct hfp_codec *find_codec_by_type(struct hf_device *dev, uint8_t type)
+{
+	int i;
+
+	for (i = 0; i < CODECS_COUNT; i++)
+		if (type == dev->codecs[i].type)
+			return &dev->codecs[i];
+
+	return NULL;
+}
+
+static void at_cmd_bac(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!codec_negotiation_supported(dev))
+			goto failed;
+
+		/* set codecs to defaults */
+		init_codecs(dev);
+		dev->negotiated_codec = 0;
+
+		/*
+		 * At least CVSD mandatory codec must exist
+		 * HFP V1.6 4.34.1
+		 */
+		if (!hfp_context_get_number(result, &val) ||
+							val != CODEC_ID_CVSD)
+			goto failed;
+
+		dev->codecs[CVSD_OFFSET].remote_supported = true;
+
+		if (hfp_context_get_number(result, &val)) {
+			if (val != CODEC_ID_MSBC)
+				goto failed;
+
+			dev->codecs[MSBC_OFFSET].remote_supported = true;
+		}
+
+		while (hfp_context_has_next(result)) {
+			struct hfp_codec *codec;
+
+			if (!hfp_context_get_number(result, &val))
+				goto failed;
+
+			codec = find_codec_by_type(dev, val);
+			if (!codec)
+				continue;
+
+			codec->remote_supported = true;
+		}
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+		if (dev->proposed_codec)
+			select_codec(dev, 0);
+		return;
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+failed:
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+	if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED)
+		hfp_gw_disconnect(dev->gw);
+}
+
+static void register_slc_at(struct hf_device *dev)
+{
+	hfp_gw_register(dev->gw, at_cmd_brsf, "+BRSF", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_cind, "+CIND", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_cmer, "+CMER", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_chld, "+CHLD", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_bac, "+BAC", dev, NULL);
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct hf_device *dev = user_data;
+
+	DBG("");
+
+	if (err) {
+		error("handsfree: connect failed (%s)", err->message);
+		goto failed;
+	}
+
+	dev->gw = hfp_gw_new(g_io_channel_unix_get_fd(chan));
+	if (!dev->gw)
+		goto failed;
+
+	g_io_channel_set_close_on_unref(chan, FALSE);
+
+	hfp_gw_set_close_on_unref(dev->gw, true);
+	hfp_gw_set_disconnect_handler(dev->gw, disconnect_watch, dev, NULL);
+
+	if (dev->hsp) {
+		register_post_slc_at(dev);
+		set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTED);
+		set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED);
+		return;
+	}
+
+	register_slc_at(dev);
+	set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTED);
+	return;
+
+failed:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+	device_destroy(dev);
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+	char address[18];
+	bdaddr_t bdaddr;
+	GError *err = NULL;
+	struct hf_device *dev;
+
+	bt_io_get(chan, &err,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_DEST_BDADDR, &bdaddr,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("handsfree: confirm failed (%s)", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	DBG("incoming connect from %s", address);
+
+	dev = get_device(&bdaddr);
+	if (!dev) {
+		error("handsfree: Failed to get device object for %s", address);
+		goto drop;
+	}
+
+	if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) {
+		info("handsfree: refusing connection from %s", address);
+		goto drop;
+	}
+
+	if (!bt_io_accept(chan, connect_cb, dev, NULL, NULL)) {
+		error("handsfree: failed to accept connection");
+		device_destroy(dev);
+		goto drop;
+	}
+
+	dev->hsp = GPOINTER_TO_INT(data);
+
+	set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTING);
+
+	return;
+
+drop:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static void sdp_hsp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	struct hf_device *dev = data;
+	sdp_list_t *protos;
+	GError *gerr = NULL;
+	GIOChannel *io;
+	uuid_t class;
+	int channel;
+
+	DBG("");
+
+	if (err < 0) {
+		error("handsfree: unable to get SDP record: %s",
+								strerror(-err));
+		goto fail;
+	}
+
+	sdp_uuid16_create(&class, HEADSET_SVCLASS_ID);
+
+	/* Find record with proper service class */
+	for (; recs; recs = recs->next) {
+		sdp_record_t *rec = recs->data;
+
+		if (rec && !sdp_uuid_cmp(&rec->svclass, &class))
+			break;
+	}
+
+	if (!recs || !recs->data) {
+		info("handsfree: no valid HSP SDP records found");
+		goto fail;
+	}
+
+	if (sdp_get_access_protos(recs->data, &protos) < 0) {
+		error("handsfree: unable to get access protocols from record");
+		goto fail;
+	}
+
+	/* TODO read remote version? */
+	/* TODO read volume control support */
+
+	channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+	sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+	sdp_list_free(protos, NULL);
+	if (channel <= 0) {
+		error("handsfree: unable to get RFCOMM channel from record");
+		goto fail;
+	}
+
+	io = bt_io_connect(connect_cb, dev, NULL, &gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_CHANNEL, channel,
+				BT_IO_OPT_INVALID);
+	if (!io) {
+		error("handsfree: unable to connect: %s", gerr->message);
+		g_error_free(gerr);
+		goto fail;
+	}
+
+	dev->hsp = true;
+
+	g_io_channel_unref(io);
+	return;
+
+fail:
+	device_destroy(dev);
+}
+
+static int sdp_search_hsp(struct hf_device *dev)
+{
+	uuid_t uuid;
+
+	sdp_uuid16_create(&uuid, HEADSET_SVCLASS_ID);
+
+	return bt_search_service(&adapter_addr, &dev->bdaddr, &uuid,
+					sdp_hsp_search_cb, dev, NULL, 0);
+}
+
+static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	struct hf_device *dev = data;
+	sdp_list_t *protos;
+	GError *gerr = NULL;
+	GIOChannel *io;
+	uuid_t class;
+	int channel;
+
+	DBG("");
+
+	if (err < 0) {
+		error("handsfree: unable to get SDP record: %s",
+								strerror(-err));
+		goto fail;
+	}
+
+	sdp_uuid16_create(&class, HANDSFREE_SVCLASS_ID);
+
+	/* Find record with proper service class */
+	for (; recs; recs = recs->next) {
+		sdp_record_t *rec = recs->data;
+
+		if (rec && !sdp_uuid_cmp(&rec->svclass, &class))
+			break;
+	}
+
+	if (!recs || !recs->data) {
+		info("handsfree: no HFP SDP records found, trying HSP");
+
+		if (sdp_search_hsp(dev) < 0) {
+			error("handsfree: HSP SDP search failed");
+			goto fail;
+		}
+
+		return;
+	}
+
+	if (sdp_get_access_protos(recs->data, &protos) < 0) {
+		error("handsfree: unable to get access protocols from record");
+		goto fail;
+	}
+
+	channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+	sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+	sdp_list_free(protos, NULL);
+	if (channel <= 0) {
+		error("handsfree: unable to get RFCOMM channel from record");
+		goto fail;
+	}
+
+	/* TODO read remote version? */
+
+	io = bt_io_connect(connect_cb, dev, NULL, &gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_CHANNEL, channel,
+				BT_IO_OPT_INVALID);
+	if (!io) {
+		error("handsfree: unable to connect: %s", gerr->message);
+		g_error_free(gerr);
+		goto fail;
+	}
+
+	g_io_channel_unref(io);
+	return;
+
+fail:
+	device_destroy(dev);
+}
+
+static int sdp_search_hfp(struct hf_device *dev)
+{
+	uuid_t uuid;
+
+	sdp_uuid16_create(&uuid, HANDSFREE_SVCLASS_ID);
+
+	return bt_search_service(&adapter_addr, &dev->bdaddr, &uuid,
+					sdp_hfp_search_cb, dev, NULL, 0);
+}
+
+static void handle_connect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_connect *cmd = buf;
+	struct hf_device *dev;
+	char addr[18];
+	uint8_t status;
+	bdaddr_t bdaddr;
+	int ret;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+	dev = get_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ba2str(&bdaddr, addr);
+	DBG("connecting to %s", addr);
+
+	/* prefer HFP over HSP */
+	ret = hfp_server ? sdp_search_hfp(dev) : sdp_search_hsp(dev);
+	if (ret < 0) {
+		error("handsfree: SDP search failed");
+		device_destroy(dev);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTING);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_CONNECT, status);
+}
+
+static void handle_disconnect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_disconnect *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (dev->state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (dev->state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING) {
+		status = HAL_STATUS_SUCCESS;
+		goto failed;
+	}
+
+	if (dev->state == HAL_EV_HANDSFREE_CONN_STATE_CONNECTING) {
+		device_destroy(dev);
+	} else {
+		set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING);
+		hfp_gw_disconnect(dev->gw);
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_DISCONNECT, status);
+}
+
+static bool disconnect_sco(struct hf_device *dev)
+{
+	if (dev->audio_state == HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED ||
+		dev->audio_state == HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING)
+		return false;
+
+	bt_sco_disconnect(sco);
+	set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING);
+
+	return true;
+}
+
+static bool connect_audio(struct hf_device *dev)
+{
+	if (dev->audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED)
+		return false;
+
+	/* we haven't negotiated codec, start selection */
+	if (codec_negotiation_supported(dev) && !dev->negotiated_codec) {
+		select_codec(dev, 0);
+		return true;
+	}
+
+	return connect_sco(dev);
+}
+
+static void handle_connect_audio(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_connect_audio *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (dev->audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	status = connect_audio(dev) ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_CONNECT_AUDIO, status);
+}
+
+static void handle_disconnect_audio(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_disconnect_audio *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	status = disconnect_sco(dev) ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_DISCONNECT_AUDIO, status);
+}
+
+static void handle_start_vr(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_start_vr *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (dev->features & HFP_HF_FEAT_VR) {
+		hfp_gw_send_info(dev->gw, "+BVRA: 1");
+		status = HAL_STATUS_SUCCESS;
+	} else {
+		status = HAL_STATUS_FAILED;
+	}
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_START_VR, status);
+}
+
+static void handle_stop_vr(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_stop_vr *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (dev->features & HFP_HF_FEAT_VR) {
+		hfp_gw_send_info(dev->gw, "+BVRA: 0");
+		status = HAL_STATUS_SUCCESS;
+	} else {
+		status = HAL_STATUS_FAILED;
+	}
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_STOP_VR, status);
+}
+
+static void handle_volume_control(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_volume_control *cmd = buf;
+	struct hf_device *dev;
+	uint8_t status, volume;
+	bdaddr_t bdaddr;
+
+	DBG("type=%u volume=%u", cmd->type, cmd->volume);
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	volume = cmd->volume > 15 ? 15 : cmd->volume;
+
+	switch (cmd->type) {
+	case HAL_HANDSFREE_VOLUME_TYPE_MIC:
+		hfp_gw_send_info(dev->gw, "+VGM: %u", volume);
+
+		status = HAL_STATUS_SUCCESS;
+		break;
+	case HAL_HANDSFREE_VOLUME_TYPE_SPEAKER:
+		hfp_gw_send_info(dev->gw, "+VGS: %u", volume);
+
+		status = HAL_STATUS_SUCCESS;
+		break;
+	default:
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_VOLUME_CONTROL, status);
+}
+
+static void update_indicator(struct hf_device *dev, int ind, uint8_t val)
+{
+	DBG("ind=%u new=%u old=%u", ind, val, dev->inds[ind].val);
+
+	if (dev->inds[ind].val == val)
+		return;
+
+	dev->inds[ind].val = val;
+
+	if (!dev->indicators_enabled)
+		return;
+
+	if (!dev->inds[ind].active)
+		return;
+
+	/* indicator numbers in CIEV start from 1 */
+	hfp_gw_send_info(dev->gw, "+CIEV: %u,%u", ind + 1, val);
+}
+
+static void device_status_notif(void *data, void *user_data)
+{
+	struct hf_device *dev = data;
+	struct hal_cmd_handsfree_device_status_notif *cmd = user_data;
+
+	update_indicator(dev, IND_SERVICE, cmd->state);
+	update_indicator(dev, IND_ROAM, cmd->type);
+	update_indicator(dev, IND_SIGNAL, cmd->signal);
+	update_indicator(dev, IND_BATTCHG, cmd->battery);
+}
+
+static void handle_device_status_notif(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_device_status_notif *cmd = buf;
+	uint8_t status;
+
+	DBG("");
+
+	if (queue_isempty(devices)) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	/* Cast cmd to void as queue api needs that */
+	queue_foreach(devices, device_status_notif, (void *) cmd);
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF, status);
+}
+
+static void handle_cops(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_cops_response *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	if (len != sizeof(*cmd) + cmd->len ||
+			(cmd->len != 0 && cmd->buf[cmd->len - 1] != '\0')) {
+		error("Invalid cops response command, terminating");
+		raise(SIGTERM);
+		return;
+	}
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	hfp_gw_send_info(dev->gw, "+COPS: 0,0,\"%.16s\"",
+					cmd->len ? (char *) cmd->buf : "");
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_COPS_RESPONSE, status);
+}
+
+static unsigned int get_callsetup(uint8_t state)
+{
+	switch (state) {
+	case HAL_HANDSFREE_CALL_STATE_INCOMING:
+		return 1;
+	case HAL_HANDSFREE_CALL_STATE_DIALING:
+		return 2;
+	case HAL_HANDSFREE_CALL_STATE_ALERTING:
+		return 3;
+	default:
+		return 0;
+	}
+}
+
+static void handle_cind(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_cind_response *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	/* HAL doesn't provide indicators values so need to convert here */
+	dev->inds[IND_SERVICE].val = cmd->svc;
+	dev->inds[IND_CALL].val = !!(cmd->num_active + cmd->num_held);
+	dev->inds[IND_CALLSETUP].val = get_callsetup(cmd->state);
+	dev->inds[IND_CALLHELD].val = cmd->num_held ?
+						(cmd->num_active ? 1 : 2) : 0;
+	dev->inds[IND_SIGNAL].val = cmd->signal;
+	dev->inds[IND_ROAM].val = cmd->roam;
+	dev->inds[IND_BATTCHG].val = cmd->batt_chg;
+
+	/* Order must match indicators_defaults table */
+	hfp_gw_send_info(dev->gw, "+CIND: %u,%u,%u,%u,%u,%u,%u",
+						dev->inds[IND_SERVICE].val,
+						dev->inds[IND_CALL].val,
+						dev->inds[IND_CALLSETUP].val,
+						dev->inds[IND_CALLHELD].val,
+						dev->inds[IND_SIGNAL].val,
+						dev->inds[IND_ROAM].val,
+						dev->inds[IND_BATTCHG].val);
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_CIND_RESPONSE, status);
+}
+
+static void handle_formatted_at_resp(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_formatted_at_response *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + cmd->len ||
+			(cmd->len != 0 && cmd->buf[cmd->len - 1] != '\0')) {
+		error("Invalid formatted AT response command, terminating");
+		raise(SIGTERM);
+		return;
+	}
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	hfp_gw_send_info(dev->gw, "%s", cmd->len ? (char *) cmd->buf : "");
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+			HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE, status);
+}
+
+static void handle_at_resp(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_at_response *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (cmd->response == HAL_HANDSFREE_AT_RESPONSE_OK)
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+	else if (dev->cmee_enabled)
+		hfp_gw_send_error(dev->gw, cmd->error);
+	else
+		hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_AT_RESPONSE, status);
+}
+
+static void handle_clcc_resp(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_clcc_response *cmd = buf;
+	struct hf_device *dev;
+	uint8_t status;
+	bdaddr_t bdaddr;
+	char *number;
+
+	if (len != sizeof(*cmd) + cmd->number_len || (cmd->number_len != 0 &&
+				cmd->number[cmd->number_len - 1] != '\0')) {
+		error("Invalid CLCC response command, terminating");
+		raise(SIGTERM);
+		return;
+	}
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (!cmd->index) {
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+		status = HAL_STATUS_SUCCESS;
+		goto done;
+	}
+
+	number = cmd->number_len ? (char *) cmd->number : "";
+
+	switch (cmd->state) {
+	case HAL_HANDSFREE_CALL_STATE_INCOMING:
+	case HAL_HANDSFREE_CALL_STATE_WAITING:
+	case HAL_HANDSFREE_CALL_STATE_ACTIVE:
+	case HAL_HANDSFREE_CALL_STATE_HELD:
+	case HAL_HANDSFREE_CALL_STATE_DIALING:
+	case HAL_HANDSFREE_CALL_STATE_ALERTING:
+		if (cmd->type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL &&
+							number[0] != '+')
+			hfp_gw_send_info(dev->gw,
+					"+CLCC: %u,%u,%u,%u,%u,\"+%s\",%u",
+					cmd->index, cmd->dir, cmd->state,
+					cmd->mode, cmd->mpty, number,
+					cmd->type);
+		else
+			hfp_gw_send_info(dev->gw,
+					"+CLCC: %u,%u,%u,%u,%u,\"%s\",%u",
+					cmd->index, cmd->dir, cmd->state,
+					cmd->mode, cmd->mpty, number,
+					cmd->type);
+
+		status = HAL_STATUS_SUCCESS;
+		break;
+	case HAL_HANDSFREE_CALL_STATE_IDLE:
+	default:
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_CLCC_RESPONSE, status);
+}
+
+static gboolean ring_cb(gpointer user_data)
+{
+	struct hf_device *dev = user_data;
+
+	hfp_gw_send_info(dev->gw, "RING");
+
+	if (dev->clip_enabled && dev->clip)
+		hfp_gw_send_info(dev->gw, "%s", dev->clip);
+
+	return TRUE;
+}
+
+static void phone_state_dialing(struct hf_device *dev, int num_active,
+								int num_held)
+{
+	if (dev->call_hanging_up) {
+		g_source_remove(dev->call_hanging_up);
+		dev->call_hanging_up = 0;
+	}
+
+	update_indicator(dev, IND_CALLSETUP, 2);
+
+	if (num_active == 0 && num_held > 0)
+		update_indicator(dev, IND_CALLHELD, 2);
+
+	if (dev->num_active == 0 && dev->num_held == 0)
+		connect_audio(dev);
+}
+
+static void phone_state_alerting(struct hf_device *dev, int num_active,
+								int num_held)
+{
+	if (dev->call_hanging_up) {
+		g_source_remove(dev->call_hanging_up);
+		dev->call_hanging_up = 0;
+	}
+
+	update_indicator(dev, IND_CALLSETUP, 3);
+}
+
+static void phone_state_waiting(struct hf_device *dev, int num_active,
+					int num_held, uint8_t type,
+					const uint8_t *number, int number_len)
+{
+	char *num;
+
+	if (!dev->ccwa_enabled)
+		return;
+
+	num = number_len ? (char *) number : "";
+
+	if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+')
+		hfp_gw_send_info(dev->gw, "+CCWA: \"+%s\",%u", num, type);
+	else
+		hfp_gw_send_info(dev->gw, "+CCWA: \"%s\",%u", num, type);
+
+	update_indicator(dev, IND_CALLSETUP, 1);
+}
+
+static void phone_state_incoming(struct hf_device *dev, int num_active,
+					int num_held, uint8_t type,
+					const uint8_t *number, int number_len)
+{
+	char *num;
+
+	if (dev->setup_state == HAL_HANDSFREE_CALL_STATE_INCOMING) {
+		if (dev->num_active != num_active ||
+						dev->num_held != num_held) {
+			if (dev->num_active == num_held &&
+						dev->num_held == num_active)
+				return;
+			/*
+			 * calls changed while waiting call ie. due to
+			 * termination of active call
+			 */
+			update_indicator(dev, IND_CALLHELD,
+					num_held ? (num_active ? 1 : 2) : 0);
+			update_indicator(dev, IND_CALL,
+						!!(num_active + num_held));
+		}
+
+		return;
+	}
+
+	if (dev->call_hanging_up)
+		return;
+
+	if (num_active > 0 || num_held > 0) {
+		phone_state_waiting(dev, num_active, num_held, type, number,
+								number_len);
+		return;
+	}
+
+	update_indicator(dev, IND_CALLSETUP, 1);
+
+	num = number_len ? (char *) number : "";
+
+	if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+')
+		dev->clip = g_strdup_printf("+CLIP: \"+%s\",%u", num, type);
+	else
+		dev->clip = g_strdup_printf("+CLIP: \"%s\",%u", num, type);
+
+	/* send first RING */
+	ring_cb(dev);
+
+	dev->ring = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
+							RING_TIMEOUT, ring_cb,
+							dev, NULL);
+	if (!dev->ring) {
+		g_free(dev->clip);
+		dev->clip = NULL;
+	}
+}
+
+static gboolean hang_up_cb(gpointer user_data)
+{
+	struct hf_device *dev = user_data;
+
+	DBG("");
+
+	dev->call_hanging_up = 0;
+
+	return FALSE;
+}
+
+static void phone_state_idle(struct hf_device *dev, int num_active,
+								int num_held)
+{
+	if (dev->ring) {
+		g_source_remove(dev->ring);
+		dev->ring = 0;
+
+		if (dev->clip) {
+			g_free(dev->clip);
+			dev->clip = NULL;
+		}
+	}
+
+	switch (dev->setup_state) {
+	case HAL_HANDSFREE_CALL_STATE_INCOMING:
+		if (num_active > dev->num_active) {
+			update_indicator(dev, IND_CALL, 1);
+
+			if (dev->num_active == 0 && dev->num_held == 0)
+				connect_audio(dev);
+		}
+
+		if (num_held >= dev->num_held && num_held != 0)
+			update_indicator(dev, IND_CALLHELD, 1);
+
+		update_indicator(dev, IND_CALLSETUP, 0);
+
+		if (num_active == 0 && num_held == 0 &&
+				num_active == dev->num_active &&
+				num_held == dev->num_held)
+			dev->call_hanging_up = g_timeout_add(800, hang_up_cb,
+									dev);
+		break;
+	case HAL_HANDSFREE_CALL_STATE_DIALING:
+	case HAL_HANDSFREE_CALL_STATE_ALERTING:
+		if (num_active > dev->num_active)
+			update_indicator(dev, IND_CALL, 1);
+
+		update_indicator(dev, IND_CALLHELD,
+					num_held ? (num_active ? 1 : 2) : 0);
+
+		update_indicator(dev, IND_CALLSETUP, 0);
+
+		/* disconnect SCO if we hang up while dialing or alerting */
+		if (num_active == 0 && num_held == 0)
+			disconnect_sco(dev);
+		break;
+	case HAL_HANDSFREE_CALL_STATE_IDLE:
+		if (dev->call_hanging_up) {
+			g_source_remove(dev->call_hanging_up);
+			dev->call_hanging_up = 0;
+			return;
+		}
+
+		/* check if calls swapped */
+		if (num_held != 0 && num_active != 0 &&
+				dev->num_active == num_held &&
+				dev->num_held == num_active) {
+			/* TODO better way for forcing indicator */
+			dev->inds[IND_CALLHELD].val = 0;
+		} else if ((num_active > 0 || num_held > 0) &&
+						dev->num_active == 0 &&
+						dev->num_held == 0) {
+			/*
+			 * If number of active or held calls change but there
+			 * was no call setup change this means that there were
+			 * calls present when headset was connected.
+			 */
+			connect_audio(dev);
+		} else if (num_active == 0 && num_held == 0) {
+			disconnect_sco(dev);
+		}
+
+		update_indicator(dev, IND_CALLHELD,
+					num_held ? (num_active ? 1 : 2) : 0);
+		update_indicator(dev, IND_CALL, !!(num_active + num_held));
+		update_indicator(dev, IND_CALLSETUP, 0);
+
+		/* If call was terminated due to carrier lost send NO CARRIER */
+		if (num_active == 0 && num_held == 0 &&
+				dev->inds[IND_SERVICE].val == 0 &&
+				(dev->num_active > 0 || dev->num_held > 0))
+			hfp_gw_send_info(dev->gw, "NO CARRIER");
+
+		break;
+	default:
+		DBG("unhandled state %u", dev->setup_state);
+		break;
+	}
+}
+
+static void phone_state_change(void *data, void *user_data)
+{
+	struct hf_device *dev = data;
+	struct hal_cmd_handsfree_phone_state_change *cmd = user_data;
+
+	switch (cmd->state) {
+	case HAL_HANDSFREE_CALL_STATE_DIALING:
+		phone_state_dialing(dev, cmd->num_active, cmd->num_held);
+		break;
+	case HAL_HANDSFREE_CALL_STATE_ALERTING:
+		phone_state_alerting(dev, cmd->num_active, cmd->num_held);
+		break;
+	case HAL_HANDSFREE_CALL_STATE_INCOMING:
+		phone_state_incoming(dev, cmd->num_active, cmd->num_held,
+						cmd->type, cmd->number,
+						cmd->number_len);
+		break;
+	case HAL_HANDSFREE_CALL_STATE_IDLE:
+		phone_state_idle(dev, cmd->num_active, cmd->num_held);
+		break;
+	default:
+		DBG("unhandled new state %u (current state %u)", cmd->state,
+							dev->setup_state);
+
+		return;
+	}
+
+	dev->num_active = cmd->num_active;
+	dev->num_held = cmd->num_held;
+	dev->setup_state = cmd->state;
+
+}
+
+static void handle_phone_state_change(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_phone_state_change *cmd = buf;
+	uint8_t status;
+
+	if (len != sizeof(*cmd) + cmd->number_len || (cmd->number_len != 0 &&
+				cmd->number[cmd->number_len - 1] != '\0')) {
+		error("Invalid phone state change command, terminating");
+		raise(SIGTERM);
+		return;
+	}
+
+	DBG("active=%u hold=%u state=%u", cmd->num_active, cmd->num_held,
+								cmd->state);
+
+	if (queue_isempty(devices)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	/* Cast cmd to void as queue api needs that */
+	queue_foreach(devices, phone_state_change, (void *) cmd);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_PHONE_STATE_CHANGE, status);
+}
+
+static void handle_configure_wbs(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_configure_wbs *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	if (!(hfp_ag_features & HFP_AG_FEAT_CODEC)) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (dev->audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	switch (cmd->config) {
+	case HAL_HANDSFREE_WBS_NO:
+		dev->codecs[MSBC_OFFSET].local_supported = false;
+		break;
+	case HAL_HANDSFREE_WBS_YES:
+		dev->codecs[MSBC_OFFSET].local_supported = true;
+		break;
+	case HAL_HANDSFREE_WBS_NONE:
+		/* TODO */
+	default:
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	/*
+	 * cleanup negotiated codec if WBS support was changed, it will be
+	 * renegotiated on next audio connection based on currently supported
+	 * codecs
+	 */
+	dev->negotiated_codec = 0;
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_CONFIGURE_WBS, status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_HANDSFREE_CONNECT */
+	{ handle_connect, false,
+		sizeof(struct hal_cmd_handsfree_connect) },
+	/* HAL_OP_HANDSFREE_DISCONNECT */
+	{ handle_disconnect, false,
+		sizeof(struct hal_cmd_handsfree_disconnect) },
+	/* HAL_OP_HANDSFREE_CONNECT_AUDIO */
+	{ handle_connect_audio, false,
+		sizeof(struct hal_cmd_handsfree_connect_audio) },
+	/* HAL_OP_HANDSFREE_DISCONNECT_AUDIO */
+	{ handle_disconnect_audio, false,
+		sizeof(struct hal_cmd_handsfree_disconnect_audio) },
+	/* define HAL_OP_HANDSFREE_START_VR */
+	{ handle_start_vr, false, sizeof(struct hal_cmd_handsfree_start_vr) },
+	/* define HAL_OP_HANDSFREE_STOP_VR */
+	{ handle_stop_vr, false, sizeof(struct hal_cmd_handsfree_stop_vr) },
+	/* HAL_OP_HANDSFREE_VOLUME_CONTROL */
+	{ handle_volume_control, false,
+		sizeof(struct hal_cmd_handsfree_volume_control) },
+	/* HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF */
+	{ handle_device_status_notif, false,
+		sizeof(struct hal_cmd_handsfree_device_status_notif) },
+	/* HAL_OP_HANDSFREE_COPS_RESPONSE */
+	{ handle_cops, true,
+		sizeof(struct hal_cmd_handsfree_cops_response) },
+	/* HAL_OP_HANDSFREE_CIND_RESPONSE */
+	{ handle_cind, false,
+		sizeof(struct hal_cmd_handsfree_cind_response) },
+	/* HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE */
+	{ handle_formatted_at_resp, true,
+		sizeof(struct hal_cmd_handsfree_formatted_at_response) },
+	/* HAL_OP_HANDSFREE_AT_RESPONSE */
+	{ handle_at_resp, false,
+		sizeof(struct hal_cmd_handsfree_at_response) },
+	/* HAL_OP_HANDSFREE_CLCC_RESPONSE */
+	{ handle_clcc_resp, true,
+		sizeof(struct hal_cmd_handsfree_clcc_response) },
+	/* HAL_OP_HANDSFREE_PHONE_STATE_CHANGE */
+	{ handle_phone_state_change, true,
+		sizeof(struct hal_cmd_handsfree_phone_state_change) },
+	/* HAL_OP_HANDSFREE_CONFIGURE_WBS */
+	{ handle_configure_wbs, false,
+		sizeof(struct hal_cmd_handsfree_configure_wbs) },
+};
+
+static sdp_record_t *headset_ag_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+	uuid_t l2cap_uuid, rfcomm_uuid;
+	sdp_profile_desc_t profile;
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t *record;
+	sdp_data_t *channel;
+	uint8_t netid = 0x01;
+	sdp_data_t *network;
+	uint8_t ch = HSP_AG_CHANNEL;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	network = sdp_data_alloc(SDP_UINT8, &netid);
+	if (!network) {
+		sdp_record_free(record);
+		return NULL;
+	}
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &svclass_uuid);
+	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
+	profile.version = 0x0102;
+	pfseq = sdp_list_append(NULL, &profile);
+	sdp_set_profile_descs(record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &ch);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_set_info_attr(record, "Voice Gateway", NULL, NULL);
+
+	sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static bool confirm_sco_cb(const bdaddr_t *addr, uint16_t *voice_settings)
+{
+	char address[18];
+	struct hf_device *dev;
+
+	ba2str(addr, address);
+
+	DBG("incoming SCO connection from %s", address);
+
+	dev = find_device(addr);
+	if (!dev || dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED) {
+		error("handsfree: audio connection from %s rejected", address);
+		return false;
+	}
+
+	/* If HF initiate SCO there must be no WBS used */
+	*voice_settings = 0;
+
+	set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING);
+	return true;
+}
+
+static bool enable_hsp_ag(void)
+{
+	sdp_record_t *rec;
+	GError *err = NULL;
+
+	DBG("");
+
+	hsp_server =  bt_io_listen(NULL, confirm_cb, GINT_TO_POINTER(true),
+					NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_CHANNEL, HSP_AG_CHANNEL,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_INVALID);
+	if (!hsp_server) {
+		error("Failed to listen on Headset rfcomm: %s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	rec = headset_ag_record();
+	if (!rec) {
+		error("Failed to allocate Headset record");
+		goto failed;
+	}
+
+	if (bt_adapter_add_record(rec, 0) < 0) {
+		error("Failed to register Headset record");
+		sdp_record_free(rec);
+		goto failed;
+	}
+
+	hsp_record_id = rec->handle;
+	return true;
+
+failed:
+	g_io_channel_shutdown(hsp_server, TRUE, NULL);
+	g_io_channel_unref(hsp_server);
+	hsp_server = NULL;
+
+	return false;
+}
+
+static void cleanup_hsp_ag(void)
+{
+	if (hsp_server) {
+		g_io_channel_shutdown(hsp_server, TRUE, NULL);
+		g_io_channel_unref(hsp_server);
+		hsp_server = NULL;
+	}
+
+	if (hsp_record_id > 0) {
+		bt_adapter_remove_record(hsp_record_id);
+		hsp_record_id = 0;
+	}
+}
+
+static sdp_record_t *hfp_ag_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+	uuid_t l2cap_uuid, rfcomm_uuid;
+	sdp_profile_desc_t profile;
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t *record;
+	sdp_data_t *channel, *features;
+	uint8_t netid = 0x01;
+	uint16_t sdpfeat;
+	sdp_data_t *network;
+	uint8_t ch = HFP_AG_CHANNEL;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	network = sdp_data_alloc(SDP_UINT8, &netid);
+	if (!network) {
+		sdp_record_free(record);
+		return NULL;
+	}
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &svclass_uuid);
+	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+	profile.version = 0x0106;
+	pfseq = sdp_list_append(NULL, &profile);
+	sdp_set_profile_descs(record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &ch);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	/* Codec Negotiation bit in SDP feature is different then in BRSF */
+	sdpfeat = hfp_ag_features & 0x0000003F;
+	if (hfp_ag_features & HFP_AG_FEAT_CODEC)
+		sdpfeat |= 0x00000020;
+	else
+		sdpfeat &= ~0x00000020;
+
+	features = sdp_data_alloc(SDP_UINT16, &sdpfeat);
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_set_info_attr(record, "Hands-Free Audio Gateway", NULL, NULL);
+
+	sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static bool enable_hfp_ag(void)
+{
+	sdp_record_t *rec;
+	GError *err = NULL;
+
+	DBG("");
+
+	if (hfp_server)
+		return false;
+
+	hfp_server =  bt_io_listen(NULL, confirm_cb, GINT_TO_POINTER(false),
+					NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_CHANNEL, HFP_AG_CHANNEL,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_INVALID);
+	if (!hfp_server) {
+		error("Failed to listen on Handsfree rfcomm: %s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	rec = hfp_ag_record();
+	if (!rec) {
+		error("Failed to allocate Handsfree record");
+		goto failed;
+	}
+
+	if (bt_adapter_add_record(rec, 0) < 0) {
+		error("Failed to register Handsfree record");
+		sdp_record_free(rec);
+		goto failed;
+	}
+
+	hfp_record_id = rec->handle;
+	return true;
+
+failed:
+	g_io_channel_shutdown(hfp_server, TRUE, NULL);
+	g_io_channel_unref(hfp_server);
+	hfp_server = NULL;
+
+	return false;
+}
+
+static void cleanup_hfp_ag(void)
+{
+	if (hfp_server) {
+		g_io_channel_shutdown(hfp_server, TRUE, NULL);
+		g_io_channel_unref(hfp_server);
+		hfp_server = NULL;
+	}
+
+	if (hfp_record_id > 0) {
+		bt_adapter_remove_record(hfp_record_id);
+		hfp_record_id = 0;
+	}
+}
+
+static void bt_sco_get_fd(const void *buf, uint16_t len)
+{
+	const struct sco_cmd_get_fd *cmd = buf;
+	struct sco_rsp_get_fd rsp;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	int fd;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev || !bt_sco_get_fd_and_mtu(sco, &fd, &rsp.mtu))
+		goto failed;
+
+	DBG("fd %d mtu %u", fd, rsp.mtu);
+
+	ipc_send_rsp_full(sco_ipc, SCO_SERVICE_ID, SCO_OP_GET_FD,
+							sizeof(rsp), &rsp, fd);
+
+	return;
+
+failed:
+	ipc_send_rsp(sco_ipc, SCO_SERVICE_ID, SCO_OP_STATUS, SCO_STATUS_FAILED);
+}
+
+static const struct ipc_handler sco_handlers[] = {
+	/* SCO_OP_GET_FD */
+	{ bt_sco_get_fd, false, sizeof(struct sco_cmd_get_fd) }
+};
+
+static void bt_sco_unregister(void)
+{
+	DBG("");
+
+	ipc_cleanup(sco_ipc);
+	sco_ipc = NULL;
+}
+
+static bool bt_sco_register(ipc_disconnect_cb disconnect)
+{
+	DBG("");
+
+	sco_ipc = ipc_init(BLUEZ_SCO_SK_PATH, sizeof(BLUEZ_SCO_SK_PATH),
+				SCO_SERVICE_ID, false, disconnect, NULL);
+	if (!sco_ipc)
+		return false;
+
+	ipc_register(sco_ipc, SCO_SERVICE_ID, sco_handlers,
+						G_N_ELEMENTS(sco_handlers));
+
+	return true;
+}
+
+bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode,
+								int max_clients)
+{
+	DBG("mode 0x%x max_clients %d", mode, max_clients);
+
+	bacpy(&adapter_addr, addr);
+
+	if (max_clients < 1)
+		return false;
+
+	devices = queue_new();
+
+	if (!enable_hsp_ag())
+		goto failed_queue;
+
+	sco = bt_sco_new(addr);
+	if (!sco)
+		goto failed_hsp;
+
+	bt_sco_set_confirm_cb(sco, confirm_sco_cb);
+	bt_sco_set_connect_cb(sco, connect_sco_cb);
+	bt_sco_set_disconnect_cb(sco, disconnect_sco_cb);
+
+	if (mode == HAL_MODE_HANDSFREE_HSP_ONLY)
+		goto done;
+
+	hfp_ag_features = HFP_AG_FEATURES;
+
+	if (mode == HAL_MODE_HANDSFREE_HFP_WBS)
+		hfp_ag_features |= HFP_AG_FEAT_CODEC;
+
+	if (enable_hfp_ag())
+		goto done;
+
+	bt_sco_unref(sco);
+	sco = NULL;
+	hfp_ag_features = 0;
+failed_hsp:
+	cleanup_hsp_ag();
+failed_queue:
+	queue_destroy(devices, NULL);
+	devices = NULL;
+
+	return false;
+
+done:
+	hal_ipc = ipc;
+	ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	bt_sco_register(NULL);
+
+	max_hfp_clients = max_clients;
+
+	return true;
+}
+
+void bt_handsfree_unregister(void)
+{
+	DBG("");
+
+	bt_sco_unregister();
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_HANDSFREE);
+	hal_ipc = NULL;
+
+	cleanup_hfp_ag();
+	cleanup_hsp_ag();
+	bt_sco_unref(sco);
+	sco = NULL;
+
+	hfp_ag_features = 0;
+
+	queue_destroy(devices, (queue_destroy_func_t) device_destroy);
+	devices = NULL;
+
+	max_hfp_clients = 0;
+}
diff --git a/repo/android/handsfree.h b/repo/android/handsfree.h
new file mode 100644
index 0000000..d4fd649
--- /dev/null
+++ b/repo/android/handsfree.h
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode,
+							int max_clients);
+void bt_handsfree_unregister(void);
diff --git a/repo/android/hardware/audio.h b/repo/android/hardware/audio.h
new file mode 100644
index 0000000..3cc2be5
--- /dev/null
+++ b/repo/android/hardware/audio.h
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_AUDIO_HAL_INTERFACE_H
+#define ANDROID_AUDIO_HAL_INTERFACE_H
+
+#include <stdint.h>
+#include <strings.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <hardware/hardware.h>
+#include <system/audio.h>
+#include <hardware/audio_effect.h>
+
+__BEGIN_DECLS
+
+/**
+ * The id of this module
+ */
+#define AUDIO_HARDWARE_MODULE_ID "audio"
+
+/**
+ * Name of the audio devices to open
+ */
+#define AUDIO_HARDWARE_INTERFACE "audio_hw_if"
+
+
+/* Use version 0.1 to be compatible with first generation of audio hw module with version_major
+ * hardcoded to 1. No audio module API change.
+ */
+#define AUDIO_MODULE_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(0, 1)
+#define AUDIO_MODULE_API_VERSION_CURRENT AUDIO_MODULE_API_VERSION_0_1
+
+/* First generation of audio devices had version hardcoded to 0. all devices with versions < 1.0
+ * will be considered of first generation API.
+ */
+#define AUDIO_DEVICE_API_VERSION_0_0 HARDWARE_DEVICE_API_VERSION(0, 0)
+#define AUDIO_DEVICE_API_VERSION_1_0 HARDWARE_DEVICE_API_VERSION(1, 0)
+#define AUDIO_DEVICE_API_VERSION_2_0 HARDWARE_DEVICE_API_VERSION(2, 0)
+#define AUDIO_DEVICE_API_VERSION_3_0 HARDWARE_DEVICE_API_VERSION(3, 0)
+#define AUDIO_DEVICE_API_VERSION_CURRENT AUDIO_DEVICE_API_VERSION_3_0
+/* Minimal audio HAL version supported by the audio framework */
+#define AUDIO_DEVICE_API_VERSION_MIN AUDIO_DEVICE_API_VERSION_2_0
+
+/**
+ * List of known audio HAL modules. This is the base name of the audio HAL
+ * library composed of the "audio." prefix, one of the base names below and
+ * a suffix specific to the device.
+ * e.g: audio.primary.goldfish.so or audio.a2dp.default.so
+ */
+
+#define AUDIO_HARDWARE_MODULE_ID_PRIMARY "primary"
+#define AUDIO_HARDWARE_MODULE_ID_A2DP "a2dp"
+#define AUDIO_HARDWARE_MODULE_ID_USB "usb"
+#define AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX "r_submix"
+#define AUDIO_HARDWARE_MODULE_ID_CODEC_OFFLOAD "codec_offload"
+
+/**************************************/
+
+/**
+ *  standard audio parameters that the HAL may need to handle
+ */
+
+/**
+ *  audio device parameters
+ */
+
+/* BT SCO Noise Reduction + Echo Cancellation parameters */
+#define AUDIO_PARAMETER_KEY_BT_NREC "bt_headset_nrec"
+#define AUDIO_PARAMETER_VALUE_ON "on"
+#define AUDIO_PARAMETER_VALUE_OFF "off"
+
+/* TTY mode selection */
+#define AUDIO_PARAMETER_KEY_TTY_MODE "tty_mode"
+#define AUDIO_PARAMETER_VALUE_TTY_OFF "tty_off"
+#define AUDIO_PARAMETER_VALUE_TTY_VCO "tty_vco"
+#define AUDIO_PARAMETER_VALUE_TTY_HCO "tty_hco"
+#define AUDIO_PARAMETER_VALUE_TTY_FULL "tty_full"
+
+/* Hearing Aid Compatibility - Telecoil (HAC-T) mode on/off
+   Strings must be in sync with CallFeaturesSetting.java */
+#define AUDIO_PARAMETER_KEY_HAC "HACSetting"
+#define AUDIO_PARAMETER_VALUE_HAC_ON "ON"
+#define AUDIO_PARAMETER_VALUE_HAC_OFF "OFF"
+
+/* A2DP sink address set by framework */
+#define AUDIO_PARAMETER_A2DP_SINK_ADDRESS "a2dp_sink_address"
+
+/* A2DP source address set by framework */
+#define AUDIO_PARAMETER_A2DP_SOURCE_ADDRESS "a2dp_source_address"
+
+/* Screen state */
+#define AUDIO_PARAMETER_KEY_SCREEN_STATE "screen_state"
+
+/* Bluetooth SCO wideband */
+#define AUDIO_PARAMETER_KEY_BT_SCO_WB "bt_wbs"
+
+
+/**
+ *  audio stream parameters
+ */
+
+#define AUDIO_PARAMETER_STREAM_ROUTING "routing"             /* audio_devices_t */
+#define AUDIO_PARAMETER_STREAM_FORMAT "format"               /* audio_format_t */
+#define AUDIO_PARAMETER_STREAM_CHANNELS "channels"           /* audio_channel_mask_t */
+#define AUDIO_PARAMETER_STREAM_FRAME_COUNT "frame_count"     /* size_t */
+#define AUDIO_PARAMETER_STREAM_INPUT_SOURCE "input_source"   /* audio_source_t */
+#define AUDIO_PARAMETER_STREAM_SAMPLING_RATE "sampling_rate" /* uint32_t */
+
+#define AUDIO_PARAMETER_DEVICE_DISCONNECT "disconnect"      /* audio_devices_t */
+
+/* Query supported formats. The response is a '|' separated list of strings from
+ * audio_format_t enum e.g: "sup_formats=AUDIO_FORMAT_PCM_16_BIT" */
+#define AUDIO_PARAMETER_STREAM_SUP_FORMATS "sup_formats"
+/* Query supported channel masks. The response is a '|' separated list of strings from
+ * audio_channel_mask_t enum e.g: "sup_channels=AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_MONO" */
+#define AUDIO_PARAMETER_STREAM_SUP_CHANNELS "sup_channels"
+/* Query supported sampling rates. The response is a '|' separated list of integer values e.g:
+ * "sup_sampling_rates=44100|48000" */
+#define AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES "sup_sampling_rates"
+
+/* Get the HW synchronization source used for an output stream.
+ * Return a valid source (positive integer) or AUDIO_HW_SYNC_INVALID if an error occurs
+ * or no HW sync source is used. */
+#define AUDIO_PARAMETER_STREAM_HW_AV_SYNC "hw_av_sync"
+
+/**
+ * audio codec parameters
+ */
+
+#define AUDIO_OFFLOAD_CODEC_PARAMS "music_offload_codec_param"
+#define AUDIO_OFFLOAD_CODEC_BIT_PER_SAMPLE "music_offload_bit_per_sample"
+#define AUDIO_OFFLOAD_CODEC_BIT_RATE "music_offload_bit_rate"
+#define AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE "music_offload_avg_bit_rate"
+#define AUDIO_OFFLOAD_CODEC_ID "music_offload_codec_id"
+#define AUDIO_OFFLOAD_CODEC_BLOCK_ALIGN "music_offload_block_align"
+#define AUDIO_OFFLOAD_CODEC_SAMPLE_RATE "music_offload_sample_rate"
+#define AUDIO_OFFLOAD_CODEC_ENCODE_OPTION "music_offload_encode_option"
+#define AUDIO_OFFLOAD_CODEC_NUM_CHANNEL  "music_offload_num_channels"
+#define AUDIO_OFFLOAD_CODEC_DOWN_SAMPLING  "music_offload_down_sampling"
+#define AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES  "delay_samples"
+#define AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES  "padding_samples"
+
+/**************************************/
+
+/* common audio stream parameters and operations */
+struct audio_stream {
+
+    /**
+     * Return the sampling rate in Hz - eg. 44100.
+     */
+    uint32_t (*get_sample_rate)(const struct audio_stream *stream);
+
+    /* currently unused - use set_parameters with key
+     *    AUDIO_PARAMETER_STREAM_SAMPLING_RATE
+     */
+    int (*set_sample_rate)(struct audio_stream *stream, uint32_t rate);
+
+    /**
+     * Return size of input/output buffer in bytes for this stream - eg. 4800.
+     * It should be a multiple of the frame size.  See also get_input_buffer_size.
+     */
+    size_t (*get_buffer_size)(const struct audio_stream *stream);
+
+    /**
+     * Return the channel mask -
+     *  e.g. AUDIO_CHANNEL_OUT_STEREO or AUDIO_CHANNEL_IN_STEREO
+     */
+    audio_channel_mask_t (*get_channels)(const struct audio_stream *stream);
+
+    /**
+     * Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT
+     */
+    audio_format_t (*get_format)(const struct audio_stream *stream);
+
+    /* currently unused - use set_parameters with key
+     *     AUDIO_PARAMETER_STREAM_FORMAT
+     */
+    int (*set_format)(struct audio_stream *stream, audio_format_t format);
+
+    /**
+     * Put the audio hardware input/output into standby mode.
+     * Driver should exit from standby mode at the next I/O operation.
+     * Returns 0 on success and <0 on failure.
+     */
+    int (*standby)(struct audio_stream *stream);
+
+    /** dump the state of the audio input/output device */
+    int (*dump)(const struct audio_stream *stream, int fd);
+
+    /** Return the set of device(s) which this stream is connected to */
+    audio_devices_t (*get_device)(const struct audio_stream *stream);
+
+    /**
+     * Currently unused - set_device() corresponds to set_parameters() with key
+     * AUDIO_PARAMETER_STREAM_ROUTING for both input and output.
+     * AUDIO_PARAMETER_STREAM_INPUT_SOURCE is an additional information used by
+     * input streams only.
+     */
+    int (*set_device)(struct audio_stream *stream, audio_devices_t device);
+
+    /**
+     * set/get audio stream parameters. The function accepts a list of
+     * parameter key value pairs in the form: key1=value1;key2=value2;...
+     *
+     * Some keys are reserved for standard parameters (See AudioParameter class)
+     *
+     * If the implementation does not accept a parameter change while
+     * the output is active but the parameter is acceptable otherwise, it must
+     * return -ENOSYS.
+     *
+     * The audio flinger will put the stream in standby and then change the
+     * parameter value.
+     */
+    int (*set_parameters)(struct audio_stream *stream, const char *kv_pairs);
+
+    /*
+     * Returns a pointer to a heap allocated string. The caller is responsible
+     * for freeing the memory for it using free().
+     */
+    char * (*get_parameters)(const struct audio_stream *stream,
+                             const char *keys);
+    int (*add_audio_effect)(const struct audio_stream *stream,
+                             effect_handle_t effect);
+    int (*remove_audio_effect)(const struct audio_stream *stream,
+                             effect_handle_t effect);
+};
+typedef struct audio_stream audio_stream_t;
+
+/* type of asynchronous write callback events. Mutually exclusive */
+typedef enum {
+    STREAM_CBK_EVENT_WRITE_READY, /* non blocking write completed */
+    STREAM_CBK_EVENT_DRAIN_READY  /* drain completed */
+} stream_callback_event_t;
+
+typedef int (*stream_callback_t)(stream_callback_event_t event, void *param, void *cookie);
+
+/* type of drain requested to audio_stream_out->drain(). Mutually exclusive */
+typedef enum {
+    AUDIO_DRAIN_ALL,            /* drain() returns when all data has been played */
+    AUDIO_DRAIN_EARLY_NOTIFY    /* drain() returns a short time before all data
+                                   from the current track has been played to
+                                   give time for gapless track switch */
+} audio_drain_type_t;
+
+/**
+ * audio_stream_out is the abstraction interface for the audio output hardware.
+ *
+ * It provides information about various properties of the audio output
+ * hardware driver.
+ */
+
+struct audio_stream_out {
+    /**
+     * Common methods of the audio stream out.  This *must* be the first member of audio_stream_out
+     * as users of this structure will cast a audio_stream to audio_stream_out pointer in contexts
+     * where it's known the audio_stream references an audio_stream_out.
+     */
+    struct audio_stream common;
+
+    /**
+     * Return the audio hardware driver estimated latency in milliseconds.
+     */
+    uint32_t (*get_latency)(const struct audio_stream_out *stream);
+
+    /**
+     * Use this method in situations where audio mixing is done in the
+     * hardware. This method serves as a direct interface with hardware,
+     * allowing you to directly set the volume as apposed to via the framework.
+     * This method might produce multiple PCM outputs or hardware accelerated
+     * codecs, such as MP3 or AAC.
+     */
+    int (*set_volume)(struct audio_stream_out *stream, float left, float right);
+
+    /**
+     * Write audio buffer to driver. Returns number of bytes written, or a
+     * negative status_t. If at least one frame was written successfully prior to the error,
+     * it is suggested that the driver return that successful (short) byte count
+     * and then return an error in the subsequent call.
+     *
+     * If set_callback() has previously been called to enable non-blocking mode
+     * the write() is not allowed to block. It must write only the number of
+     * bytes that currently fit in the driver/hardware buffer and then return
+     * this byte count. If this is less than the requested write size the
+     * callback function must be called when more space is available in the
+     * driver/hardware buffer.
+     */
+    ssize_t (*write)(struct audio_stream_out *stream, const void* buffer,
+                     size_t bytes);
+
+    /* return the number of audio frames written by the audio dsp to DAC since
+     * the output has exited standby
+     */
+    int (*get_render_position)(const struct audio_stream_out *stream,
+                               uint32_t *dsp_frames);
+
+    /**
+     * get the local time at which the next write to the audio driver will be presented.
+     * The units are microseconds, where the epoch is decided by the local audio HAL.
+     */
+    int (*get_next_write_timestamp)(const struct audio_stream_out *stream,
+                                    int64_t *timestamp);
+
+    /**
+     * set the callback function for notifying completion of non-blocking
+     * write and drain.
+     * Calling this function implies that all future write() and drain()
+     * must be non-blocking and use the callback to signal completion.
+     */
+    int (*set_callback)(struct audio_stream_out *stream,
+            stream_callback_t callback, void *cookie);
+
+    /**
+     * Notifies to the audio driver to stop playback however the queued buffers are
+     * retained by the hardware. Useful for implementing pause/resume. Empty implementation
+     * if not supported however should be implemented for hardware with non-trivial
+     * latency. In the pause state audio hardware could still be using power. User may
+     * consider calling suspend after a timeout.
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     */
+    int (*pause)(struct audio_stream_out* stream);
+
+    /**
+     * Notifies to the audio driver to resume playback following a pause.
+     * Returns error if called without matching pause.
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     */
+    int (*resume)(struct audio_stream_out* stream);
+
+    /**
+     * Requests notification when data buffered by the driver/hardware has
+     * been played. If set_callback() has previously been called to enable
+     * non-blocking mode, the drain() must not block, instead it should return
+     * quickly and completion of the drain is notified through the callback.
+     * If set_callback() has not been called, the drain() must block until
+     * completion.
+     * If type==AUDIO_DRAIN_ALL, the drain completes when all previously written
+     * data has been played.
+     * If type==AUDIO_DRAIN_EARLY_NOTIFY, the drain completes shortly before all
+     * data for the current track has played to allow time for the framework
+     * to perform a gapless track switch.
+     *
+     * Drain must return immediately on stop() and flush() call
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     */
+    int (*drain)(struct audio_stream_out* stream, audio_drain_type_t type );
+
+    /**
+     * Notifies to the audio driver to flush the queued data. Stream must already
+     * be paused before calling flush().
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     */
+   int (*flush)(struct audio_stream_out* stream);
+
+    /**
+     * Return a recent count of the number of audio frames presented to an external observer.
+     * This excludes frames which have been written but are still in the pipeline.
+     * The count is not reset to zero when output enters standby.
+     * Also returns the value of CLOCK_MONOTONIC as of this presentation count.
+     * The returned count is expected to be 'recent',
+     * but does not need to be the most recent possible value.
+     * However, the associated time should correspond to whatever count is returned.
+     * Example:  assume that N+M frames have been presented, where M is a 'small' number.
+     * Then it is permissible to return N instead of N+M,
+     * and the timestamp should correspond to N rather than N+M.
+     * The terms 'recent' and 'small' are not defined.
+     * They reflect the quality of the implementation.
+     *
+     * 3.0 and higher only.
+     */
+    int (*get_presentation_position)(const struct audio_stream_out *stream,
+                               uint64_t *frames, struct timespec *timestamp);
+
+};
+typedef struct audio_stream_out audio_stream_out_t;
+
+struct audio_stream_in {
+    /**
+     * Common methods of the audio stream in.  This *must* be the first member of audio_stream_in
+     * as users of this structure will cast a audio_stream to audio_stream_in pointer in contexts
+     * where it's known the audio_stream references an audio_stream_in.
+     */
+    struct audio_stream common;
+
+    /** set the input gain for the audio driver. This method is for
+     *  for future use */
+    int (*set_gain)(struct audio_stream_in *stream, float gain);
+
+    /** Read audio buffer in from audio driver. Returns number of bytes read, or a
+     *  negative status_t. If at least one frame was read prior to the error,
+     *  read should return that byte count and then return an error in the subsequent call.
+     */
+    ssize_t (*read)(struct audio_stream_in *stream, void* buffer,
+                    size_t bytes);
+
+    /**
+     * Return the amount of input frames lost in the audio driver since the
+     * last call of this function.
+     * Audio driver is expected to reset the value to 0 and restart counting
+     * upon returning the current value by this function call.
+     * Such loss typically occurs when the user space process is blocked
+     * longer than the capacity of audio driver buffers.
+     *
+     * Unit: the number of input audio frames
+     */
+    uint32_t (*get_input_frames_lost)(struct audio_stream_in *stream);
+};
+typedef struct audio_stream_in audio_stream_in_t;
+
+/**
+ * return the frame size (number of bytes per sample).
+ *
+ * Deprecated: use audio_stream_out_frame_size() or audio_stream_in_frame_size() instead.
+ */
+__attribute__((__deprecated__))
+static inline size_t audio_stream_frame_size(const struct audio_stream *s)
+{
+    size_t chan_samp_sz;
+    audio_format_t format = s->get_format(s);
+
+    if (audio_is_linear_pcm(format)) {
+        chan_samp_sz = audio_bytes_per_sample(format);
+        return popcount(s->get_channels(s)) * chan_samp_sz;
+    }
+
+    return sizeof(int8_t);
+}
+
+/**
+ * return the frame size (number of bytes per sample) of an output stream.
+ */
+static inline size_t audio_stream_out_frame_size(const struct audio_stream_out *s)
+{
+    size_t chan_samp_sz;
+    audio_format_t format = s->common.get_format(&s->common);
+
+    if (audio_is_linear_pcm(format)) {
+        chan_samp_sz = audio_bytes_per_sample(format);
+        return audio_channel_count_from_out_mask(s->common.get_channels(&s->common)) * chan_samp_sz;
+    }
+
+    return sizeof(int8_t);
+}
+
+/**
+ * return the frame size (number of bytes per sample) of an input stream.
+ */
+static inline size_t audio_stream_in_frame_size(const struct audio_stream_in *s)
+{
+    size_t chan_samp_sz;
+    audio_format_t format = s->common.get_format(&s->common);
+
+    if (audio_is_linear_pcm(format)) {
+        chan_samp_sz = audio_bytes_per_sample(format);
+        return audio_channel_count_from_in_mask(s->common.get_channels(&s->common)) * chan_samp_sz;
+    }
+
+    return sizeof(int8_t);
+}
+
+/**********************************************************************/
+
+/**
+ * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
+ * and the fields of this data structure must begin with hw_module_t
+ * followed by module specific information.
+ */
+struct audio_module {
+    struct hw_module_t common;
+};
+
+struct audio_hw_device {
+    /**
+     * Common methods of the audio device.  This *must* be the first member of audio_hw_device
+     * as users of this structure will cast a hw_device_t to audio_hw_device pointer in contexts
+     * where it's known the hw_device_t references an audio_hw_device.
+     */
+    struct hw_device_t common;
+
+    /**
+     * used by audio flinger to enumerate what devices are supported by
+     * each audio_hw_device implementation.
+     *
+     * Return value is a bitmask of 1 or more values of audio_devices_t
+     *
+     * NOTE: audio HAL implementations starting with
+     * AUDIO_DEVICE_API_VERSION_2_0 do not implement this function.
+     * All supported devices should be listed in audio_policy.conf
+     * file and the audio policy manager must choose the appropriate
+     * audio module based on information in this file.
+     */
+    uint32_t (*get_supported_devices)(const struct audio_hw_device *dev);
+
+    /**
+     * check to see if the audio hardware interface has been initialized.
+     * returns 0 on success, -ENODEV on failure.
+     */
+    int (*init_check)(const struct audio_hw_device *dev);
+
+    /** set the audio volume of a voice call. Range is between 0.0 and 1.0 */
+    int (*set_voice_volume)(struct audio_hw_device *dev, float volume);
+
+    /**
+     * set the audio volume for all audio activities other than voice call.
+     * Range between 0.0 and 1.0. If any value other than 0 is returned,
+     * the software mixer will emulate this capability.
+     */
+    int (*set_master_volume)(struct audio_hw_device *dev, float volume);
+
+    /**
+     * Get the current master volume value for the HAL, if the HAL supports
+     * master volume control.  AudioFlinger will query this value from the
+     * primary audio HAL when the service starts and use the value for setting
+     * the initial master volume across all HALs.  HALs which do not support
+     * this method may leave it set to NULL.
+     */
+    int (*get_master_volume)(struct audio_hw_device *dev, float *volume);
+
+    /**
+     * set_mode is called when the audio mode changes. AUDIO_MODE_NORMAL mode
+     * is for standard audio playback, AUDIO_MODE_RINGTONE when a ringtone is
+     * playing, and AUDIO_MODE_IN_CALL when a call is in progress.
+     */
+    int (*set_mode)(struct audio_hw_device *dev, audio_mode_t mode);
+
+    /* mic mute */
+    int (*set_mic_mute)(struct audio_hw_device *dev, bool state);
+    int (*get_mic_mute)(const struct audio_hw_device *dev, bool *state);
+
+    /* set/get global audio parameters */
+    int (*set_parameters)(struct audio_hw_device *dev, const char *kv_pairs);
+
+    /*
+     * Returns a pointer to a heap allocated string. The caller is responsible
+     * for freeing the memory for it using free().
+     */
+    char * (*get_parameters)(const struct audio_hw_device *dev,
+                             const char *keys);
+
+    /* Returns audio input buffer size according to parameters passed or
+     * 0 if one of the parameters is not supported.
+     * See also get_buffer_size which is for a particular stream.
+     */
+    size_t (*get_input_buffer_size)(const struct audio_hw_device *dev,
+                                    const struct audio_config *config);
+
+    /** This method creates and opens the audio hardware output stream.
+     * The "address" parameter qualifies the "devices" audio device type if needed.
+     * The format format depends on the device type:
+     * - Bluetooth devices use the MAC address of the device in the form "00:11:22:AA:BB:CC"
+     * - USB devices use the ALSA card and device numbers in the form  "card=X;device=Y"
+     * - Other devices may use a number or any other string.
+     */
+
+    int (*open_output_stream)(struct audio_hw_device *dev,
+                              audio_io_handle_t handle,
+                              audio_devices_t devices,
+                              audio_output_flags_t flags,
+                              struct audio_config *config,
+                              struct audio_stream_out **stream_out,
+                              const char *address);
+
+    void (*close_output_stream)(struct audio_hw_device *dev,
+                                struct audio_stream_out* stream_out);
+
+    /** This method creates and opens the audio hardware input stream */
+    int (*open_input_stream)(struct audio_hw_device *dev,
+                             audio_io_handle_t handle,
+                             audio_devices_t devices,
+                             struct audio_config *config,
+                             struct audio_stream_in **stream_in,
+                             audio_input_flags_t flags,
+                             const char *address,
+                             audio_source_t source);
+
+    void (*close_input_stream)(struct audio_hw_device *dev,
+                               struct audio_stream_in *stream_in);
+
+    /** This method dumps the state of the audio hardware */
+    int (*dump)(const struct audio_hw_device *dev, int fd);
+
+    /**
+     * set the audio mute status for all audio activities.  If any value other
+     * than 0 is returned, the software mixer will emulate this capability.
+     */
+    int (*set_master_mute)(struct audio_hw_device *dev, bool mute);
+
+    /**
+     * Get the current master mute status for the HAL, if the HAL supports
+     * master mute control.  AudioFlinger will query this value from the primary
+     * audio HAL when the service starts and use the value for setting the
+     * initial master mute across all HALs.  HALs which do not support this
+     * method may leave it set to NULL.
+     */
+    int (*get_master_mute)(struct audio_hw_device *dev, bool *mute);
+
+    /**
+     * Routing control
+     */
+
+    /* Creates an audio patch between several source and sink ports.
+     * The handle is allocated by the HAL and should be unique for this
+     * audio HAL module. */
+    int (*create_audio_patch)(struct audio_hw_device *dev,
+                               unsigned int num_sources,
+                               const struct audio_port_config *sources,
+                               unsigned int num_sinks,
+                               const struct audio_port_config *sinks,
+                               audio_patch_handle_t *handle);
+
+    /* Release an audio patch */
+    int (*release_audio_patch)(struct audio_hw_device *dev,
+                               audio_patch_handle_t handle);
+
+    /* Fills the list of supported attributes for a given audio port.
+     * As input, "port" contains the information (type, role, address etc...)
+     * needed by the HAL to identify the port.
+     * As output, "port" contains possible attributes (sampling rates, formats,
+     * channel masks, gain controllers...) for this port.
+     */
+    int (*get_audio_port)(struct audio_hw_device *dev,
+                          struct audio_port *port);
+
+    /* Set audio port configuration */
+    int (*set_audio_port_config)(struct audio_hw_device *dev,
+                         const struct audio_port_config *config);
+
+};
+typedef struct audio_hw_device audio_hw_device_t;
+
+/** convenience API for opening and closing a supported device */
+
+static inline int audio_hw_device_open(const struct hw_module_t* module,
+                                       struct audio_hw_device** device)
+{
+    return module->methods->open(module, AUDIO_HARDWARE_INTERFACE,
+                                 (struct hw_device_t**)device);
+}
+
+static inline int audio_hw_device_close(struct audio_hw_device* device)
+{
+    return device->common.close(&device->common);
+}
+
+
+__END_DECLS
+
+#endif  // ANDROID_AUDIO_INTERFACE_H
diff --git a/repo/android/hardware/audio_effect.h b/repo/android/hardware/audio_effect.h
new file mode 100644
index 0000000..69ea896
--- /dev/null
+++ b/repo/android/hardware/audio_effect.h
@@ -0,0 +1,1010 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_AUDIO_EFFECT_H
+#define ANDROID_AUDIO_EFFECT_H
+
+#include <errno.h>
+#include <stdint.h>
+#include <strings.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <system/audio.h>
+
+
+__BEGIN_DECLS
+
+
+/////////////////////////////////////////////////
+//      Common Definitions
+/////////////////////////////////////////////////
+
+//
+//--- Effect descriptor structure effect_descriptor_t
+//
+
+// Unique effect ID (can be generated from the following site:
+//  http://www.itu.int/ITU-T/asn1/uuid.html)
+// This format is used for both "type" and "uuid" fields of the effect descriptor structure.
+// - When used for effect type and the engine is implementing and effect corresponding to a standard
+// OpenSL ES interface, this ID must be the one defined in OpenSLES_IID.h for that interface.
+// - When used as uuid, it should be a unique UUID for this particular implementation.
+typedef struct effect_uuid_s {
+    uint32_t timeLow;
+    uint16_t timeMid;
+    uint16_t timeHiAndVersion;
+    uint16_t clockSeq;
+    uint8_t node[6];
+} effect_uuid_t;
+
+// Maximum length of character strings in structures defines by this API.
+#define EFFECT_STRING_LEN_MAX 64
+
+// NULL UUID definition (matches SL_IID_NULL_)
+#define EFFECT_UUID_INITIALIZER { 0xec7178ec, 0xe5e1, 0x4432, 0xa3f4, \
+                                  { 0x46, 0x57, 0xe6, 0x79, 0x52, 0x10 } }
+static const effect_uuid_t EFFECT_UUID_NULL_ = EFFECT_UUID_INITIALIZER;
+static const effect_uuid_t * const EFFECT_UUID_NULL = &EFFECT_UUID_NULL_;
+static const char * const EFFECT_UUID_NULL_STR = "ec7178ec-e5e1-4432-a3f4-4657e6795210";
+
+
+// The effect descriptor contains necessary information to facilitate the enumeration of the effect
+// engines present in a library.
+typedef struct effect_descriptor_s {
+    effect_uuid_t type;     // UUID of to the OpenSL ES interface implemented by this effect
+    effect_uuid_t uuid;     // UUID for this particular implementation
+    uint32_t apiVersion;    // Version of the effect control API implemented
+    uint32_t flags;         // effect engine capabilities/requirements flags (see below)
+    uint16_t cpuLoad;       // CPU load indication (see below)
+    uint16_t memoryUsage;   // Data Memory usage (see below)
+    char    name[EFFECT_STRING_LEN_MAX];   // human readable effect name
+    char    implementor[EFFECT_STRING_LEN_MAX];    // human readable effect implementor name
+} effect_descriptor_t;
+
+// CPU load and memory usage indication: each effect implementation must provide an indication of
+// its CPU and memory usage for the audio effect framework to limit the number of effects
+// instantiated at a given time on a given platform.
+// The CPU load is expressed in 0.1 MIPS units as estimated on an ARM9E core (ARMv5TE) with 0 WS.
+// The memory usage is expressed in KB and includes only dynamically allocated memory
+
+// Definitions for flags field of effect descriptor.
+//  +---------------------------+-----------+-----------------------------------
+//  | description               | bits      | values
+//  +---------------------------+-----------+-----------------------------------
+//  | connection mode           | 0..2      | 0 insert: after track process
+//  |                           |           | 1 auxiliary: connect to track auxiliary
+//  |                           |           |  output and use send level
+//  |                           |           | 2 replace: replaces track process function;
+//  |                           |           |   must implement SRC, volume and mono to stereo.
+//  |                           |           | 3 pre processing: applied below audio HAL on input
+//  |                           |           | 4 post processing: applied below audio HAL on output
+//  |                           |           | 5 - 7 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | insertion preference      | 3..5      | 0 none
+//  |                           |           | 1 first of the chain
+//  |                           |           | 2 last of the chain
+//  |                           |           | 3 exclusive (only effect in the insert chain)
+//  |                           |           | 4..7 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | Volume management         | 6..8      | 0 none
+//  |                           |           | 1 implements volume control
+//  |                           |           | 2 requires volume indication
+//  |                           |           | 4 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | Device indication         | 9..11     | 0 none
+//  |                           |           | 1 requires device updates
+//  |                           |           | 2, 4 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | Sample input mode         | 12..13    | 1 direct: process() function or EFFECT_CMD_SET_CONFIG
+//  |                           |           |   command must specify a buffer descriptor
+//  |                           |           | 2 provider: process() function uses the
+//  |                           |           |   bufferProvider indicated by the
+//  |                           |           |   EFFECT_CMD_SET_CONFIG command to request input.
+//  |                           |           |   buffers.
+//  |                           |           | 3 both: both input modes are supported
+//  +---------------------------+-----------+-----------------------------------
+//  | Sample output mode        | 14..15    | 1 direct: process() function or EFFECT_CMD_SET_CONFIG
+//  |                           |           |   command must specify a buffer descriptor
+//  |                           |           | 2 provider: process() function uses the
+//  |                           |           |   bufferProvider indicated by the
+//  |                           |           |   EFFECT_CMD_SET_CONFIG command to request output
+//  |                           |           |   buffers.
+//  |                           |           | 3 both: both output modes are supported
+//  +---------------------------+-----------+-----------------------------------
+//  | Hardware acceleration     | 16..17    | 0 No hardware acceleration
+//  |                           |           | 1 non tunneled hw acceleration: the process() function
+//  |                           |           |   reads the samples, send them to HW accelerated
+//  |                           |           |   effect processor, reads back the processed samples
+//  |                           |           |   and returns them to the output buffer.
+//  |                           |           | 2 tunneled hw acceleration: the process() function is
+//  |                           |           |   transparent. The effect interface is only used to
+//  |                           |           |   control the effect engine. This mode is relevant for
+//  |                           |           |   global effects actually applied by the audio
+//  |                           |           |   hardware on the output stream.
+//  +---------------------------+-----------+-----------------------------------
+//  | Audio Mode indication     | 18..19    | 0 none
+//  |                           |           | 1 requires audio mode updates
+//  |                           |           | 2..3 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | Audio source indication   | 20..21    | 0 none
+//  |                           |           | 1 requires audio source updates
+//  |                           |           | 2..3 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | Effect offload supported  | 22        | 0 The effect cannot be offloaded to an audio DSP
+//  |                           |           | 1 The effect can be offloaded to an audio DSP
+//  +---------------------------+-----------+-----------------------------------
+
+// Insert mode
+#define EFFECT_FLAG_TYPE_SHIFT          0
+#define EFFECT_FLAG_TYPE_SIZE           3
+#define EFFECT_FLAG_TYPE_MASK           (((1 << EFFECT_FLAG_TYPE_SIZE) -1) \
+                                            << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_INSERT         (0 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_AUXILIARY      (1 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_REPLACE        (2 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_PRE_PROC       (3 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_POST_PROC      (4 << EFFECT_FLAG_TYPE_SHIFT)
+
+// Insert preference
+#define EFFECT_FLAG_INSERT_SHIFT        (EFFECT_FLAG_TYPE_SHIFT + EFFECT_FLAG_TYPE_SIZE)
+#define EFFECT_FLAG_INSERT_SIZE         3
+#define EFFECT_FLAG_INSERT_MASK         (((1 << EFFECT_FLAG_INSERT_SIZE) -1) \
+                                            << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_ANY          (0 << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_FIRST        (1 << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_LAST         (2 << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_EXCLUSIVE    (3 << EFFECT_FLAG_INSERT_SHIFT)
+
+
+// Volume control
+#define EFFECT_FLAG_VOLUME_SHIFT        (EFFECT_FLAG_INSERT_SHIFT + EFFECT_FLAG_INSERT_SIZE)
+#define EFFECT_FLAG_VOLUME_SIZE         3
+#define EFFECT_FLAG_VOLUME_MASK         (((1 << EFFECT_FLAG_VOLUME_SIZE) -1) \
+                                            << EFFECT_FLAG_VOLUME_SHIFT)
+#define EFFECT_FLAG_VOLUME_CTRL         (1 << EFFECT_FLAG_VOLUME_SHIFT)
+#define EFFECT_FLAG_VOLUME_IND          (2 << EFFECT_FLAG_VOLUME_SHIFT)
+#define EFFECT_FLAG_VOLUME_NONE         (0 << EFFECT_FLAG_VOLUME_SHIFT)
+
+// Device indication
+#define EFFECT_FLAG_DEVICE_SHIFT        (EFFECT_FLAG_VOLUME_SHIFT + EFFECT_FLAG_VOLUME_SIZE)
+#define EFFECT_FLAG_DEVICE_SIZE         3
+#define EFFECT_FLAG_DEVICE_MASK         (((1 << EFFECT_FLAG_DEVICE_SIZE) -1) \
+                                            << EFFECT_FLAG_DEVICE_SHIFT)
+#define EFFECT_FLAG_DEVICE_IND          (1 << EFFECT_FLAG_DEVICE_SHIFT)
+#define EFFECT_FLAG_DEVICE_NONE         (0 << EFFECT_FLAG_DEVICE_SHIFT)
+
+// Sample input modes
+#define EFFECT_FLAG_INPUT_SHIFT         (EFFECT_FLAG_DEVICE_SHIFT + EFFECT_FLAG_DEVICE_SIZE)
+#define EFFECT_FLAG_INPUT_SIZE          2
+#define EFFECT_FLAG_INPUT_MASK          (((1 << EFFECT_FLAG_INPUT_SIZE) -1) \
+                                            << EFFECT_FLAG_INPUT_SHIFT)
+#define EFFECT_FLAG_INPUT_DIRECT        (1 << EFFECT_FLAG_INPUT_SHIFT)
+#define EFFECT_FLAG_INPUT_PROVIDER      (2 << EFFECT_FLAG_INPUT_SHIFT)
+#define EFFECT_FLAG_INPUT_BOTH          (3 << EFFECT_FLAG_INPUT_SHIFT)
+
+// Sample output modes
+#define EFFECT_FLAG_OUTPUT_SHIFT        (EFFECT_FLAG_INPUT_SHIFT + EFFECT_FLAG_INPUT_SIZE)
+#define EFFECT_FLAG_OUTPUT_SIZE         2
+#define EFFECT_FLAG_OUTPUT_MASK         (((1 << EFFECT_FLAG_OUTPUT_SIZE) -1) \
+                                            << EFFECT_FLAG_OUTPUT_SHIFT)
+#define EFFECT_FLAG_OUTPUT_DIRECT       (1 << EFFECT_FLAG_OUTPUT_SHIFT)
+#define EFFECT_FLAG_OUTPUT_PROVIDER     (2 << EFFECT_FLAG_OUTPUT_SHIFT)
+#define EFFECT_FLAG_OUTPUT_BOTH         (3 << EFFECT_FLAG_OUTPUT_SHIFT)
+
+// Hardware acceleration mode
+#define EFFECT_FLAG_HW_ACC_SHIFT        (EFFECT_FLAG_OUTPUT_SHIFT + EFFECT_FLAG_OUTPUT_SIZE)
+#define EFFECT_FLAG_HW_ACC_SIZE         2
+#define EFFECT_FLAG_HW_ACC_MASK         (((1 << EFFECT_FLAG_HW_ACC_SIZE) -1) \
+                                            << EFFECT_FLAG_HW_ACC_SHIFT)
+#define EFFECT_FLAG_HW_ACC_SIMPLE       (1 << EFFECT_FLAG_HW_ACC_SHIFT)
+#define EFFECT_FLAG_HW_ACC_TUNNEL       (2 << EFFECT_FLAG_HW_ACC_SHIFT)
+
+// Audio mode indication
+#define EFFECT_FLAG_AUDIO_MODE_SHIFT    (EFFECT_FLAG_HW_ACC_SHIFT + EFFECT_FLAG_HW_ACC_SIZE)
+#define EFFECT_FLAG_AUDIO_MODE_SIZE     2
+#define EFFECT_FLAG_AUDIO_MODE_MASK     (((1 << EFFECT_FLAG_AUDIO_MODE_SIZE) -1) \
+                                            << EFFECT_FLAG_AUDIO_MODE_SHIFT)
+#define EFFECT_FLAG_AUDIO_MODE_IND      (1 << EFFECT_FLAG_AUDIO_MODE_SHIFT)
+#define EFFECT_FLAG_AUDIO_MODE_NONE     (0 << EFFECT_FLAG_AUDIO_MODE_SHIFT)
+
+// Audio source indication
+#define EFFECT_FLAG_AUDIO_SOURCE_SHIFT  (EFFECT_FLAG_AUDIO_MODE_SHIFT + EFFECT_FLAG_AUDIO_MODE_SIZE)
+#define EFFECT_FLAG_AUDIO_SOURCE_SIZE   2
+#define EFFECT_FLAG_AUDIO_SOURCE_MASK   (((1 << EFFECT_FLAG_AUDIO_SOURCE_SIZE) -1) \
+                                          << EFFECT_FLAG_AUDIO_SOURCE_SHIFT)
+#define EFFECT_FLAG_AUDIO_SOURCE_IND    (1 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT)
+#define EFFECT_FLAG_AUDIO_SOURCE_NONE   (0 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT)
+
+// Effect offload indication
+#define EFFECT_FLAG_OFFLOAD_SHIFT       (EFFECT_FLAG_AUDIO_SOURCE_SHIFT + \
+                                                    EFFECT_FLAG_AUDIO_SOURCE_SIZE)
+#define EFFECT_FLAG_OFFLOAD_SIZE        1
+#define EFFECT_FLAG_OFFLOAD_MASK        (((1 << EFFECT_FLAG_OFFLOAD_SIZE) -1) \
+                                          << EFFECT_FLAG_OFFLOAD_SHIFT)
+#define EFFECT_FLAG_OFFLOAD_SUPPORTED   (1 << EFFECT_FLAG_OFFLOAD_SHIFT)
+
+#define EFFECT_MAKE_API_VERSION(M, m)  (((M)<<16) | ((m) & 0xFFFF))
+#define EFFECT_API_VERSION_MAJOR(v)    ((v)>>16)
+#define EFFECT_API_VERSION_MINOR(v)    ((m) & 0xFFFF)
+
+
+
+/////////////////////////////////////////////////
+//      Effect control interface
+/////////////////////////////////////////////////
+
+// Effect control interface version 2.0
+#define EFFECT_CONTROL_API_VERSION EFFECT_MAKE_API_VERSION(2,0)
+
+// Effect control interface structure: effect_interface_s
+// The effect control interface is exposed by each effect engine implementation. It consists of
+// a set of functions controlling the configuration, activation and process of the engine.
+// The functions are grouped in a structure of type effect_interface_s.
+//
+// Effect control interface handle: effect_handle_t
+// The effect_handle_t serves two purposes regarding the implementation of the effect engine:
+// - 1 it is the address of a pointer to an effect_interface_s structure where the functions
+// of the effect control API for a particular effect are located.
+// - 2 it is the address of the context of a particular effect instance.
+// A typical implementation in the effect library would define a structure as follows:
+// struct effect_module_s {
+//        const struct effect_interface_s *itfe;
+//        effect_config_t config;
+//        effect_context_t context;
+// }
+// The implementation of EffectCreate() function would then allocate a structure of this
+// type and return its address as effect_handle_t
+typedef struct effect_interface_s **effect_handle_t;
+
+
+// Forward definition of type audio_buffer_t
+typedef struct audio_buffer_s audio_buffer_t;
+
+
+
+
+
+
+// Effect control interface definition
+struct effect_interface_s {
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:       process
+    //
+    //    Description:    Effect process function. Takes input samples as specified
+    //          (count and location) in input buffer descriptor and output processed
+    //          samples as specified in output buffer descriptor. If the buffer descriptor
+    //          is not specified the function must use either the buffer or the
+    //          buffer provider function installed by the EFFECT_CMD_SET_CONFIG command.
+    //          The effect framework will call the process() function after the EFFECT_CMD_ENABLE
+    //          command is received and until the EFFECT_CMD_DISABLE is received. When the engine
+    //          receives the EFFECT_CMD_DISABLE command it should turn off the effect gracefully
+    //          and when done indicate that it is OK to stop calling the process() function by
+    //          returning the -ENODATA status.
+    //
+    //    NOTE: the process() function implementation should be "real-time safe" that is
+    //      it should not perform blocking calls: malloc/free, sleep, read/write/open/close,
+    //      pthread_cond_wait/pthread_mutex_lock...
+    //
+    //    Input:
+    //          self:       handle to the effect interface this function
+    //              is called on.
+    //          inBuffer:   buffer descriptor indicating where to read samples to process.
+    //              If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command.
+    //
+    //          outBuffer:   buffer descriptor indicating where to write processed samples.
+    //              If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command.
+    //
+    //    Output:
+    //        returned value:    0 successful operation
+    //                          -ENODATA the engine has finished the disable phase and the framework
+    //                                  can stop calling process()
+    //                          -EINVAL invalid interface handle or
+    //                                  invalid input/output buffer description
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*process)(effect_handle_t self,
+                       audio_buffer_t *inBuffer,
+                       audio_buffer_t *outBuffer);
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:       command
+    //
+    //    Description:    Send a command and receive a response to/from effect engine.
+    //
+    //    Input:
+    //          self:       handle to the effect interface this function
+    //              is called on.
+    //          cmdCode:    command code: the command can be a standardized command defined in
+    //              effect_command_e (see below) or a proprietary command.
+    //          cmdSize:    size of command in bytes
+    //          pCmdData:   pointer to command data
+    //          pReplyData: pointer to reply data
+    //
+    //    Input/Output:
+    //          replySize: maximum size of reply data as input
+    //                      actual size of reply data as output
+    //
+    //    Output:
+    //          returned value: 0       successful operation
+    //                          -EINVAL invalid interface handle or
+    //                                  invalid command/reply size or format according to command code
+    //              The return code should be restricted to indicate problems related to the this
+    //              API specification. Status related to the execution of a particular command should be
+    //              indicated as part of the reply field.
+    //
+    //          *pReplyData updated with command response
+    //
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*command)(effect_handle_t self,
+                       uint32_t cmdCode,
+                       uint32_t cmdSize,
+                       void *pCmdData,
+                       uint32_t *replySize,
+                       void *pReplyData);
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:        get_descriptor
+    //
+    //    Description:    Returns the effect descriptor
+    //
+    //    Input:
+    //          self:       handle to the effect interface this function
+    //              is called on.
+    //
+    //    Input/Output:
+    //          pDescriptor:    address where to return the effect descriptor.
+    //
+    //    Output:
+    //        returned value:    0          successful operation.
+    //                          -EINVAL     invalid interface handle or invalid pDescriptor
+    //        *pDescriptor:     updated with the effect descriptor.
+    //
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*get_descriptor)(effect_handle_t self,
+                              effect_descriptor_t *pDescriptor);
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:       process_reverse
+    //
+    //    Description:    Process reverse stream function. This function is used to pass
+    //          a reference stream to the effect engine. If the engine does not need a reference
+    //          stream, this function pointer can be set to NULL.
+    //          This function would typically implemented by an Echo Canceler.
+    //
+    //    Input:
+    //          self:       handle to the effect interface this function
+    //              is called on.
+    //          inBuffer:   buffer descriptor indicating where to read samples to process.
+    //              If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command.
+    //
+    //          outBuffer:   buffer descriptor indicating where to write processed samples.
+    //              If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command.
+    //              If the buffer and buffer provider in the configuration received by
+    //              EFFECT_CMD_SET_CONFIG_REVERSE are also NULL, do not return modified reverse
+    //              stream data
+    //
+    //    Output:
+    //        returned value:    0 successful operation
+    //                          -ENODATA the engine has finished the disable phase and the framework
+    //                                  can stop calling process_reverse()
+    //                          -EINVAL invalid interface handle or
+    //                                  invalid input/output buffer description
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*process_reverse)(effect_handle_t self,
+                               audio_buffer_t *inBuffer,
+                               audio_buffer_t *outBuffer);
+};
+
+
+//
+//--- Standardized command codes for command() function
+//
+enum effect_command_e {
+   EFFECT_CMD_INIT,                 // initialize effect engine
+   EFFECT_CMD_SET_CONFIG,           // configure effect engine (see effect_config_t)
+   EFFECT_CMD_RESET,                // reset effect engine
+   EFFECT_CMD_ENABLE,               // enable effect process
+   EFFECT_CMD_DISABLE,              // disable effect process
+   EFFECT_CMD_SET_PARAM,            // set parameter immediately (see effect_param_t)
+   EFFECT_CMD_SET_PARAM_DEFERRED,   // set parameter deferred
+   EFFECT_CMD_SET_PARAM_COMMIT,     // commit previous set parameter deferred
+   EFFECT_CMD_GET_PARAM,            // get parameter
+   EFFECT_CMD_SET_DEVICE,           // set audio device (see audio.h, audio_devices_t)
+   EFFECT_CMD_SET_VOLUME,           // set volume
+   EFFECT_CMD_SET_AUDIO_MODE,       // set the audio mode (normal, ring, ...)
+   EFFECT_CMD_SET_CONFIG_REVERSE,   // configure effect engine reverse stream(see effect_config_t)
+   EFFECT_CMD_SET_INPUT_DEVICE,     // set capture device (see audio.h, audio_devices_t)
+   EFFECT_CMD_GET_CONFIG,           // read effect engine configuration
+   EFFECT_CMD_GET_CONFIG_REVERSE,   // read configure effect engine reverse stream configuration
+   EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS,// get all supported configurations for a feature.
+   EFFECT_CMD_GET_FEATURE_CONFIG,   // get current feature configuration
+   EFFECT_CMD_SET_FEATURE_CONFIG,   // set current feature configuration
+   EFFECT_CMD_SET_AUDIO_SOURCE,     // set the audio source (see audio.h, audio_source_t)
+   EFFECT_CMD_OFFLOAD,              // set if effect thread is an offload one,
+                                    // send the ioHandle of the effect thread
+   EFFECT_CMD_FIRST_PROPRIETARY = 0x10000 // first proprietary command code
+};
+
+//==================================================================================================
+// command: EFFECT_CMD_INIT
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Initialize effect engine: All configurations return to default
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Apply new audio parameters configurations for input and output buffers
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_config_t)
+//  data: effect_config_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_RESET
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Reset the effect engine. Keep configuration but resets state and buffer content
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_ENABLE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Enable the process. Called by the framework before the first call to process()
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_DISABLE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Disable the process. Called by the framework after the last call to process()
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set a parameter and apply it immediately
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_param_t) + size of param and value
+//  data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM_DEFERRED
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set a parameter but apply it only when receiving EFFECT_CMD_SET_PARAM_COMMIT command
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_param_t) + size of param and value
+//  data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM_COMMIT
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Apply all previously received EFFECT_CMD_SET_PARAM_DEFERRED commands
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_GET_PARAM
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Get a parameter value
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_param_t) + size of param
+//  data: effect_param_t + param
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(effect_param_t) + size of param and value
+//  data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//==================================================================================================
+// command: EFFECT_CMD_SET_DEVICE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set the rendering device the audio output path is connected to. See audio.h, audio_devices_t
+//  for device values.
+//  The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this
+//  command when the device changes
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_VOLUME
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set and get volume. Used by audio framework to delegate volume control to effect engine.
+//  The effect implementation must set EFFECT_FLAG_VOLUME_IND or EFFECT_FLAG_VOLUME_CTRL flag in
+//  its descriptor to receive this command before every call to process() function
+//  If EFFECT_FLAG_VOLUME_CTRL flag is set in the effect descriptor, the effect engine must return
+//  the volume that should be applied before the effect is processed. The overall volume (the volume
+//  actually applied by the effect engine multiplied by the returned value) should match the value
+//  indicated in the command.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: n * sizeof(uint32_t)
+//  data: volume for each channel defined in effect_config_t for output buffer expressed in
+//      8.24 fixed point format
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: n * sizeof(uint32_t) / 0
+//  data: - if EFFECT_FLAG_VOLUME_CTRL is set in effect descriptor:
+//              volume for each channel defined in effect_config_t for output buffer expressed in
+//              8.24 fixed point format
+//        - if EFFECT_FLAG_VOLUME_CTRL is not set in effect descriptor:
+//              N/A
+//  It is legal to receive a null pointer as pReplyData in which case the effect framework has
+//  delegated volume control to another effect
+//==================================================================================================
+// command: EFFECT_CMD_SET_AUDIO_MODE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set the audio mode. The effect implementation must set EFFECT_FLAG_AUDIO_MODE_IND flag in its
+//  descriptor to receive this command when the audio mode changes.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: audio_mode_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_CONFIG_REVERSE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Apply new audio parameters configurations for input and output buffers of reverse stream.
+//  An example of reverse stream is the echo reference supplied to an Acoustic Echo Canceler.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_config_t)
+//  data: effect_config_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_INPUT_DEVICE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set the capture device the audio input path is connected to. See audio.h, audio_devices_t
+//  for device values.
+//  The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this
+//  command when the device changes
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_GET_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Read audio parameters configurations for input and output buffers
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(effect_config_t)
+//  data: effect_config_t
+//==================================================================================================
+// command: EFFECT_CMD_GET_CONFIG_REVERSE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Read audio parameters configurations for input and output buffers of reverse stream
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(effect_config_t)
+//  data: effect_config_t
+//==================================================================================================
+// command: EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Queries for supported configurations for a particular feature (e.g. get the supported
+// combinations of main and auxiliary channels for a noise suppressor).
+// The command parameter is the feature identifier (See effect_feature_e for a list of defined
+// features) followed by the maximum number of configuration descriptor to return.
+// The reply is composed of:
+//  - status (uint32_t):
+//          - 0 if feature is supported
+//          - -ENOSYS if the feature is not supported,
+//          - -ENOMEM if the feature is supported but the total number of supported configurations
+//          exceeds the maximum number indicated by the caller.
+//  - total number of supported configurations (uint32_t)
+//  - an array of configuration descriptors.
+// The actual number of descriptors returned must not exceed the maximum number indicated by
+// the caller.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 2 x sizeof(uint32_t)
+//  data: effect_feature_e + maximum number of configurations to return
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 2 x sizeof(uint32_t) + n x sizeof (<config descriptor>)
+//  data: status + total number of configurations supported + array of n config descriptors
+//==================================================================================================
+// command: EFFECT_CMD_GET_FEATURE_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Retrieves current configuration for a given feature.
+// The reply status is:
+//      - 0 if feature is supported
+//      - -ENOSYS if the feature is not supported,
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: effect_feature_e
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(uint32_t) + sizeof (<config descriptor>)
+//  data: status + config descriptor
+//==================================================================================================
+// command: EFFECT_CMD_SET_FEATURE_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Sets current configuration for a given feature.
+// The reply status is:
+//      - 0 if feature is supported
+//      - -ENOSYS if the feature is not supported,
+//      - -EINVAL if the configuration is invalid
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t) + sizeof (<config descriptor>)
+//  data: effect_feature_e + config descriptor
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(uint32_t)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_AUDIO_SOURCE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set the audio source the capture path is configured for (Camcorder, voice recognition...).
+//  See audio.h, audio_source_t for values.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_OFFLOAD
+//--------------------------------------------------------------------------------------------------
+// description:
+//  1.indicate if the playback thread the effect is attached to is offloaded or not
+//  2.update the io handle of the playback thread the effect is attached to
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_offload_param_t)
+//  data: effect_offload_param_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(uint32_t)
+//  data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// command: EFFECT_CMD_FIRST_PROPRIETARY
+//--------------------------------------------------------------------------------------------------
+// description:
+//  All proprietary effect commands must use command codes above this value. The size and format of
+//  command and response fields is free in this case
+//==================================================================================================
+
+
+// Audio buffer descriptor used by process(), bufferProvider() functions and buffer_config_t
+// structure. Multi-channel audio is always interleaved. The channel order is from LSB to MSB with
+// regard to the channel mask definition in audio.h, audio_channel_mask_t e.g :
+// Stereo: left, right
+// 5 point 1: front left, front right, front center, low frequency, back left, back right
+// The buffer size is expressed in frame count, a frame being composed of samples for all
+// channels at a given time. Frame size for unspecified format (AUDIO_FORMAT_OTHER) is 8 bit by
+// definition
+struct audio_buffer_s {
+    size_t   frameCount;        // number of frames in buffer
+    union {
+        void*       raw;        // raw pointer to start of buffer
+        int32_t*    s32;        // pointer to signed 32 bit data at start of buffer
+        int16_t*    s16;        // pointer to signed 16 bit data at start of buffer
+        uint8_t*    u8;         // pointer to unsigned 8 bit data at start of buffer
+    };
+};
+
+// The buffer_provider_s structure contains functions that can be used
+// by the effect engine process() function to query and release input
+// or output audio buffer.
+// The getBuffer() function is called to retrieve a buffer where data
+// should read from or written to by process() function.
+// The releaseBuffer() function MUST be called when the buffer retrieved
+// with getBuffer() is not needed anymore.
+// The process function should use the buffer provider mechanism to retrieve
+// input or output buffer if the inBuffer or outBuffer passed as argument is NULL
+// and the buffer configuration (buffer_config_t) given by the EFFECT_CMD_SET_CONFIG
+// command did not specify an audio buffer.
+
+typedef int32_t (* buffer_function_t)(void *cookie, audio_buffer_t *buffer);
+
+typedef struct buffer_provider_s {
+    buffer_function_t getBuffer;       // retrieve next buffer
+    buffer_function_t releaseBuffer;   // release used buffer
+    void       *cookie;                // for use by client of buffer provider functions
+} buffer_provider_t;
+
+
+// The buffer_config_s structure specifies the input or output audio format
+// to be used by the effect engine. It is part of the effect_config_t
+// structure that defines both input and output buffer configurations and is
+// passed by the EFFECT_CMD_SET_CONFIG or EFFECT_CMD_SET_CONFIG_REVERSE command.
+typedef struct buffer_config_s {
+    audio_buffer_t  buffer;     // buffer for use by process() function if not passed explicitly
+    uint32_t   samplingRate;    // sampling rate
+    uint32_t   channels;        // channel mask (see audio_channel_mask_t in audio.h)
+    buffer_provider_t bufferProvider;   // buffer provider
+    uint8_t    format;          // Audio format (see audio_format_t in audio.h)
+    uint8_t    accessMode;      // read/write or accumulate in buffer (effect_buffer_access_e)
+    uint16_t   mask;            // indicates which of the above fields is valid
+} buffer_config_t;
+
+// Values for "accessMode" field of buffer_config_t:
+//   overwrite, read only, accumulate (read/modify/write)
+enum effect_buffer_access_e {
+    EFFECT_BUFFER_ACCESS_WRITE,
+    EFFECT_BUFFER_ACCESS_READ,
+    EFFECT_BUFFER_ACCESS_ACCUMULATE
+
+};
+
+// feature identifiers for EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS command
+enum effect_feature_e {
+    EFFECT_FEATURE_AUX_CHANNELS, // supports auxiliary channels (e.g. dual mic noise suppressor)
+    EFFECT_FEATURE_CNT
+};
+
+// EFFECT_FEATURE_AUX_CHANNELS feature configuration descriptor. Describe a combination
+// of main and auxiliary channels supported
+typedef struct channel_config_s {
+    audio_channel_mask_t main_channels; // channel mask for main channels
+    audio_channel_mask_t aux_channels;  // channel mask for auxiliary channels
+} channel_config_t;
+
+
+// Values for bit field "mask" in buffer_config_t. If a bit is set, the corresponding field
+// in buffer_config_t must be taken into account when executing the EFFECT_CMD_SET_CONFIG command
+#define EFFECT_CONFIG_BUFFER    0x0001  // buffer field must be taken into account
+#define EFFECT_CONFIG_SMP_RATE  0x0002  // samplingRate field must be taken into account
+#define EFFECT_CONFIG_CHANNELS  0x0004  // channels field must be taken into account
+#define EFFECT_CONFIG_FORMAT    0x0008  // format field must be taken into account
+#define EFFECT_CONFIG_ACC_MODE  0x0010  // accessMode field must be taken into account
+#define EFFECT_CONFIG_PROVIDER  0x0020  // bufferProvider field must be taken into account
+#define EFFECT_CONFIG_ALL (EFFECT_CONFIG_BUFFER | EFFECT_CONFIG_SMP_RATE | \
+                           EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT | \
+                           EFFECT_CONFIG_ACC_MODE | EFFECT_CONFIG_PROVIDER)
+
+
+// effect_config_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_CONFIG
+// command to configure audio parameters and buffers for effect engine input and output.
+typedef struct effect_config_s {
+    buffer_config_t   inputCfg;
+    buffer_config_t   outputCfg;
+} effect_config_t;
+
+
+// effect_param_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_PARAM
+// command and pCmdData and pReplyData of EFFECT_CMD_GET_PARAM command.
+// psize and vsize represent the actual size of parameter and value.
+//
+// NOTE: the start of value field inside the data field is always on a 32 bit boundary:
+//
+//  +-----------+
+//  | status    | sizeof(int)
+//  +-----------+
+//  | psize     | sizeof(int)
+//  +-----------+
+//  | vsize     | sizeof(int)
+//  +-----------+
+//  |           |   |           |
+//  ~ parameter ~   > psize     |
+//  |           |   |           >  ((psize - 1)/sizeof(int) + 1) * sizeof(int)
+//  +-----------+               |
+//  | padding   |               |
+//  +-----------+
+//  |           |   |
+//  ~ value     ~   > vsize
+//  |           |   |
+//  +-----------+
+
+typedef struct effect_param_s {
+    int32_t     status;     // Transaction status (unused for command, used for reply)
+    uint32_t    psize;      // Parameter size
+    uint32_t    vsize;      // Value size
+    char        data[];     // Start of Parameter + Value data
+} effect_param_t;
+
+// structure used by EFFECT_CMD_OFFLOAD command
+typedef struct effect_offload_param_s {
+    bool isOffload;         // true if the playback thread the effect is attached to is offloaded
+    int ioHandle;           // io handle of the playback thread the effect is attached to
+} effect_offload_param_t;
+
+
+/////////////////////////////////////////////////
+//      Effect library interface
+/////////////////////////////////////////////////
+
+// Effect library interface version 3.0
+// Note that EffectsFactory.c only checks the major version component, so changes to the minor
+// number can only be used for fully backwards compatible changes
+#define EFFECT_LIBRARY_API_VERSION EFFECT_MAKE_API_VERSION(3,0)
+
+#define AUDIO_EFFECT_LIBRARY_TAG ((('A') << 24) | (('E') << 16) | (('L') << 8) | ('T'))
+
+// Every effect library must have a data structure named AUDIO_EFFECT_LIBRARY_INFO_SYM
+// and the fields of this data structure must begin with audio_effect_library_t
+
+typedef struct audio_effect_library_s {
+    // tag must be initialized to AUDIO_EFFECT_LIBRARY_TAG
+    uint32_t tag;
+    // Version of the effect library API : 0xMMMMmmmm MMMM: Major, mmmm: minor
+    uint32_t version;
+    // Name of this library
+    const char *name;
+    // Author/owner/implementor of the library
+    const char *implementor;
+
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:        create_effect
+    //
+    //    Description:    Creates an effect engine of the specified implementation uuid and
+    //          returns an effect control interface on this engine. The function will allocate the
+    //          resources for an instance of the requested effect engine and return
+    //          a handle on the effect control interface.
+    //
+    //    Input:
+    //          uuid:    pointer to the effect uuid.
+    //          sessionId:  audio session to which this effect instance will be attached. All effects
+    //              created with the same session ID are connected in series and process the same signal
+    //              stream. Knowing that two effects are part of the same effect chain can help the
+    //              library implement some kind of optimizations.
+    //          ioId:   identifies the output or input stream this effect is directed to at audio HAL.
+    //              For future use especially with tunneled HW accelerated effects
+    //
+    //    Input/Output:
+    //          pHandle:        address where to return the effect interface handle.
+    //
+    //    Output:
+    //        returned value:    0          successful operation.
+    //                          -ENODEV     library failed to initialize
+    //                          -EINVAL     invalid pEffectUuid or pHandle
+    //                          -ENOENT     no effect with this uuid found
+    //        *pHandle:         updated with the effect interface handle.
+    //
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*create_effect)(const effect_uuid_t *uuid,
+                             int32_t sessionId,
+                             int32_t ioId,
+                             effect_handle_t *pHandle);
+
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:        release_effect
+    //
+    //    Description:    Releases the effect engine whose handle is given as argument.
+    //          All resources allocated to this particular instance of the effect are
+    //          released.
+    //
+    //    Input:
+    //          handle:         handle on the effect interface to be released.
+    //
+    //    Output:
+    //        returned value:    0          successful operation.
+    //                          -ENODEV     library failed to initialize
+    //                          -EINVAL     invalid interface handle
+    //
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*release_effect)(effect_handle_t handle);
+
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:        get_descriptor
+    //
+    //    Description:    Returns the descriptor of the effect engine which implementation UUID is
+    //          given as argument.
+    //
+    //    Input/Output:
+    //          uuid:           pointer to the effect uuid.
+    //          pDescriptor:    address where to return the effect descriptor.
+    //
+    //    Output:
+    //        returned value:    0          successful operation.
+    //                          -ENODEV     library failed to initialize
+    //                          -EINVAL     invalid pDescriptor or uuid
+    //        *pDescriptor:     updated with the effect descriptor.
+    //
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*get_descriptor)(const effect_uuid_t *uuid,
+                              effect_descriptor_t *pDescriptor);
+} audio_effect_library_t;
+
+// Name of the hal_module_info
+#define AUDIO_EFFECT_LIBRARY_INFO_SYM         AELI
+
+// Name of the hal_module_info as a string
+#define AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR  "AELI"
+
+__END_DECLS
+
+#endif  // ANDROID_AUDIO_EFFECT_H
diff --git a/repo/android/hardware/bluetooth.h b/repo/android/hardware/bluetooth.h
new file mode 100644
index 0000000..74cd1fc
--- /dev/null
+++ b/repo/android/hardware/bluetooth.h
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BLUETOOTH_H
+#define ANDROID_INCLUDE_BLUETOOTH_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <hardware/hardware.h>
+
+__BEGIN_DECLS
+
+/**
+ * The Bluetooth Hardware Module ID
+ */
+
+#define BT_HARDWARE_MODULE_ID "bluetooth"
+#define BT_STACK_MODULE_ID "bluetooth"
+#define BT_STACK_TEST_MODULE_ID "bluetooth_test"
+
+
+/* Bluetooth profile interface IDs */
+
+#define BT_PROFILE_HANDSFREE_ID "handsfree"
+#define BT_PROFILE_HANDSFREE_CLIENT_ID "handsfree_client"
+#define BT_PROFILE_ADVANCED_AUDIO_ID "a2dp"
+#define BT_PROFILE_ADVANCED_AUDIO_SINK_ID "a2dp_sink"
+#define BT_PROFILE_HEALTH_ID "health"
+#define BT_PROFILE_SOCKETS_ID "socket"
+#define BT_PROFILE_HIDHOST_ID "hidhost"
+#define BT_PROFILE_PAN_ID "pan"
+#define BT_PROFILE_MAP_CLIENT_ID "map_client"
+
+#define BT_PROFILE_GATT_ID "gatt"
+#define BT_PROFILE_AV_RC_ID "avrcp"
+#define BT_PROFILE_AV_RC_CTRL_ID "avrcp_ctrl"
+
+/** Bluetooth Address */
+typedef struct {
+    uint8_t address[6];
+} __attribute__((packed))bt_bdaddr_t;
+
+/** Bluetooth Device Name */
+typedef struct {
+    uint8_t name[249];
+} __attribute__((packed))bt_bdname_t;
+
+/** Bluetooth Adapter Visibility Modes*/
+typedef enum {
+    BT_SCAN_MODE_NONE,
+    BT_SCAN_MODE_CONNECTABLE,
+    BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
+} bt_scan_mode_t;
+
+/** Bluetooth Adapter State */
+typedef enum {
+    BT_STATE_OFF,
+    BT_STATE_ON
+}   bt_state_t;
+
+/** Bluetooth Error Status */
+/** We need to build on this */
+
+typedef enum {
+    BT_STATUS_SUCCESS,
+    BT_STATUS_FAIL,
+    BT_STATUS_NOT_READY,
+    BT_STATUS_NOMEM,
+    BT_STATUS_BUSY,
+    BT_STATUS_DONE,        /* request already completed */
+    BT_STATUS_UNSUPPORTED,
+    BT_STATUS_PARM_INVALID,
+    BT_STATUS_UNHANDLED,
+    BT_STATUS_AUTH_FAILURE,
+    BT_STATUS_RMT_DEV_DOWN,
+    BT_STATUS_AUTH_REJECTED
+
+} bt_status_t;
+
+/** Bluetooth PinKey Code */
+typedef struct {
+    uint8_t pin[16];
+} __attribute__((packed))bt_pin_code_t;
+
+typedef struct {
+    uint8_t status;
+    uint8_t ctrl_state;     /* stack reported state */
+    uint64_t tx_time;       /* in ms */
+    uint64_t rx_time;       /* in ms */
+    uint64_t idle_time;     /* in ms */
+    uint64_t energy_used;   /* a product of mA, V and ms */
+} __attribute__((packed))bt_activity_energy_info;
+
+/** Bluetooth Adapter Discovery state */
+typedef enum {
+    BT_DISCOVERY_STOPPED,
+    BT_DISCOVERY_STARTED
+} bt_discovery_state_t;
+
+/** Bluetooth ACL connection state */
+typedef enum {
+    BT_ACL_STATE_CONNECTED,
+    BT_ACL_STATE_DISCONNECTED
+} bt_acl_state_t;
+
+/** Bluetooth 128-bit UUID */
+typedef struct {
+   uint8_t uu[16];
+} bt_uuid_t;
+
+/** Bluetooth SDP service record */
+typedef struct
+{
+   bt_uuid_t uuid;
+   uint16_t channel;
+   char name[256]; // what's the maximum length
+} bt_service_record_t;
+
+
+/** Bluetooth Remote Version info */
+typedef struct
+{
+   int version;
+   int sub_ver;
+   int manufacturer;
+} bt_remote_version_t;
+
+typedef struct
+{
+    uint8_t local_privacy_enabled;
+    uint8_t max_adv_instance;
+    uint8_t rpa_offload_supported;
+    uint8_t max_irk_list_size;
+    uint8_t max_adv_filter_supported;
+    uint8_t scan_result_storage_size_lobyte;
+    uint8_t scan_result_storage_size_hibyte;
+    uint8_t activity_energy_info_supported;
+}bt_local_le_features_t;
+
+/* Bluetooth Adapter and Remote Device property types */
+typedef enum {
+    /* Properties common to both adapter and remote device */
+    /**
+     * Description - Bluetooth Device Name
+     * Access mode - Adapter name can be GET/SET. Remote device can be GET
+     * Data type   - bt_bdname_t
+     */
+    BT_PROPERTY_BDNAME = 0x1,
+    /**
+     * Description - Bluetooth Device Address
+     * Access mode - Only GET.
+     * Data type   - bt_bdaddr_t
+     */
+    BT_PROPERTY_BDADDR,
+    /**
+     * Description - Bluetooth Service 128-bit UUIDs
+     * Access mode - Only GET.
+     * Data type   - Array of bt_uuid_t (Array size inferred from property length).
+     */
+    BT_PROPERTY_UUIDS,
+    /**
+     * Description - Bluetooth Class of Device as found in Assigned Numbers
+     * Access mode - Only GET.
+     * Data type   - uint32_t.
+     */
+    BT_PROPERTY_CLASS_OF_DEVICE,
+    /**
+     * Description - Device Type - BREDR, BLE or DUAL Mode
+     * Access mode - Only GET.
+     * Data type   - bt_device_type_t
+     */
+    BT_PROPERTY_TYPE_OF_DEVICE,
+    /**
+     * Description - Bluetooth Service Record
+     * Access mode - Only GET.
+     * Data type   - bt_service_record_t
+     */
+    BT_PROPERTY_SERVICE_RECORD,
+
+    /* Properties unique to adapter */
+    /**
+     * Description - Bluetooth Adapter scan mode
+     * Access mode - GET and SET
+     * Data type   - bt_scan_mode_t.
+     */
+    BT_PROPERTY_ADAPTER_SCAN_MODE,
+    /**
+     * Description - List of bonded devices
+     * Access mode - Only GET.
+     * Data type   - Array of bt_bdaddr_t of the bonded remote devices
+     *               (Array size inferred from property length).
+     */
+    BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+    /**
+     * Description - Bluetooth Adapter Discovery timeout (in seconds)
+     * Access mode - GET and SET
+     * Data type   - uint32_t
+     */
+    BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+
+    /* Properties unique to remote device */
+    /**
+     * Description - User defined friendly name of the remote device
+     * Access mode - GET and SET
+     * Data type   - bt_bdname_t.
+     */
+    BT_PROPERTY_REMOTE_FRIENDLY_NAME,
+    /**
+     * Description - RSSI value of the inquired remote device
+     * Access mode - Only GET.
+     * Data type   - int32_t.
+     */
+    BT_PROPERTY_REMOTE_RSSI,
+    /**
+     * Description - Remote version info
+     * Access mode - SET/GET.
+     * Data type   - bt_remote_version_t.
+     */
+
+    BT_PROPERTY_REMOTE_VERSION_INFO,
+
+    /**
+     * Description - Local LE features
+     * Access mode - GET.
+     * Data type   - bt_local_le_features_t.
+     */
+    BT_PROPERTY_LOCAL_LE_FEATURES,
+
+    BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP = 0xFF,
+} bt_property_type_t;
+
+/** Bluetooth Adapter Property data structure */
+typedef struct
+{
+    bt_property_type_t type;
+    int len;
+    void *val;
+} bt_property_t;
+
+
+/** Bluetooth Device Type */
+typedef enum {
+    BT_DEVICE_DEVTYPE_BREDR = 0x1,
+    BT_DEVICE_DEVTYPE_BLE,
+    BT_DEVICE_DEVTYPE_DUAL
+} bt_device_type_t;
+/** Bluetooth Bond state */
+typedef enum {
+    BT_BOND_STATE_NONE,
+    BT_BOND_STATE_BONDING,
+    BT_BOND_STATE_BONDED
+} bt_bond_state_t;
+
+/** Bluetooth SSP Bonding Variant */
+typedef enum {
+    BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
+    BT_SSP_VARIANT_PASSKEY_ENTRY,
+    BT_SSP_VARIANT_CONSENT,
+    BT_SSP_VARIANT_PASSKEY_NOTIFICATION
+} bt_ssp_variant_t;
+
+#define BT_MAX_NUM_UUIDS 32
+
+/** Bluetooth Interface callbacks */
+
+/** Bluetooth Enable/Disable Callback. */
+typedef void (*adapter_state_changed_callback)(bt_state_t state);
+
+/** GET/SET Adapter Properties callback */
+/* TODO: For the GET/SET property APIs/callbacks, we may need a session
+ * identifier to associate the call with the callback. This would be needed
+ * whenever more than one simultaneous instance of the same adapter_type
+ * is get/set.
+ *
+ * If this is going to be handled in the Java framework, then we do not need
+ * to manage sessions here.
+ */
+typedef void (*adapter_properties_callback)(bt_status_t status,
+                                               int num_properties,
+                                               bt_property_t *properties);
+
+/** GET/SET Remote Device Properties callback */
+/** TODO: For remote device properties, do not see a need to get/set
+ * multiple properties - num_properties shall be 1
+ */
+typedef void (*remote_device_properties_callback)(bt_status_t status,
+                                                       bt_bdaddr_t *bd_addr,
+                                                       int num_properties,
+                                                       bt_property_t *properties);
+
+/** New device discovered callback */
+/** If EIR data is not present, then BD_NAME and RSSI shall be NULL and -1
+ * respectively */
+typedef void (*device_found_callback)(int num_properties,
+                                         bt_property_t *properties);
+
+/** Discovery state changed callback */
+typedef void (*discovery_state_changed_callback)(bt_discovery_state_t state);
+
+/** Bluetooth Legacy PinKey Request callback */
+typedef void (*pin_request_callback)(bt_bdaddr_t *remote_bd_addr,
+                                        bt_bdname_t *bd_name, uint32_t cod);
+
+/** Bluetooth SSP Request callback - Just Works & Numeric Comparison*/
+/** pass_key - Shall be 0 for BT_SSP_PAIRING_VARIANT_CONSENT &
+ *  BT_SSP_PAIRING_PASSKEY_ENTRY */
+/* TODO: Passkey request callback shall not be needed for devices with display
+ * capability. We still need support this in the stack for completeness */
+typedef void (*ssp_request_callback)(bt_bdaddr_t *remote_bd_addr,
+                                        bt_bdname_t *bd_name,
+                                        uint32_t cod,
+                                        bt_ssp_variant_t pairing_variant,
+                                     uint32_t pass_key);
+
+/** Bluetooth Bond state changed callback */
+/* Invoked in response to create_bond, cancel_bond or remove_bond */
+typedef void (*bond_state_changed_callback)(bt_status_t status,
+                                               bt_bdaddr_t *remote_bd_addr,
+                                               bt_bond_state_t state);
+
+/** Bluetooth ACL connection state changed callback */
+typedef void (*acl_state_changed_callback)(bt_status_t status, bt_bdaddr_t *remote_bd_addr,
+                                            bt_acl_state_t state);
+
+typedef enum {
+    ASSOCIATE_JVM,
+    DISASSOCIATE_JVM
+} bt_cb_thread_evt;
+
+/** Thread Associate/Disassociate JVM Callback */
+/* Callback that is invoked by the callback thread to allow upper layer to attach/detach to/from
+ * the JVM */
+typedef void (*callback_thread_event)(bt_cb_thread_evt evt);
+
+/** Bluetooth Test Mode Callback */
+/* Receive any HCI event from controller. Must be in DUT Mode for this callback to be received */
+typedef void (*dut_mode_recv_callback)(uint16_t opcode, uint8_t *buf, uint8_t len);
+
+/* LE Test mode callbacks
+* This callback shall be invoked whenever the le_tx_test, le_rx_test or le_test_end is invoked
+* The num_packets is valid only for le_test_end command */
+typedef void (*le_test_mode_callback)(bt_status_t status, uint16_t num_packets);
+
+/** Callback invoked when energy details are obtained */
+/* Ctrl_state-Current controller state-Active-1,scan-2,or idle-3 state as defined by HCI spec.
+ * If the ctrl_state value is 0, it means the API call failed
+ * Time values-In milliseconds as returned by the controller
+ * Energy used-Value as returned by the controller
+ * Status-Provides the status of the read_energy_info API call */
+typedef void (*energy_info_callback)(bt_activity_energy_info *energy_info);
+
+/** TODO: Add callbacks for Link Up/Down and other generic
+  *  notifications/callbacks */
+
+/** Bluetooth DM callback structure. */
+typedef struct {
+    /** set to sizeof(bt_callbacks_t) */
+    size_t size;
+    adapter_state_changed_callback adapter_state_changed_cb;
+    adapter_properties_callback adapter_properties_cb;
+    remote_device_properties_callback remote_device_properties_cb;
+    device_found_callback device_found_cb;
+    discovery_state_changed_callback discovery_state_changed_cb;
+    pin_request_callback pin_request_cb;
+    ssp_request_callback ssp_request_cb;
+    bond_state_changed_callback bond_state_changed_cb;
+    acl_state_changed_callback acl_state_changed_cb;
+    callback_thread_event thread_evt_cb;
+    dut_mode_recv_callback dut_mode_recv_cb;
+    le_test_mode_callback le_test_mode_cb;
+    energy_info_callback energy_info_cb;
+} bt_callbacks_t;
+
+typedef void (*alarm_cb)(void *data);
+typedef bool (*set_wake_alarm_callout)(uint64_t delay_millis, bool should_wake, alarm_cb cb, void *data);
+typedef int (*acquire_wake_lock_callout)(const char *lock_name);
+typedef int (*release_wake_lock_callout)(const char *lock_name);
+
+/** The set of functions required by bluedroid to set wake alarms and
+  * grab wake locks. This struct is passed into the stack through the
+  * |set_os_callouts| function on |bt_interface_t|.
+  */
+typedef struct {
+  /* set to sizeof(bt_os_callouts_t) */
+  size_t size;
+
+  set_wake_alarm_callout set_wake_alarm;
+  acquire_wake_lock_callout acquire_wake_lock;
+  release_wake_lock_callout release_wake_lock;
+} bt_os_callouts_t;
+
+/** NOTE: By default, no profiles are initialized at the time of init/enable.
+ *  Whenever the application invokes the 'init' API of a profile, then one of
+ *  the following shall occur:
+ *
+ *    1.) If Bluetooth is not enabled, then the Bluetooth core shall mark the
+ *        profile as enabled. Subsequently, when the application invokes the
+ *        Bluetooth 'enable', as part of the enable sequence the profile that were
+ *        marked shall be enabled by calling appropriate stack APIs. The
+ *        'adapter_properties_cb' shall return the list of UUIDs of the
+ *        enabled profiles.
+ *
+ *    2.) If Bluetooth is enabled, then the Bluetooth core shall invoke the stack
+ *        profile API to initialize the profile and trigger a
+ *        'adapter_properties_cb' with the current list of UUIDs including the
+ *        newly added profile's UUID.
+ *
+ *   The reverse shall occur whenever the profile 'cleanup' APIs are invoked
+ */
+
+/** Represents the standard Bluetooth DM interface. */
+typedef struct {
+    /** set to sizeof(bt_interface_t) */
+    size_t size;
+    /**
+     * Opens the interface and provides the callback routines
+     * to the implemenation of this interface.
+     */
+    int (*init)(bt_callbacks_t* callbacks );
+
+    /** Enable Bluetooth. */
+    int (*enable)(void);
+
+    /** Disable Bluetooth. */
+    int (*disable)(void);
+
+    /** Closes the interface. */
+    void (*cleanup)(void);
+
+    /** Get all Bluetooth Adapter properties at init */
+    int (*get_adapter_properties)(void);
+
+    /** Get Bluetooth Adapter property of 'type' */
+    int (*get_adapter_property)(bt_property_type_t type);
+
+    /** Set Bluetooth Adapter property of 'type' */
+    /* Based on the type, val shall be one of
+     * bt_bdaddr_t or bt_bdname_t or bt_scanmode_t etc
+     */
+    int (*set_adapter_property)(const bt_property_t *property);
+
+    /** Get all Remote Device properties */
+    int (*get_remote_device_properties)(bt_bdaddr_t *remote_addr);
+
+    /** Get Remote Device property of 'type' */
+    int (*get_remote_device_property)(bt_bdaddr_t *remote_addr,
+                                      bt_property_type_t type);
+
+    /** Set Remote Device property of 'type' */
+    int (*set_remote_device_property)(bt_bdaddr_t *remote_addr,
+                                      const bt_property_t *property);
+
+    /** Get Remote Device's service record  for the given UUID */
+    int (*get_remote_service_record)(bt_bdaddr_t *remote_addr,
+                                     bt_uuid_t *uuid);
+
+    /** Start SDP to get remote services */
+    int (*get_remote_services)(bt_bdaddr_t *remote_addr);
+
+    /** Start Discovery */
+    int (*start_discovery)(void);
+
+    /** Cancel Discovery */
+    int (*cancel_discovery)(void);
+
+    /** Create Bluetooth Bonding */
+    int (*create_bond)(const bt_bdaddr_t *bd_addr, int transport);
+
+    /** Remove Bond */
+    int (*remove_bond)(const bt_bdaddr_t *bd_addr);
+
+    /** Cancel Bond */
+    int (*cancel_bond)(const bt_bdaddr_t *bd_addr);
+
+    /**
+     * Get the connection status for a given remote device.
+     * return value of 0 means the device is not connected,
+     * non-zero return status indicates an active connection.
+     */
+    int (*get_connection_state)(const bt_bdaddr_t *bd_addr);
+
+    /** BT Legacy PinKey Reply */
+    /** If accept==FALSE, then pin_len and pin_code shall be 0x0 */
+    int (*pin_reply)(const bt_bdaddr_t *bd_addr, uint8_t accept,
+                     uint8_t pin_len, bt_pin_code_t *pin_code);
+
+    /** BT SSP Reply - Just Works, Numeric Comparison and Passkey
+     * passkey shall be zero for BT_SSP_VARIANT_PASSKEY_COMPARISON &
+     * BT_SSP_VARIANT_CONSENT
+     * For BT_SSP_VARIANT_PASSKEY_ENTRY, if accept==FALSE, then passkey
+     * shall be zero */
+    int (*ssp_reply)(const bt_bdaddr_t *bd_addr, bt_ssp_variant_t variant,
+                     uint8_t accept, uint32_t passkey);
+
+    /** Get Bluetooth profile interface */
+    const void* (*get_profile_interface) (const char *profile_id);
+
+    /** Bluetooth Test Mode APIs - Bluetooth must be enabled for these APIs */
+    /* Configure DUT Mode - Use this mode to enter/exit DUT mode */
+    int (*dut_mode_configure)(uint8_t enable);
+
+    /* Send any test HCI (vendor-specific) command to the controller. Must be in DUT Mode */
+    int (*dut_mode_send)(uint16_t opcode, uint8_t *buf, uint8_t len);
+    /** BLE Test Mode APIs */
+    /* opcode MUST be one of: LE_Receiver_Test, LE_Transmitter_Test, LE_Test_End */
+    int (*le_test_mode)(uint16_t opcode, uint8_t *buf, uint8_t len);
+
+    /* enable or disable bluetooth HCI snoop log */
+    int (*config_hci_snoop_log)(uint8_t enable);
+
+    /** Sets the OS call-out functions that bluedroid needs for alarms and wake locks.
+      * This should be called immediately after a successful |init|.
+      */
+    int (*set_os_callouts)(bt_os_callouts_t *callouts);
+
+    /** Read Energy info details - return value indicates BT_STATUS_SUCCESS or BT_STATUS_NOT_READY
+      * Success indicates that the VSC command was sent to controller
+      */
+    int (*read_energy_info)();
+} bt_interface_t;
+
+/** TODO: Need to add APIs for Service Discovery, Service authorization and
+  *       connection management. Also need to add APIs for configuring
+  *       properties of remote bonded devices such as name, UUID etc. */
+
+typedef struct {
+    struct hw_device_t common;
+    const bt_interface_t* (*get_bluetooth_interface)();
+} bluetooth_device_t;
+
+typedef bluetooth_device_t bluetooth_module_t;
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BLUETOOTH_H */
diff --git a/repo/android/hardware/bt_av.h b/repo/android/hardware/bt_av.h
new file mode 100644
index 0000000..5252a17
--- /dev/null
+++ b/repo/android/hardware/bt_av.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_AV_H
+#define ANDROID_INCLUDE_BT_AV_H
+
+__BEGIN_DECLS
+
+/* Bluetooth AV connection states */
+typedef enum {
+    BTAV_CONNECTION_STATE_DISCONNECTED = 0,
+    BTAV_CONNECTION_STATE_CONNECTING,
+    BTAV_CONNECTION_STATE_CONNECTED,
+    BTAV_CONNECTION_STATE_DISCONNECTING
+} btav_connection_state_t;
+
+/* Bluetooth AV datapath states */
+typedef enum {
+    BTAV_AUDIO_STATE_REMOTE_SUSPEND = 0,
+    BTAV_AUDIO_STATE_STOPPED,
+    BTAV_AUDIO_STATE_STARTED,
+} btav_audio_state_t;
+
+
+/** Callback for connection state change.
+ *  state will have one of the values from btav_connection_state_t
+ */
+typedef void (* btav_connection_state_callback)(btav_connection_state_t state, 
+                                                    bt_bdaddr_t *bd_addr);
+
+/** Callback for audiopath state change.
+ *  state will have one of the values from btav_audio_state_t
+ */
+typedef void (* btav_audio_state_callback)(btav_audio_state_t state, 
+                                               bt_bdaddr_t *bd_addr);
+
+/** Callback for audio configuration change.
+ *  Used only for the A2DP sink interface.
+ *  state will have one of the values from btav_audio_state_t
+ *  sample_rate: sample rate in Hz
+ *  channel_count: number of channels (1 for mono, 2 for stereo)
+ */
+typedef void (* btav_audio_config_callback)(bt_bdaddr_t *bd_addr,
+                                                uint32_t sample_rate,
+                                                uint8_t channel_count);
+
+/** BT-AV callback structure. */
+typedef struct {
+    /** set to sizeof(btav_callbacks_t) */
+    size_t      size;
+    btav_connection_state_callback  connection_state_cb;
+    btav_audio_state_callback audio_state_cb;
+    btav_audio_config_callback audio_config_cb;
+} btav_callbacks_t;
+
+/** 
+ * NOTE:
+ *
+ * 1. AVRCP 1.0 shall be supported initially. AVRCP passthrough commands
+ *    shall be handled internally via uinput 
+ *
+ * 2. A2DP data path shall be handled via a socket pipe between the AudioFlinger
+ *    android_audio_hw library and the Bluetooth stack.
+ * 
+ */
+/** Represents the standard BT-AV interface.
+ *  Used for both the A2DP source and sink interfaces.
+ */
+typedef struct {
+
+    /** set to sizeof(btav_interface_t) */
+    size_t          size;
+    /**
+     * Register the BtAv callbacks
+     */
+    bt_status_t (*init)( btav_callbacks_t* callbacks );
+
+    /** connect to headset */
+    bt_status_t (*connect)( bt_bdaddr_t *bd_addr );
+
+    /** dis-connect from headset */
+    bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr );
+
+    /** Closes the interface. */
+    void  (*cleanup)( void );
+} btav_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_AV_H */
diff --git a/repo/android/hardware/bt_gatt.h b/repo/android/hardware/bt_gatt.h
new file mode 100644
index 0000000..42e14c2
--- /dev/null
+++ b/repo/android/hardware/bt_gatt.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_INCLUDE_BT_GATT_H
+#define ANDROID_INCLUDE_BT_GATT_H
+
+#include <stdint.h>
+#include "bt_gatt_client.h"
+#include "bt_gatt_server.h"
+
+__BEGIN_DECLS
+
+/** BT-GATT callbacks */
+typedef struct {
+    /** Set to sizeof(btgatt_callbacks_t) */
+    size_t size;
+
+    /** GATT Client callbacks */
+    const btgatt_client_callbacks_t* client;
+
+    /** GATT Server callbacks */
+    const btgatt_server_callbacks_t* server;
+} btgatt_callbacks_t;
+
+/** Represents the standard Bluetooth GATT interface. */
+typedef struct {
+    /** Set to sizeof(btgatt_interface_t) */
+    size_t          size;
+
+    /**
+     * Initializes the interface and provides callback routines
+     */
+    bt_status_t (*init)( const btgatt_callbacks_t* callbacks );
+
+    /** Closes the interface */
+    void (*cleanup)( void );
+
+    /** Pointer to the GATT client interface methods.*/
+    const btgatt_client_interface_t* client;
+
+    /** Pointer to the GATT server interface methods.*/
+    const btgatt_server_interface_t* server;
+} btgatt_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_GATT_H */
diff --git a/repo/android/hardware/bt_gatt_client.h b/repo/android/hardware/bt_gatt_client.h
new file mode 100644
index 0000000..8073dd1
--- /dev/null
+++ b/repo/android/hardware/bt_gatt_client.h
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_INCLUDE_BT_GATT_CLIENT_H
+#define ANDROID_INCLUDE_BT_GATT_CLIENT_H
+
+#include <stdint.h>
+#include "bt_gatt_types.h"
+
+__BEGIN_DECLS
+
+/**
+ * Buffer sizes for maximum attribute length and maximum read/write
+ * operation buffer size.
+ */
+#define BTGATT_MAX_ATTR_LEN 600
+
+/** Buffer type for unformatted reads/writes */
+typedef struct
+{
+    uint8_t             value[BTGATT_MAX_ATTR_LEN];
+    uint16_t            len;
+} btgatt_unformatted_value_t;
+
+/** Parameters for GATT read operations */
+typedef struct
+{
+    btgatt_srvc_id_t    srvc_id;
+    btgatt_gatt_id_t    char_id;
+    btgatt_gatt_id_t    descr_id;
+    btgatt_unformatted_value_t value;
+    uint16_t            value_type;
+    uint8_t             status;
+} btgatt_read_params_t;
+
+/** Parameters for GATT write operations */
+typedef struct
+{
+    btgatt_srvc_id_t    srvc_id;
+    btgatt_gatt_id_t    char_id;
+    btgatt_gatt_id_t    descr_id;
+    uint8_t             status;
+} btgatt_write_params_t;
+
+/** Attribute change notification parameters */
+typedef struct
+{
+    uint8_t             value[BTGATT_MAX_ATTR_LEN];
+    bt_bdaddr_t         bda;
+    btgatt_srvc_id_t    srvc_id;
+    btgatt_gatt_id_t    char_id;
+    uint16_t            len;
+    uint8_t             is_notify;
+} btgatt_notify_params_t;
+
+typedef struct
+{
+    bt_bdaddr_t        *bda1;
+    bt_uuid_t          *uuid1;
+    uint16_t            u1;
+    uint16_t            u2;
+    uint16_t            u3;
+    uint16_t            u4;
+    uint16_t            u5;
+} btgatt_test_params_t;
+
+/** BT-GATT Client callback structure. */
+
+/** Callback invoked in response to register_client */
+typedef void (*register_client_callback)(int status, int client_if,
+                bt_uuid_t *app_uuid);
+
+/** Callback for scan results */
+typedef void (*scan_result_callback)(bt_bdaddr_t* bda, int rssi, uint8_t* adv_data);
+
+/** GATT open callback invoked in response to open */
+typedef void (*connect_callback)(int conn_id, int status, int client_if, bt_bdaddr_t* bda);
+
+/** Callback invoked in response to close */
+typedef void (*disconnect_callback)(int conn_id, int status,
+                int client_if, bt_bdaddr_t* bda);
+
+/**
+ * Invoked in response to search_service when the GATT service search
+ * has been completed.
+ */
+typedef void (*search_complete_callback)(int conn_id, int status);
+
+/** Reports GATT services on a remote device */
+typedef void (*search_result_callback)( int conn_id, btgatt_srvc_id_t *srvc_id);
+
+/** GATT characteristic enumeration result callback */
+typedef void (*get_characteristic_callback)(int conn_id, int status,
+                btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+                int char_prop);
+
+/** GATT descriptor enumeration result callback */
+typedef void (*get_descriptor_callback)(int conn_id, int status,
+                btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+                btgatt_gatt_id_t *descr_id);
+
+/** GATT included service enumeration result callback */
+typedef void (*get_included_service_callback)(int conn_id, int status,
+                btgatt_srvc_id_t *srvc_id, btgatt_srvc_id_t *incl_srvc_id);
+
+/** Callback invoked in response to [de]register_for_notification */
+typedef void (*register_for_notification_callback)(int conn_id,
+                int registered, int status, btgatt_srvc_id_t *srvc_id,
+                btgatt_gatt_id_t *char_id);
+
+/**
+ * Remote device notification callback, invoked when a remote device sends
+ * a notification or indication that a client has registered for.
+ */
+typedef void (*notify_callback)(int conn_id, btgatt_notify_params_t *p_data);
+
+/** Reports result of a GATT read operation */
+typedef void (*read_characteristic_callback)(int conn_id, int status,
+                btgatt_read_params_t *p_data);
+
+/** GATT write characteristic operation callback */
+typedef void (*write_characteristic_callback)(int conn_id, int status,
+                btgatt_write_params_t *p_data);
+
+/** GATT execute prepared write callback */
+typedef void (*execute_write_callback)(int conn_id, int status);
+
+/** Callback invoked in response to read_descriptor */
+typedef void (*read_descriptor_callback)(int conn_id, int status,
+                btgatt_read_params_t *p_data);
+
+/** Callback invoked in response to write_descriptor */
+typedef void (*write_descriptor_callback)(int conn_id, int status,
+                btgatt_write_params_t *p_data);
+
+/** Callback triggered in response to read_remote_rssi */
+typedef void (*read_remote_rssi_callback)(int client_if, bt_bdaddr_t* bda,
+                                          int rssi, int status);
+
+/**
+ * Callback indicating the status of a listen() operation
+ */
+typedef void (*listen_callback)(int status, int server_if);
+
+/** Callback invoked when the MTU for a given connection changes */
+typedef void (*configure_mtu_callback)(int conn_id, int status, int mtu);
+
+/** Callback invoked when a scan filter configuration command has completed */
+typedef void (*scan_filter_cfg_callback)(int action, int client_if, int status, int filt_type,
+                                         int avbl_space);
+
+/** Callback invoked when scan param has been added, cleared, or deleted */
+typedef void (*scan_filter_param_callback)(int action, int client_if, int status,
+                                         int avbl_space);
+
+/** Callback invoked when a scan filter configuration command has completed */
+typedef void (*scan_filter_status_callback)(int enable, int client_if, int status);
+
+/** Callback invoked when multi-adv enable operation has completed */
+typedef void (*multi_adv_enable_callback)(int client_if, int status);
+
+/** Callback invoked when multi-adv param update operation has completed */
+typedef void (*multi_adv_update_callback)(int client_if, int status);
+
+/** Callback invoked when multi-adv instance data set operation has completed */
+typedef void (*multi_adv_data_callback)(int client_if, int status);
+
+/** Callback invoked when multi-adv disable operation has completed */
+typedef void (*multi_adv_disable_callback)(int client_if, int status);
+
+/**
+ * Callback notifying an application that a remote device connection is currently congested
+ * and cannot receive any more data. An application should avoid sending more data until
+ * a further callback is received indicating the congestion status has been cleared.
+ */
+typedef void (*congestion_callback)(int conn_id, bool congested);
+/** Callback invoked when batchscan storage config operation has completed */
+typedef void (*batchscan_cfg_storage_callback)(int client_if, int status);
+
+/** Callback invoked when batchscan enable / disable operation has completed */
+typedef void (*batchscan_enable_disable_callback)(int action, int client_if, int status);
+
+/** Callback invoked when batchscan reports are obtained */
+typedef void (*batchscan_reports_callback)(int client_if, int status, int report_format,
+                                           int num_records, int data_len, uint8_t* rep_data);
+
+/** Callback invoked when batchscan storage threshold limit is crossed */
+typedef void (*batchscan_threshold_callback)(int client_if);
+
+/** Track ADV VSE callback invoked when tracked device is found or lost */
+typedef void (*track_adv_event_callback)(int client_if, int filt_index, int addr_type,
+                                             bt_bdaddr_t* bda, int adv_state);
+
+typedef struct {
+    register_client_callback            register_client_cb;
+    scan_result_callback                scan_result_cb;
+    connect_callback                    open_cb;
+    disconnect_callback                 close_cb;
+    search_complete_callback            search_complete_cb;
+    search_result_callback              search_result_cb;
+    get_characteristic_callback         get_characteristic_cb;
+    get_descriptor_callback             get_descriptor_cb;
+    get_included_service_callback       get_included_service_cb;
+    register_for_notification_callback  register_for_notification_cb;
+    notify_callback                     notify_cb;
+    read_characteristic_callback        read_characteristic_cb;
+    write_characteristic_callback       write_characteristic_cb;
+    read_descriptor_callback            read_descriptor_cb;
+    write_descriptor_callback           write_descriptor_cb;
+    execute_write_callback              execute_write_cb;
+    read_remote_rssi_callback           read_remote_rssi_cb;
+    listen_callback                     listen_cb;
+    configure_mtu_callback              configure_mtu_cb;
+    scan_filter_cfg_callback            scan_filter_cfg_cb;
+    scan_filter_param_callback          scan_filter_param_cb;
+    scan_filter_status_callback         scan_filter_status_cb;
+    multi_adv_enable_callback           multi_adv_enable_cb;
+    multi_adv_update_callback           multi_adv_update_cb;
+    multi_adv_data_callback             multi_adv_data_cb;
+    multi_adv_disable_callback          multi_adv_disable_cb;
+    congestion_callback                 congestion_cb;
+    batchscan_cfg_storage_callback      batchscan_cfg_storage_cb;
+    batchscan_enable_disable_callback   batchscan_enb_disable_cb;
+    batchscan_reports_callback          batchscan_reports_cb;
+    batchscan_threshold_callback        batchscan_threshold_cb;
+    track_adv_event_callback            track_adv_event_cb;
+} btgatt_client_callbacks_t;
+
+/** Represents the standard BT-GATT client interface. */
+
+typedef struct {
+    /** Registers a GATT client application with the stack */
+    bt_status_t (*register_client)( bt_uuid_t *uuid );
+
+    /** Unregister a client application from the stack */
+    bt_status_t (*unregister_client)(int client_if );
+
+    /** Start or stop LE device scanning */
+    bt_status_t (*scan)( bool start );
+
+    /** Create a connection to a remote LE or dual-mode device */
+    bt_status_t (*connect)( int client_if, const bt_bdaddr_t *bd_addr,
+                         bool is_direct, int transport );
+
+    /** Disconnect a remote device or cancel a pending connection */
+    bt_status_t (*disconnect)( int client_if, const bt_bdaddr_t *bd_addr,
+                    int conn_id);
+
+    /** Start or stop advertisements to listen for incoming connections */
+    bt_status_t (*listen)(int client_if, bool start);
+
+    /** Clear the attribute cache for a given device */
+    bt_status_t (*refresh)( int client_if, const bt_bdaddr_t *bd_addr );
+
+    /**
+     * Enumerate all GATT services on a connected device.
+     * Optionally, the results can be filtered for a given UUID.
+     */
+    bt_status_t (*search_service)(int conn_id, bt_uuid_t *filter_uuid );
+
+    /**
+     * Enumerate included services for a given service.
+     * Set start_incl_srvc_id to NULL to get the first included service.
+     */
+    bt_status_t (*get_included_service)( int conn_id, btgatt_srvc_id_t *srvc_id,
+                                         btgatt_srvc_id_t *start_incl_srvc_id);
+
+    /**
+     * Enumerate characteristics for a given service.
+     * Set start_char_id to NULL to get the first characteristic.
+     */
+    bt_status_t (*get_characteristic)( int conn_id,
+                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *start_char_id);
+
+    /**
+     * Enumerate descriptors for a given characteristic.
+     * Set start_descr_id to NULL to get the first descriptor.
+     */
+    bt_status_t (*get_descriptor)( int conn_id,
+                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+                    btgatt_gatt_id_t *start_descr_id);
+
+    /** Read a characteristic on a remote device */
+    bt_status_t (*read_characteristic)( int conn_id,
+                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+                    int auth_req );
+
+    /** Write a remote characteristic */
+    bt_status_t (*write_characteristic)(int conn_id,
+                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+                    int write_type, int len, int auth_req,
+                    char* p_value);
+
+    /** Read the descriptor for a given characteristic */
+    bt_status_t (*read_descriptor)(int conn_id,
+                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+                    btgatt_gatt_id_t *descr_id, int auth_req);
+
+    /** Write a remote descriptor for a given characteristic */
+    bt_status_t (*write_descriptor)( int conn_id,
+                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+                    btgatt_gatt_id_t *descr_id, int write_type, int len,
+                    int auth_req, char* p_value);
+
+    /** Execute a prepared write operation */
+    bt_status_t (*execute_write)(int conn_id, int execute);
+
+    /**
+     * Register to receive notifications or indications for a given
+     * characteristic
+     */
+    bt_status_t (*register_for_notification)( int client_if,
+                    const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id,
+                    btgatt_gatt_id_t *char_id);
+
+    /** Deregister a previous request for notifications/indications */
+    bt_status_t (*deregister_for_notification)( int client_if,
+                    const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id,
+                    btgatt_gatt_id_t *char_id);
+
+    /** Request RSSI for a given remote device */
+    bt_status_t (*read_remote_rssi)( int client_if, const bt_bdaddr_t *bd_addr);
+
+    /** Setup scan filter params */
+    bt_status_t (*scan_filter_param_setup)(int client_if, int action, int filt_index, int feat_seln,
+                                      int list_logic_type, int filt_logic_type, int rssi_high_thres,
+                                      int rssi_low_thres, int dely_mode, int found_timeout,
+                                      int lost_timeout, int found_timeout_cnt);
+
+
+    /** Configure a scan filter condition  */
+    bt_status_t (*scan_filter_add_remove)(int client_if, int action, int filt_type,
+                                   int filt_index, int company_id,
+                                   int company_id_mask, const bt_uuid_t *p_uuid,
+                                   const bt_uuid_t *p_uuid_mask, const bt_bdaddr_t *bd_addr,
+                                   char addr_type, int data_len, char* p_data, int mask_len,
+                                   char* p_mask);
+
+    /** Clear all scan filter conditions for specific filter index*/
+    bt_status_t (*scan_filter_clear)(int client_if, int filt_index);
+
+    /** Enable / disable scan filter feature*/
+    bt_status_t (*scan_filter_enable)(int client_if, bool enable);
+
+    /** Determine the type of the remote device (LE, BR/EDR, Dual-mode) */
+    int (*get_device_type)( const bt_bdaddr_t *bd_addr );
+
+    /** Set the advertising data or scan response data */
+    bt_status_t (*set_adv_data)(int client_if, bool set_scan_rsp, bool include_name,
+                    bool include_txpower, int min_interval, int max_interval, int appearance,
+                    uint16_t manufacturer_len, char* manufacturer_data,
+                    uint16_t service_data_len, char* service_data,
+                    uint16_t service_uuid_len, char* service_uuid);
+
+    /** Configure the MTU for a given connection */
+    bt_status_t (*configure_mtu)(int conn_id, int mtu);
+
+    /** Request a connection parameter update */
+    bt_status_t (*conn_parameter_update)(const bt_bdaddr_t *bd_addr, int min_interval,
+                    int max_interval, int latency, int timeout);
+
+    /** Sets the LE scan interval and window in units of N*0.625 msec */
+    bt_status_t (*set_scan_parameters)(int scan_interval, int scan_window);
+
+    /* Setup the parameters as per spec, user manual specified values and enable multi ADV */
+    bt_status_t (*multi_adv_enable)(int client_if, int min_interval,int max_interval,int adv_type,
+                 int chnl_map, int tx_power, int timeout_s);
+
+    /* Update the parameters as per spec, user manual specified values and restart multi ADV */
+    bt_status_t (*multi_adv_update)(int client_if, int min_interval,int max_interval,int adv_type,
+                 int chnl_map, int tx_power, int timeout_s);
+
+    /* Setup the data for the specified instance */
+    bt_status_t (*multi_adv_set_inst_data)(int client_if, bool set_scan_rsp, bool include_name,
+                    bool incl_txpower, int appearance, int manufacturer_len,
+                    char* manufacturer_data, int service_data_len,
+                    char* service_data, int service_uuid_len, char* service_uuid);
+
+    /* Disable the multi adv instance */
+    bt_status_t (*multi_adv_disable)(int client_if);
+
+    /* Configure the batchscan storage */
+    bt_status_t (*batchscan_cfg_storage)(int client_if, int batch_scan_full_max,
+        int batch_scan_trunc_max, int batch_scan_notify_threshold);
+
+    /* Enable batchscan */
+    bt_status_t (*batchscan_enb_batch_scan)(int client_if, int scan_mode,
+        int scan_interval, int scan_window, int addr_type, int discard_rule);
+
+    /* Disable batchscan */
+    bt_status_t (*batchscan_dis_batch_scan)(int client_if);
+
+    /* Read out batchscan reports */
+    bt_status_t (*batchscan_read_reports)(int client_if, int scan_mode);
+
+    /** Test mode interface */
+    bt_status_t (*test_command)( int command, btgatt_test_params_t* params);
+
+} btgatt_client_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_GATT_CLIENT_H */
diff --git a/repo/android/hardware/bt_gatt_server.h b/repo/android/hardware/bt_gatt_server.h
new file mode 100644
index 0000000..0d6cc1e
--- /dev/null
+++ b/repo/android/hardware/bt_gatt_server.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_INCLUDE_BT_GATT_SERVER_H
+#define ANDROID_INCLUDE_BT_GATT_SERVER_H
+
+#include <stdint.h>
+
+#include "bt_gatt_types.h"
+
+__BEGIN_DECLS
+
+/** GATT value type used in response to remote read requests */
+typedef struct
+{
+    uint8_t           value[BTGATT_MAX_ATTR_LEN];
+    uint16_t          handle;
+    uint16_t          offset;
+    uint16_t          len;
+    uint8_t           auth_req;
+} btgatt_value_t;
+
+/** GATT remote read request response type */
+typedef union
+{
+    btgatt_value_t attr_value;
+    uint16_t            handle;
+} btgatt_response_t;
+
+/** BT-GATT Server callback structure. */
+
+/** Callback invoked in response to register_server */
+typedef void (*register_server_callback)(int status, int server_if,
+                bt_uuid_t *app_uuid);
+
+/** Callback indicating that a remote device has connected or been disconnected */
+typedef void (*connection_callback)(int conn_id, int server_if, int connected,
+                                    bt_bdaddr_t *bda);
+
+/** Callback invoked in response to create_service */
+typedef void (*service_added_callback)(int status, int server_if,
+                btgatt_srvc_id_t *srvc_id, int srvc_handle);
+
+/** Callback indicating that an included service has been added to a service */
+typedef void (*included_service_added_callback)(int status, int server_if,
+                int srvc_handle, int incl_srvc_handle);
+
+/** Callback invoked when a characteristic has been added to a service */
+typedef void (*characteristic_added_callback)(int status, int server_if,
+                bt_uuid_t *uuid, int srvc_handle, int char_handle);
+
+/** Callback invoked when a descriptor has been added to a characteristic */
+typedef void (*descriptor_added_callback)(int status, int server_if,
+                bt_uuid_t *uuid, int srvc_handle, int descr_handle);
+
+/** Callback invoked in response to start_service */
+typedef void (*service_started_callback)(int status, int server_if,
+                                         int srvc_handle);
+
+/** Callback invoked in response to stop_service */
+typedef void (*service_stopped_callback)(int status, int server_if,
+                                         int srvc_handle);
+
+/** Callback triggered when a service has been deleted */
+typedef void (*service_deleted_callback)(int status, int server_if,
+                                         int srvc_handle);
+
+/**
+ * Callback invoked when a remote device has requested to read a characteristic
+ * or descriptor. The application must respond by calling send_response
+ */
+typedef void (*request_read_callback)(int conn_id, int trans_id, bt_bdaddr_t *bda,
+                                      int attr_handle, int offset, bool is_long);
+
+/**
+ * Callback invoked when a remote device has requested to write to a
+ * characteristic or descriptor.
+ */
+typedef void (*request_write_callback)(int conn_id, int trans_id, bt_bdaddr_t *bda,
+                                       int attr_handle, int offset, int length,
+                                       bool need_rsp, bool is_prep, uint8_t* value);
+
+/** Callback invoked when a previously prepared write is to be executed */
+typedef void (*request_exec_write_callback)(int conn_id, int trans_id,
+                                            bt_bdaddr_t *bda, int exec_write);
+
+/**
+ * Callback triggered in response to send_response if the remote device
+ * sends a confirmation.
+ */
+typedef void (*response_confirmation_callback)(int status, int handle);
+
+/**
+ * Callback confirming that a notification or indication has been sent
+ * to a remote device.
+ */
+typedef void (*indication_sent_callback)(int conn_id, int status);
+
+/**
+ * Callback notifying an application that a remote device connection is currently congested
+ * and cannot receive any more data. An application should avoid sending more data until
+ * a further callback is received indicating the congestion status has been cleared.
+ */
+typedef void (*congestion_callback)(int conn_id, bool congested);
+
+/** Callback invoked when the MTU for a given connection changes */
+typedef void (*mtu_changed_callback)(int conn_id, int mtu);
+
+typedef struct {
+    register_server_callback        register_server_cb;
+    connection_callback             connection_cb;
+    service_added_callback          service_added_cb;
+    included_service_added_callback included_service_added_cb;
+    characteristic_added_callback   characteristic_added_cb;
+    descriptor_added_callback       descriptor_added_cb;
+    service_started_callback        service_started_cb;
+    service_stopped_callback        service_stopped_cb;
+    service_deleted_callback        service_deleted_cb;
+    request_read_callback           request_read_cb;
+    request_write_callback          request_write_cb;
+    request_exec_write_callback     request_exec_write_cb;
+    response_confirmation_callback  response_confirmation_cb;
+    indication_sent_callback        indication_sent_cb;
+    congestion_callback             congestion_cb;
+    mtu_changed_callback            mtu_changed_cb;
+} btgatt_server_callbacks_t;
+
+/** Represents the standard BT-GATT server interface. */
+typedef struct {
+    /** Registers a GATT server application with the stack */
+    bt_status_t (*register_server)( bt_uuid_t *uuid );
+
+    /** Unregister a server application from the stack */
+    bt_status_t (*unregister_server)(int server_if );
+
+    /** Create a connection to a remote peripheral */
+    bt_status_t (*connect)(int server_if, const bt_bdaddr_t *bd_addr,
+                            bool is_direct, int transport);
+
+    /** Disconnect an established connection or cancel a pending one */
+    bt_status_t (*disconnect)(int server_if, const bt_bdaddr_t *bd_addr,
+                    int conn_id );
+
+    /** Create a new service */
+    bt_status_t (*add_service)( int server_if, btgatt_srvc_id_t *srvc_id, int num_handles);
+
+    /** Assign an included service to it's parent service */
+    bt_status_t (*add_included_service)( int server_if, int service_handle, int included_handle);
+
+    /** Add a characteristic to a service */
+    bt_status_t (*add_characteristic)( int server_if,
+                    int service_handle, bt_uuid_t *uuid,
+                    int properties, int permissions);
+
+    /** Add a descriptor to a given service */
+    bt_status_t (*add_descriptor)(int server_if, int service_handle,
+                                  bt_uuid_t *uuid, int permissions);
+
+    /** Starts a local service */
+    bt_status_t (*start_service)(int server_if, int service_handle,
+                                 int transport);
+
+    /** Stops a local service */
+    bt_status_t (*stop_service)(int server_if, int service_handle);
+
+    /** Delete a local service */
+    bt_status_t (*delete_service)(int server_if, int service_handle);
+
+    /** Send value indication to a remote device */
+    bt_status_t (*send_indication)(int server_if, int attribute_handle,
+                                   int conn_id, int len, int confirm,
+                                   char* p_value);
+
+    /** Send a response to a read/write operation */
+    bt_status_t (*send_response)(int conn_id, int trans_id,
+                                 int status, btgatt_response_t *response);
+
+} btgatt_server_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_GATT_CLIENT_H */
diff --git a/repo/android/hardware/bt_gatt_types.h b/repo/android/hardware/bt_gatt_types.h
new file mode 100644
index 0000000..e037ddc
--- /dev/null
+++ b/repo/android/hardware/bt_gatt_types.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_INCLUDE_BT_GATT_TYPES_H
+#define ANDROID_INCLUDE_BT_GATT_TYPES_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+__BEGIN_DECLS
+
+/**
+ * GATT Service types
+ */
+#define BTGATT_SERVICE_TYPE_PRIMARY 0
+#define BTGATT_SERVICE_TYPE_SECONDARY 1
+
+/** GATT ID adding instance id tracking to the UUID */
+typedef struct
+{
+    bt_uuid_t           uuid;
+    uint8_t             inst_id;
+} btgatt_gatt_id_t;
+
+/** GATT Service ID also identifies the service type (primary/secondary) */
+typedef struct
+{
+    btgatt_gatt_id_t    id;
+    uint8_t             is_primary;
+} btgatt_srvc_id_t;
+
+/** Preferred physical Transport for GATT connection */
+typedef enum
+{
+    GATT_TRANSPORT_AUTO,
+    GATT_TRANSPORT_BREDR,
+    GATT_TRANSPORT_LE
+} btgatt_transport_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_GATT_TYPES_H */
diff --git a/repo/android/hardware/bt_hf.h b/repo/android/hardware/bt_hf.h
new file mode 100644
index 0000000..7dcb40a
--- /dev/null
+++ b/repo/android/hardware/bt_hf.h
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_HF_H
+#define ANDROID_INCLUDE_BT_HF_H
+
+__BEGIN_DECLS
+
+/* AT response code - OK/Error */
+typedef enum {
+    BTHF_AT_RESPONSE_ERROR = 0,
+    BTHF_AT_RESPONSE_OK
+} bthf_at_response_t;
+
+typedef enum {
+    BTHF_CONNECTION_STATE_DISCONNECTED = 0,
+    BTHF_CONNECTION_STATE_CONNECTING,
+    BTHF_CONNECTION_STATE_CONNECTED,
+    BTHF_CONNECTION_STATE_SLC_CONNECTED,
+    BTHF_CONNECTION_STATE_DISCONNECTING
+} bthf_connection_state_t;
+
+typedef enum {
+    BTHF_AUDIO_STATE_DISCONNECTED = 0,
+    BTHF_AUDIO_STATE_CONNECTING,
+    BTHF_AUDIO_STATE_CONNECTED,
+    BTHF_AUDIO_STATE_DISCONNECTING
+} bthf_audio_state_t;
+
+typedef enum {
+    BTHF_VR_STATE_STOPPED = 0,
+    BTHF_VR_STATE_STARTED
+} bthf_vr_state_t;
+
+typedef enum {
+    BTHF_VOLUME_TYPE_SPK = 0,
+    BTHF_VOLUME_TYPE_MIC
+} bthf_volume_type_t;
+
+/* Noise Reduction and Echo Cancellation */
+typedef enum
+{
+    BTHF_NREC_STOP,
+    BTHF_NREC_START
+} bthf_nrec_t;
+
+/* WBS codec setting */
+typedef enum
+{
+   BTHF_WBS_NONE,
+   BTHF_WBS_NO,
+   BTHF_WBS_YES
+}bthf_wbs_config_t;
+
+/* CHLD - Call held handling */
+typedef enum
+{
+    BTHF_CHLD_TYPE_RELEASEHELD,              // Terminate all held or set UDUB("busy") to a waiting call
+    BTHF_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD, // Terminate all active calls and accepts a waiting/held call
+    BTHF_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD,    // Hold all active calls and accepts a waiting/held call
+    BTHF_CHLD_TYPE_ADDHELDTOCONF,            // Add all held calls to a conference
+} bthf_chld_type_t;
+
+/** Callback for connection state change.
+ *  state will have one of the values from BtHfConnectionState
+ */
+typedef void (* bthf_connection_state_callback)(bthf_connection_state_t state, bt_bdaddr_t *bd_addr);
+
+/** Callback for audio connection state change.
+ *  state will have one of the values from BtHfAudioState
+ */
+typedef void (* bthf_audio_state_callback)(bthf_audio_state_t state, bt_bdaddr_t *bd_addr);
+
+/** Callback for VR connection state change.
+ *  state will have one of the values from BtHfVRState
+ */
+typedef void (* bthf_vr_cmd_callback)(bthf_vr_state_t state, bt_bdaddr_t *bd_addr);
+
+/** Callback for answer incoming call (ATA)
+ */
+typedef void (* bthf_answer_call_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** Callback for disconnect call (AT+CHUP)
+ */
+typedef void (* bthf_hangup_call_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** Callback for disconnect call (AT+CHUP)
+ *  type will denote Speaker/Mic gain (BtHfVolumeControl).
+ */
+typedef void (* bthf_volume_cmd_callback)(bthf_volume_type_t type, int volume, bt_bdaddr_t *bd_addr);
+
+/** Callback for dialing an outgoing call
+ *  If number is NULL, redial
+ */
+typedef void (* bthf_dial_call_cmd_callback)(char *number, bt_bdaddr_t *bd_addr);
+
+/** Callback for sending DTMF tones
+ *  tone contains the dtmf character to be sent
+ */
+typedef void (* bthf_dtmf_cmd_callback)(char tone, bt_bdaddr_t *bd_addr);
+
+/** Callback for enabling/disabling noise reduction/echo cancellation
+ *  value will be 1 to enable, 0 to disable
+ */
+typedef void (* bthf_nrec_cmd_callback)(bthf_nrec_t nrec, bt_bdaddr_t *bd_addr);
+
+/** Callback for AT+BCS and event from BAC
+ *  WBS enable, WBS disable
+ */
+typedef void (* bthf_wbs_callback)(bthf_wbs_config_t wbs, bt_bdaddr_t *bd_addr);
+
+/** Callback for call hold handling (AT+CHLD)
+ *  value will contain the call hold command (0, 1, 2, 3)
+ */
+typedef void (* bthf_chld_cmd_callback)(bthf_chld_type_t chld, bt_bdaddr_t *bd_addr);
+
+/** Callback for CNUM (subscriber number)
+ */
+typedef void (* bthf_cnum_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** Callback for indicators (CIND)
+ */
+typedef void (* bthf_cind_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** Callback for operator selection (COPS)
+ */
+typedef void (* bthf_cops_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** Callback for call list (AT+CLCC)
+ */
+typedef void (* bthf_clcc_cmd_callback) (bt_bdaddr_t *bd_addr);
+
+/** Callback for unknown AT command recd from HF
+ *  at_string will contain the unparsed AT string
+ */
+typedef void (* bthf_unknown_at_cmd_callback)(char *at_string, bt_bdaddr_t *bd_addr);
+
+/** Callback for keypressed (HSP) event.
+ */
+typedef void (* bthf_key_pressed_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** BT-HF callback structure. */
+typedef struct {
+    /** set to sizeof(BtHfCallbacks) */
+    size_t      size;
+    bthf_connection_state_callback  connection_state_cb;
+    bthf_audio_state_callback       audio_state_cb;
+    bthf_vr_cmd_callback            vr_cmd_cb;
+    bthf_answer_call_cmd_callback   answer_call_cmd_cb;
+    bthf_hangup_call_cmd_callback   hangup_call_cmd_cb;
+    bthf_volume_cmd_callback        volume_cmd_cb;
+    bthf_dial_call_cmd_callback     dial_call_cmd_cb;
+    bthf_dtmf_cmd_callback          dtmf_cmd_cb;
+    bthf_nrec_cmd_callback          nrec_cmd_cb;
+    bthf_wbs_callback               wbs_cb;
+    bthf_chld_cmd_callback          chld_cmd_cb;
+    bthf_cnum_cmd_callback          cnum_cmd_cb;
+    bthf_cind_cmd_callback          cind_cmd_cb;
+    bthf_cops_cmd_callback          cops_cmd_cb;
+    bthf_clcc_cmd_callback          clcc_cmd_cb;
+    bthf_unknown_at_cmd_callback    unknown_at_cmd_cb;
+    bthf_key_pressed_cmd_callback   key_pressed_cmd_cb;
+} bthf_callbacks_t;
+
+/** Network Status */
+typedef enum
+{
+    BTHF_NETWORK_STATE_NOT_AVAILABLE = 0,
+    BTHF_NETWORK_STATE_AVAILABLE
+} bthf_network_state_t;
+
+/** Service type */
+typedef enum
+{
+    BTHF_SERVICE_TYPE_HOME = 0,
+    BTHF_SERVICE_TYPE_ROAMING
+} bthf_service_type_t;
+
+typedef enum {
+    BTHF_CALL_STATE_ACTIVE = 0,
+    BTHF_CALL_STATE_HELD,
+    BTHF_CALL_STATE_DIALING,
+    BTHF_CALL_STATE_ALERTING,
+    BTHF_CALL_STATE_INCOMING,
+    BTHF_CALL_STATE_WAITING,
+    BTHF_CALL_STATE_IDLE
+} bthf_call_state_t;
+
+typedef enum {
+    BTHF_CALL_DIRECTION_OUTGOING = 0,
+    BTHF_CALL_DIRECTION_INCOMING
+} bthf_call_direction_t;
+
+typedef enum {
+    BTHF_CALL_TYPE_VOICE = 0,
+    BTHF_CALL_TYPE_DATA,
+    BTHF_CALL_TYPE_FAX
+} bthf_call_mode_t;
+
+typedef enum {
+    BTHF_CALL_MPTY_TYPE_SINGLE = 0,
+    BTHF_CALL_MPTY_TYPE_MULTI
+} bthf_call_mpty_type_t;
+
+typedef enum {
+    BTHF_CALL_ADDRTYPE_UNKNOWN = 0x81,
+    BTHF_CALL_ADDRTYPE_INTERNATIONAL = 0x91
+} bthf_call_addrtype_t;
+/** Represents the standard BT-HF interface. */
+typedef struct {
+
+    /** set to sizeof(BtHfInterface) */
+    size_t          size;
+    /**
+     * Register the BtHf callbacks
+     */
+    bt_status_t (*init)( bthf_callbacks_t* callbacks, int max_hf_clients);
+
+    /** connect to headset */
+    bt_status_t (*connect)( bt_bdaddr_t *bd_addr );
+
+    /** dis-connect from headset */
+    bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr );
+
+    /** create an audio connection */
+    bt_status_t (*connect_audio)( bt_bdaddr_t *bd_addr );
+
+    /** close the audio connection */
+    bt_status_t (*disconnect_audio)( bt_bdaddr_t *bd_addr );
+
+    /** start voice recognition */
+    bt_status_t (*start_voice_recognition)( bt_bdaddr_t *bd_addr );
+
+    /** stop voice recognition */
+    bt_status_t (*stop_voice_recognition)( bt_bdaddr_t *bd_addr );
+
+    /** volume control */
+    bt_status_t (*volume_control) (bthf_volume_type_t type, int volume, bt_bdaddr_t *bd_addr );
+
+    /** Combined device status change notification */
+    bt_status_t (*device_status_notification)(bthf_network_state_t ntk_state, bthf_service_type_t svc_type, int signal,
+                           int batt_chg);
+
+    /** Response for COPS command */
+    bt_status_t (*cops_response)(const char *cops, bt_bdaddr_t *bd_addr );
+
+    /** Response for CIND command */
+    bt_status_t (*cind_response)(int svc, int num_active, int num_held, bthf_call_state_t call_setup_state,
+                                 int signal, int roam, int batt_chg, bt_bdaddr_t *bd_addr );
+
+    /** Pre-formatted AT response, typically in response to unknown AT cmd */
+    bt_status_t (*formatted_at_response)(const char *rsp, bt_bdaddr_t *bd_addr );
+
+    /** ok/error response
+     *  ERROR (0)
+     *  OK    (1)
+     */
+    bt_status_t (*at_response) (bthf_at_response_t response_code, int error_code, bt_bdaddr_t *bd_addr );
+
+    /** response for CLCC command 
+     *  Can be iteratively called for each call index
+     *  Call index of 0 will be treated as NULL termination (Completes response)
+     */
+    bt_status_t (*clcc_response) (int index, bthf_call_direction_t dir,
+                                bthf_call_state_t state, bthf_call_mode_t mode,
+                                bthf_call_mpty_type_t mpty, const char *number,
+                                bthf_call_addrtype_t type, bt_bdaddr_t *bd_addr );
+
+    /** notify of a call state change
+     *  Each update notifies 
+     *    1. Number of active/held/ringing calls
+     *    2. call_state: This denotes the state change that triggered this msg
+     *                   This will take one of the values from BtHfCallState
+     *    3. number & type: valid only for incoming & waiting call
+    */
+    bt_status_t (*phone_state_change) (int num_active, int num_held, bthf_call_state_t call_setup_state,
+                                       const char *number, bthf_call_addrtype_t type);
+
+    /** Closes the interface. */
+    void  (*cleanup)( void );
+
+    /** configureation for the SCO codec */
+    bt_status_t (*configure_wbs)( bt_bdaddr_t *bd_addr ,bthf_wbs_config_t config );
+} bthf_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_HF_H */
diff --git a/repo/android/hardware/bt_hf_client.h b/repo/android/hardware/bt_hf_client.h
new file mode 100644
index 0000000..8acf1b2
--- /dev/null
+++ b/repo/android/hardware/bt_hf_client.h
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_HF_CLIENT_H
+#define ANDROID_INCLUDE_BT_HF_CLIENT_H
+
+__BEGIN_DECLS
+
+typedef enum {
+    BTHF_CLIENT_CONNECTION_STATE_DISCONNECTED = 0,
+    BTHF_CLIENT_CONNECTION_STATE_CONNECTING,
+    BTHF_CLIENT_CONNECTION_STATE_CONNECTED,
+    BTHF_CLIENT_CONNECTION_STATE_SLC_CONNECTED,
+    BTHF_CLIENT_CONNECTION_STATE_DISCONNECTING
+} bthf_client_connection_state_t;
+
+typedef enum {
+    BTHF_CLIENT_AUDIO_STATE_DISCONNECTED = 0,
+    BTHF_CLIENT_AUDIO_STATE_CONNECTING,
+    BTHF_CLIENT_AUDIO_STATE_CONNECTED,
+    BTHF_CLIENT_AUDIO_STATE_CONNECTED_MSBC,
+} bthf_client_audio_state_t;
+
+typedef enum {
+    BTHF_CLIENT_VR_STATE_STOPPED = 0,
+    BTHF_CLIENT_VR_STATE_STARTED
+} bthf_client_vr_state_t;
+
+typedef enum {
+    BTHF_CLIENT_VOLUME_TYPE_SPK = 0,
+    BTHF_CLIENT_VOLUME_TYPE_MIC
+} bthf_client_volume_type_t;
+
+typedef enum
+{
+    BTHF_CLIENT_NETWORK_STATE_NOT_AVAILABLE = 0,
+    BTHF_CLIENT_NETWORK_STATE_AVAILABLE
+} bthf_client_network_state_t;
+
+typedef enum
+{
+    BTHF_CLIENT_SERVICE_TYPE_HOME = 0,
+    BTHF_CLIENT_SERVICE_TYPE_ROAMING
+} bthf_client_service_type_t;
+
+typedef enum {
+    BTHF_CLIENT_CALL_STATE_ACTIVE = 0,
+    BTHF_CLIENT_CALL_STATE_HELD,
+    BTHF_CLIENT_CALL_STATE_DIALING,
+    BTHF_CLIENT_CALL_STATE_ALERTING,
+    BTHF_CLIENT_CALL_STATE_INCOMING,
+    BTHF_CLIENT_CALL_STATE_WAITING,
+    BTHF_CLIENT_CALL_STATE_HELD_BY_RESP_HOLD,
+} bthf_client_call_state_t;
+
+typedef enum {
+    BTHF_CLIENT_CALL_NO_CALLS_IN_PROGRESS = 0,
+    BTHF_CLIENT_CALL_CALLS_IN_PROGRESS
+} bthf_client_call_t;
+
+typedef enum {
+    BTHF_CLIENT_CALLSETUP_NONE = 0,
+    BTHF_CLIENT_CALLSETUP_INCOMING,
+    BTHF_CLIENT_CALLSETUP_OUTGOING,
+    BTHF_CLIENT_CALLSETUP_ALERTING
+
+} bthf_client_callsetup_t;
+
+typedef enum {
+    BTHF_CLIENT_CALLHELD_NONE = 0,
+    BTHF_CLIENT_CALLHELD_HOLD_AND_ACTIVE,
+    BTHF_CLIENT_CALLHELD_HOLD,
+} bthf_client_callheld_t;
+
+typedef enum {
+    BTHF_CLIENT_RESP_AND_HOLD_HELD = 0,
+    BTRH_CLIENT_RESP_AND_HOLD_ACCEPT,
+    BTRH_CLIENT_RESP_AND_HOLD_REJECT,
+} bthf_client_resp_and_hold_t;
+
+typedef enum {
+    BTHF_CLIENT_CALL_DIRECTION_OUTGOING = 0,
+    BTHF_CLIENT_CALL_DIRECTION_INCOMING
+} bthf_client_call_direction_t;
+
+typedef enum {
+    BTHF_CLIENT_CALL_MPTY_TYPE_SINGLE = 0,
+    BTHF_CLIENT_CALL_MPTY_TYPE_MULTI
+} bthf_client_call_mpty_type_t;
+
+typedef enum {
+    BTHF_CLIENT_CMD_COMPLETE_OK = 0,
+    BTHF_CLIENT_CMD_COMPLETE_ERROR,
+    BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_CARRIER,
+    BTHF_CLIENT_CMD_COMPLETE_ERROR_BUSY,
+    BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_ANSWER,
+    BTHF_CLIENT_CMD_COMPLETE_ERROR_DELAYED,
+    BTHF_CLIENT_CMD_COMPLETE_ERROR_BLACKLISTED,
+    BTHF_CLIENT_CMD_COMPLETE_ERROR_CME
+} bthf_client_cmd_complete_t;
+
+typedef enum {
+    BTHF_CLIENT_CALL_ACTION_CHLD_0 = 0,
+    BTHF_CLIENT_CALL_ACTION_CHLD_1,
+    BTHF_CLIENT_CALL_ACTION_CHLD_2,
+    BTHF_CLIENT_CALL_ACTION_CHLD_3,
+    BTHF_CLIENT_CALL_ACTION_CHLD_4,
+    BTHF_CLIENT_CALL_ACTION_CHLD_1x,
+    BTHF_CLIENT_CALL_ACTION_CHLD_2x,
+    BTHF_CLIENT_CALL_ACTION_ATA,
+    BTHF_CLIENT_CALL_ACTION_CHUP,
+    BTHF_CLIENT_CALL_ACTION_BTRH_0,
+    BTHF_CLIENT_CALL_ACTION_BTRH_1,
+    BTHF_CLIENT_CALL_ACTION_BTRH_2,
+} bthf_client_call_action_t;
+
+typedef enum {
+    BTHF_CLIENT_SERVICE_UNKNOWN = 0,
+    BTHF_CLIENT_SERVICE_VOICE,
+    BTHF_CLIENT_SERVICE_FAX
+} bthf_client_subscriber_service_type_t;
+
+typedef enum {
+    BTHF_CLIENT_IN_BAND_RINGTONE_NOT_PROVIDED = 0,
+    BTHF_CLIENT_IN_BAND_RINGTONE_PROVIDED,
+} bthf_client_in_band_ring_state_t;
+
+/* Peer features masks */
+#define BTHF_CLIENT_PEER_FEAT_3WAY   0x00000001  /* Three-way calling */
+#define BTHF_CLIENT_PEER_FEAT_ECNR   0x00000002  /* Echo cancellation and/or noise reduction */
+#define BTHF_CLIENT_PEER_FEAT_VREC   0x00000004  /* Voice recognition */
+#define BTHF_CLIENT_PEER_FEAT_INBAND 0x00000008  /* In-band ring tone */
+#define BTHF_CLIENT_PEER_FEAT_VTAG   0x00000010  /* Attach a phone number to a voice tag */
+#define BTHF_CLIENT_PEER_FEAT_REJECT 0x00000020  /* Ability to reject incoming call */
+#define BTHF_CLIENT_PEER_FEAT_ECS    0x00000040  /* Enhanced Call Status */
+#define BTHF_CLIENT_PEER_FEAT_ECC    0x00000080  /* Enhanced Call Control */
+#define BTHF_CLIENT_PEER_FEAT_EXTERR 0x00000100  /* Extended error codes */
+#define BTHF_CLIENT_PEER_FEAT_CODEC  0x00000200  /* Codec Negotiation */
+
+/* Peer call handling features masks */
+#define BTHF_CLIENT_CHLD_FEAT_REL           0x00000001  /* 0  Release waiting call or held calls */
+#define BTHF_CLIENT_CHLD_FEAT_REL_ACC       0x00000002  /* 1  Release active calls and accept other
+                                                              (waiting or held) cal */
+#define BTHF_CLIENT_CHLD_FEAT_REL_X         0x00000004  /* 1x Release specified active call only */
+#define BTHF_CLIENT_CHLD_FEAT_HOLD_ACC      0x00000008  /* 2  Active calls on hold and accept other
+                                                              (waiting or held) call */
+#define BTHF_CLIENT_CHLD_FEAT_PRIV_X        0x00000010  /* 2x Request private mode with specified
+                                                              call (put the rest on hold) */
+#define BTHF_CLIENT_CHLD_FEAT_MERGE         0x00000020  /* 3  Add held call to multiparty */
+#define BTHF_CLIENT_CHLD_FEAT_MERGE_DETACH  0x00000040  /* 4  Connect two calls and leave
+                                                              (disconnect from) multiparty */
+
+/** Callback for connection state change.
+ *  state will have one of the values from BtHfConnectionState
+ *  peer/chld_features are valid only for BTHF_CLIENT_CONNECTION_STATE_SLC_CONNECTED state
+ */
+typedef void (* bthf_client_connection_state_callback)(bthf_client_connection_state_t state,
+                                                       unsigned int peer_feat,
+                                                       unsigned int chld_feat,
+                                                       bt_bdaddr_t *bd_addr);
+
+/** Callback for audio connection state change.
+ *  state will have one of the values from BtHfAudioState
+ */
+typedef void (* bthf_client_audio_state_callback)(bthf_client_audio_state_t state,
+                                                  bt_bdaddr_t *bd_addr);
+
+/** Callback for VR connection state change.
+ *  state will have one of the values from BtHfVRState
+ */
+typedef void (* bthf_client_vr_cmd_callback)(bthf_client_vr_state_t state);
+
+/** Callback for network state change
+ */
+typedef void (* bthf_client_network_state_callback) (bthf_client_network_state_t state);
+
+/** Callback for network roaming status change
+ */
+typedef void (* bthf_client_network_roaming_callback) (bthf_client_service_type_t type);
+
+/** Callback for signal strength indication
+ */
+typedef void (* bthf_client_network_signal_callback) (int signal_strength);
+
+/** Callback for battery level indication
+ */
+typedef void (* bthf_client_battery_level_callback) (int battery_level);
+
+/** Callback for current operator name
+ */
+typedef void (* bthf_client_current_operator_callback) (const char *name);
+
+/** Callback for call indicator
+ */
+typedef void (* bthf_client_call_callback) (bthf_client_call_t call);
+
+/** Callback for callsetup indicator
+ */
+typedef void (* bthf_client_callsetup_callback) (bthf_client_callsetup_t callsetup);
+
+/** Callback for callheld indicator
+ */
+typedef void (* bthf_client_callheld_callback) (bthf_client_callheld_t callheld);
+
+/** Callback for response and hold
+ */
+typedef void (* bthf_client_resp_and_hold_callback) (bthf_client_resp_and_hold_t resp_and_hold);
+
+/** Callback for Calling Line Identification notification
+ *  Will be called only when there is an incoming call and number is provided.
+ */
+typedef void (* bthf_client_clip_callback) (const char *number);
+
+/**
+ * Callback for Call Waiting notification
+ */
+typedef void (* bthf_client_call_waiting_callback) (const char *number);
+
+/**
+ *  Callback for listing current calls. Can be called multiple time.
+ *  If number is unknown NULL is passed.
+ */
+typedef void (*bthf_client_current_calls) (int index, bthf_client_call_direction_t dir,
+                                           bthf_client_call_state_t state,
+                                           bthf_client_call_mpty_type_t mpty,
+                                           const char *number);
+
+/** Callback for audio volume change
+ */
+typedef void (*bthf_client_volume_change_callback) (bthf_client_volume_type_t type, int volume);
+
+/** Callback for command complete event
+ *  cme is valid only for BTHF_CLIENT_CMD_COMPLETE_ERROR_CME type
+ */
+typedef void (*bthf_client_cmd_complete_callback) (bthf_client_cmd_complete_t type, int cme);
+
+/** Callback for subscriber information
+ */
+typedef void (* bthf_client_subscriber_info_callback) (const char *name,
+                                                       bthf_client_subscriber_service_type_t type);
+
+/** Callback for in-band ring tone settings
+ */
+typedef void (* bthf_client_in_band_ring_tone_callback) (bthf_client_in_band_ring_state_t state);
+
+/**
+ * Callback for requested number from AG
+ */
+typedef void (* bthf_client_last_voice_tag_number_callback) (const char *number);
+
+/**
+ * Callback for sending ring indication to app
+ */
+typedef void (* bthf_client_ring_indication_callback) (void);
+
+/** BT-HF callback structure. */
+typedef struct {
+    /** set to sizeof(BtHfClientCallbacks) */
+    size_t      size;
+    bthf_client_connection_state_callback  connection_state_cb;
+    bthf_client_audio_state_callback       audio_state_cb;
+    bthf_client_vr_cmd_callback            vr_cmd_cb;
+    bthf_client_network_state_callback     network_state_cb;
+    bthf_client_network_roaming_callback   network_roaming_cb;
+    bthf_client_network_signal_callback    network_signal_cb;
+    bthf_client_battery_level_callback     battery_level_cb;
+    bthf_client_current_operator_callback  current_operator_cb;
+    bthf_client_call_callback              call_cb;
+    bthf_client_callsetup_callback         callsetup_cb;
+    bthf_client_callheld_callback          callheld_cb;
+    bthf_client_resp_and_hold_callback     resp_and_hold_cb;
+    bthf_client_clip_callback              clip_cb;
+    bthf_client_call_waiting_callback      call_waiting_cb;
+    bthf_client_current_calls              current_calls_cb;
+    bthf_client_volume_change_callback     volume_change_cb;
+    bthf_client_cmd_complete_callback      cmd_complete_cb;
+    bthf_client_subscriber_info_callback   subscriber_info_cb;
+    bthf_client_in_band_ring_tone_callback in_band_ring_tone_cb;
+    bthf_client_last_voice_tag_number_callback last_voice_tag_number_callback;
+    bthf_client_ring_indication_callback   ring_indication_cb;
+} bthf_client_callbacks_t;
+
+/** Represents the standard BT-HF interface. */
+typedef struct {
+
+    /** set to sizeof(BtHfClientInterface) */
+    size_t size;
+    /**
+     * Register the BtHf callbacks
+     */
+    bt_status_t (*init)(bthf_client_callbacks_t* callbacks);
+
+    /** connect to audio gateway */
+    bt_status_t (*connect)(bt_bdaddr_t *bd_addr);
+
+    /** disconnect from audio gateway */
+    bt_status_t (*disconnect)(bt_bdaddr_t *bd_addr);
+
+    /** create an audio connection */
+    bt_status_t (*connect_audio)(bt_bdaddr_t *bd_addr);
+
+    /** close the audio connection */
+    bt_status_t (*disconnect_audio)(bt_bdaddr_t *bd_addr);
+
+    /** start voice recognition */
+    bt_status_t (*start_voice_recognition)(void);
+
+    /** stop voice recognition */
+    bt_status_t (*stop_voice_recognition)(void);
+
+    /** volume control */
+    bt_status_t (*volume_control) (bthf_client_volume_type_t type, int volume);
+
+    /** place a call with number a number
+     * if number is NULL last called number is called (aka re-dial)*/
+    bt_status_t (*dial) (const char *number);
+
+    /** place a call with number specified by location (speed dial) */
+    bt_status_t (*dial_memory) (int location);
+
+    /** perform specified call related action
+     * idx is limited only for enhanced call control related action
+     */
+    bt_status_t (*handle_call_action) (bthf_client_call_action_t action, int idx);
+
+    /** query list of current calls */
+    bt_status_t (*query_current_calls) (void);
+
+    /** query name of current selected operator */
+    bt_status_t (*query_current_operator_name) (void);
+
+    /** Retrieve subscriber information */
+    bt_status_t (*retrieve_subscriber_info) (void);
+
+    /** Send DTMF code*/
+    bt_status_t (*send_dtmf) (char code);
+
+    /** Request a phone number from AG corresponding to last voice tag recorded */
+    bt_status_t (*request_last_voice_tag_number) (void);
+
+    /** Closes the interface. */
+    void (*cleanup)(void);
+
+    /** Send AT Command. */
+    bt_status_t (*send_at_cmd) (int cmd, int val1, int val2, const char *arg);
+} bthf_client_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_HF_CLIENT_H */
diff --git a/repo/android/hardware/bt_hh.h b/repo/android/hardware/bt_hh.h
new file mode 100644
index 0000000..dad9586
--- /dev/null
+++ b/repo/android/hardware/bt_hh.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_HH_H
+#define ANDROID_INCLUDE_BT_HH_H
+
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+#define BTHH_MAX_DSC_LEN   884
+
+/* HH connection states */
+typedef enum
+{
+    BTHH_CONN_STATE_CONNECTED              = 0,
+    BTHH_CONN_STATE_CONNECTING,
+    BTHH_CONN_STATE_DISCONNECTED,
+    BTHH_CONN_STATE_DISCONNECTING,
+    BTHH_CONN_STATE_FAILED_MOUSE_FROM_HOST,
+    BTHH_CONN_STATE_FAILED_KBD_FROM_HOST,
+    BTHH_CONN_STATE_FAILED_TOO_MANY_DEVICES,
+    BTHH_CONN_STATE_FAILED_NO_BTHID_DRIVER,
+    BTHH_CONN_STATE_FAILED_GENERIC,
+    BTHH_CONN_STATE_UNKNOWN
+} bthh_connection_state_t;
+
+typedef enum
+{
+    BTHH_OK                = 0,
+    BTHH_HS_HID_NOT_READY,        /* handshake error : device not ready */
+    BTHH_HS_INVALID_RPT_ID,       /* handshake error : invalid report ID */
+    BTHH_HS_TRANS_NOT_SPT,        /* handshake error : transaction not spt */
+    BTHH_HS_INVALID_PARAM,        /* handshake error : invalid paremter */
+    BTHH_HS_ERROR,                /* handshake error : unspecified HS error */
+    BTHH_ERR,                     /* general BTA HH error */
+    BTHH_ERR_SDP,                 /* SDP error */
+    BTHH_ERR_PROTO,               /* SET_Protocol error,
+                                                                only used in BTA_HH_OPEN_EVT callback */
+    BTHH_ERR_DB_FULL,             /* device database full error, used  */
+    BTHH_ERR_TOD_UNSPT,           /* type of device not supported */
+    BTHH_ERR_NO_RES,              /* out of system resources */
+    BTHH_ERR_AUTH_FAILED,         /* authentication fail */
+    BTHH_ERR_HDL
+}bthh_status_t;
+
+/* Protocol modes */
+typedef enum {
+    BTHH_REPORT_MODE       = 0x00,
+    BTHH_BOOT_MODE         = 0x01,
+    BTHH_UNSUPPORTED_MODE  = 0xff
+}bthh_protocol_mode_t;
+
+/* Report types */
+typedef enum {
+    BTHH_INPUT_REPORT      = 1,
+    BTHH_OUTPUT_REPORT,
+    BTHH_FEATURE_REPORT
+}bthh_report_type_t;
+
+typedef struct
+{
+    int         attr_mask;
+    uint8_t     sub_class;
+    uint8_t     app_id;
+    int         vendor_id;
+    int         product_id;
+    int         version;
+    uint8_t     ctry_code;
+    int         dl_len;
+    uint8_t     dsc_list[BTHH_MAX_DSC_LEN];
+} bthh_hid_info_t;
+
+/** Callback for connection state change.
+ *  state will have one of the values from bthh_connection_state_t
+ */
+typedef void (* bthh_connection_state_callback)(bt_bdaddr_t *bd_addr, bthh_connection_state_t state);
+
+/** Callback for vitual unplug api.
+ *  the status of the vitual unplug
+ */
+typedef void (* bthh_virtual_unplug_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status);
+
+/** Callback for get hid info
+ *  hid_info will contain attr_mask, sub_class, app_id, vendor_id, product_id, version, ctry_code, len
+ */
+typedef void (* bthh_hid_info_callback)(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info);
+
+/** Callback for get protocol api.
+ *  the protocol mode is one of the value from bthh_protocol_mode_t
+ */
+typedef void (* bthh_protocol_mode_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, bthh_protocol_mode_t mode);
+
+/** Callback for get/set_idle_time api.
+ */
+typedef void (* bthh_idle_time_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, int idle_rate);
+
+
+/** Callback for get report api.
+ *  if staus is ok rpt_data contains the report data
+ */
+typedef void (* bthh_get_report_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, uint8_t* rpt_data, int rpt_size);
+
+/** Callback for set_report/set_protocol api and if error
+ *  occurs for get_report/get_protocol api.
+ */
+typedef void (* bthh_handshake_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status);
+
+
+/** BT-HH callback structure. */
+typedef struct {
+    /** set to sizeof(BtHfCallbacks) */
+    size_t      size;
+    bthh_connection_state_callback  connection_state_cb;
+    bthh_hid_info_callback          hid_info_cb;
+    bthh_protocol_mode_callback     protocol_mode_cb;
+    bthh_idle_time_callback         idle_time_cb;
+    bthh_get_report_callback        get_report_cb;
+    bthh_virtual_unplug_callback    virtual_unplug_cb;
+    bthh_handshake_callback         handshake_cb;
+
+} bthh_callbacks_t;
+
+
+
+/** Represents the standard BT-HH interface. */
+typedef struct {
+
+    /** set to sizeof(BtHhInterface) */
+    size_t          size;
+
+    /**
+     * Register the BtHh callbacks
+     */
+    bt_status_t (*init)( bthh_callbacks_t* callbacks );
+
+    /** connect to hid device */
+    bt_status_t (*connect)( bt_bdaddr_t *bd_addr);
+
+    /** dis-connect from hid device */
+    bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr );
+
+    /** Virtual UnPlug (VUP) the specified HID device */
+    bt_status_t (*virtual_unplug)(bt_bdaddr_t *bd_addr);
+
+    /** Set the HID device descriptor for the specified HID device. */
+    bt_status_t (*set_info)(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info );
+
+    /** Get the HID proto mode. */
+    bt_status_t (*get_protocol) (bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocolMode);
+
+    /** Set the HID proto mode. */
+    bt_status_t (*set_protocol)(bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocolMode);
+
+    /** Send a GET_REPORT to HID device. */
+    bt_status_t (*get_report)(bt_bdaddr_t *bd_addr, bthh_report_type_t reportType, uint8_t reportId, int bufferSize);
+
+    /** Send a SET_REPORT to HID device. */
+    bt_status_t (*set_report)(bt_bdaddr_t *bd_addr, bthh_report_type_t reportType, char* report);
+
+    /** Send data to HID device. */
+    bt_status_t (*send_data)(bt_bdaddr_t *bd_addr, char* data);
+
+	/** Closes the interface. */
+    void  (*cleanup)( void );
+
+} bthh_interface_t;
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_HH_H */
+
+
diff --git a/repo/android/hardware/bt_hl.h b/repo/android/hardware/bt_hl.h
new file mode 100644
index 0000000..bd29e3a
--- /dev/null
+++ b/repo/android/hardware/bt_hl.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_HL_H
+#define ANDROID_INCLUDE_BT_HL_H
+
+__BEGIN_DECLS
+
+/* HL connection states */
+
+typedef enum
+{
+    BTHL_MDEP_ROLE_SOURCE,
+    BTHL_MDEP_ROLE_SINK
+} bthl_mdep_role_t;
+
+typedef enum {
+    BTHL_APP_REG_STATE_REG_SUCCESS,
+    BTHL_APP_REG_STATE_REG_FAILED,
+    BTHL_APP_REG_STATE_DEREG_SUCCESS,
+    BTHL_APP_REG_STATE_DEREG_FAILED
+} bthl_app_reg_state_t;
+
+typedef enum
+{
+    BTHL_CHANNEL_TYPE_RELIABLE,
+    BTHL_CHANNEL_TYPE_STREAMING,
+    BTHL_CHANNEL_TYPE_ANY
+} bthl_channel_type_t;
+
+
+/* HL connection states */
+typedef enum {
+    BTHL_CONN_STATE_CONNECTING,
+    BTHL_CONN_STATE_CONNECTED,
+    BTHL_CONN_STATE_DISCONNECTING,
+    BTHL_CONN_STATE_DISCONNECTED,
+    BTHL_CONN_STATE_DESTROYED
+} bthl_channel_state_t;
+
+typedef struct
+{
+    bthl_mdep_role_t        mdep_role;
+    int                     data_type;
+    bthl_channel_type_t     channel_type;
+    const char                   *mdep_description; /* MDEP description to be used in the SDP (optional); null terminated */
+} bthl_mdep_cfg_t;
+
+typedef struct
+{
+    const char      *application_name;
+    const char      *provider_name;   /* provider name to be used in the SDP (optional); null terminated */
+    const char      *srv_name;        /* service name to be used in the SDP (optional); null terminated*/
+    const char      *srv_desp;        /* service description to be used in the SDP (optional); null terminated */
+    int             number_of_mdeps;
+    bthl_mdep_cfg_t *mdep_cfg;  /* Dynamic array */
+} bthl_reg_param_t;
+
+/** Callback for application registration status.
+ *  state will have one of the values from  bthl_app_reg_state_t
+ */
+typedef void (* bthl_app_reg_state_callback)(int app_id, bthl_app_reg_state_t state);
+
+/** Callback for channel connection state change.
+ *  state will have one of the values from
+ *  bthl_connection_state_t and fd (file descriptor)
+ */
+typedef void (* bthl_channel_state_callback)(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int channel_id, bthl_channel_state_t state, int fd);
+
+/** BT-HL callback structure. */
+typedef struct {
+    /** set to sizeof(bthl_callbacks_t) */
+    size_t      size;
+    bthl_app_reg_state_callback     app_reg_state_cb;
+    bthl_channel_state_callback     channel_state_cb;
+} bthl_callbacks_t;
+
+
+/** Represents the standard BT-HL interface. */
+typedef struct {
+
+    /** set to sizeof(bthl_interface_t)  */
+    size_t          size;
+
+    /**
+     * Register the Bthl callbacks
+     */
+    bt_status_t (*init)( bthl_callbacks_t* callbacks );
+
+    /** Register HL application */
+    bt_status_t (*register_application) ( bthl_reg_param_t *p_reg_param, int *app_id);
+
+    /** Unregister HL application */
+    bt_status_t (*unregister_application) (int app_id);
+
+    /** connect channel */
+    bt_status_t (*connect_channel)(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int *channel_id);
+
+    /** destroy channel */
+    bt_status_t (*destroy_channel)(int channel_id);
+
+    /** Close the  Bthl callback **/
+    void (*cleanup)(void);
+
+} bthl_interface_t;
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_HL_H */
+
+
diff --git a/repo/android/hardware/bt_mce.h b/repo/android/hardware/bt_mce.h
new file mode 100644
index 0000000..5d159b3
--- /dev/null
+++ b/repo/android/hardware/bt_mce.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_MCE_H
+#define ANDROID_INCLUDE_BT_MCE_H
+
+__BEGIN_DECLS
+
+/** MAS instance description */
+typedef struct
+{
+    int  id;
+    int  scn;
+    int  msg_types;
+    char *p_name;
+} btmce_mas_instance_t;
+
+/** callback for get_remote_mas_instances */
+typedef void (*btmce_remote_mas_instances_callback)(bt_status_t status, bt_bdaddr_t *bd_addr,
+                                                    int num_instances, btmce_mas_instance_t *instances);
+
+typedef struct {
+    /** set to sizeof(btmce_callbacks_t) */
+    size_t      size;
+    btmce_remote_mas_instances_callback  remote_mas_instances_cb;
+} btmce_callbacks_t;
+
+typedef struct {
+    /** set to size of this struct */
+    size_t size;
+
+    /** register BT MCE callbacks */
+    bt_status_t (*init)(btmce_callbacks_t *callbacks);
+
+    /** search for MAS instances on remote device */
+    bt_status_t (*get_remote_mas_instances)(bt_bdaddr_t *bd_addr);
+} btmce_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_MCE_H */
diff --git a/repo/android/hardware/bt_pan.h b/repo/android/hardware/bt_pan.h
new file mode 100644
index 0000000..83e7949
--- /dev/null
+++ b/repo/android/hardware/bt_pan.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_PAN_H
+#define ANDROID_INCLUDE_BT_PAN_H
+
+__BEGIN_DECLS
+
+#define BTPAN_ROLE_NONE      0
+#define BTPAN_ROLE_PANNAP    1
+#define BTPAN_ROLE_PANU      2
+
+typedef enum {
+    BTPAN_STATE_CONNECTED       = 0,
+    BTPAN_STATE_CONNECTING      = 1,
+    BTPAN_STATE_DISCONNECTED    = 2,
+    BTPAN_STATE_DISCONNECTING   = 3
+} btpan_connection_state_t;
+
+typedef enum {
+    BTPAN_STATE_ENABLED = 0,
+    BTPAN_STATE_DISABLED = 1
+} btpan_control_state_t;
+
+/**
+* Callback for pan connection state
+*/
+typedef void (*btpan_connection_state_callback)(btpan_connection_state_t state, bt_status_t error,
+                                                const bt_bdaddr_t *bd_addr, int local_role, int remote_role);
+typedef void (*btpan_control_state_callback)(btpan_control_state_t state, int local_role,
+                                            bt_status_t error, const char* ifname);
+
+typedef struct {
+    size_t size;
+    btpan_control_state_callback control_state_cb;
+    btpan_connection_state_callback connection_state_cb;
+} btpan_callbacks_t;
+typedef struct {
+    /** set to size of this struct*/
+    size_t          size;
+    /**
+     * Initialize the pan interface and register the btpan callbacks
+     */
+    bt_status_t (*init)(const btpan_callbacks_t* callbacks);
+    /*
+     * enable the pan service by specified role. The result state of
+     * enabl will be returned by btpan_control_state_callback. when pan-nap is enabled,
+     * the state of connecting panu device will be notified by btpan_connection_state_callback
+     */
+    bt_status_t (*enable)(int local_role);
+    /*
+     * get current pan local role
+     */
+    int (*get_local_role)(void);
+    /**
+     * start bluetooth pan connection to the remote device by specified pan role. The result state will be
+     * returned by btpan_connection_state_callback
+     */
+    bt_status_t (*connect)(const bt_bdaddr_t *bd_addr, int local_role, int remote_role);
+    /**
+     * stop bluetooth pan connection. The result state will be returned by btpan_connection_state_callback
+     */
+    bt_status_t (*disconnect)(const bt_bdaddr_t *bd_addr);
+
+    /**
+     * Cleanup the pan interface
+     */
+    void (*cleanup)(void);
+
+} btpan_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_PAN_H */
diff --git a/repo/android/hardware/bt_rc.h b/repo/android/hardware/bt_rc.h
new file mode 100644
index 0000000..c565c48
--- /dev/null
+++ b/repo/android/hardware/bt_rc.h
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_RC_H
+#define ANDROID_INCLUDE_BT_RC_H
+
+__BEGIN_DECLS
+
+/* Macros */
+#define BTRC_MAX_ATTR_STR_LEN       255
+#define BTRC_UID_SIZE               8
+#define BTRC_MAX_APP_SETTINGS       8
+#define BTRC_MAX_FOLDER_DEPTH       4
+#define BTRC_MAX_APP_ATTR_SIZE      16
+#define BTRC_MAX_ELEM_ATTR_SIZE     7
+
+typedef uint8_t btrc_uid_t[BTRC_UID_SIZE];
+
+typedef enum {
+    BTRC_FEAT_NONE = 0x00,    /* AVRCP 1.0 */
+    BTRC_FEAT_METADATA = 0x01,    /* AVRCP 1.3 */
+    BTRC_FEAT_ABSOLUTE_VOLUME = 0x02,    /* Supports TG role and volume sync */
+    BTRC_FEAT_BROWSE = 0x04,    /* AVRCP 1.4 and up, with Browsing support */
+} btrc_remote_features_t;
+
+typedef enum {
+    BTRC_PLAYSTATE_STOPPED = 0x00,    /* Stopped */
+    BTRC_PLAYSTATE_PLAYING = 0x01,    /* Playing */
+    BTRC_PLAYSTATE_PAUSED = 0x02,    /* Paused  */
+    BTRC_PLAYSTATE_FWD_SEEK = 0x03,    /* Fwd Seek*/
+    BTRC_PLAYSTATE_REV_SEEK = 0x04,    /* Rev Seek*/
+    BTRC_PLAYSTATE_ERROR = 0xFF,    /* Error   */
+} btrc_play_status_t;
+
+typedef enum {
+    BTRC_EVT_PLAY_STATUS_CHANGED = 0x01,
+    BTRC_EVT_TRACK_CHANGE = 0x02,
+    BTRC_EVT_TRACK_REACHED_END = 0x03,
+    BTRC_EVT_TRACK_REACHED_START = 0x04,
+    BTRC_EVT_PLAY_POS_CHANGED = 0x05,
+    BTRC_EVT_APP_SETTINGS_CHANGED = 0x08,
+} btrc_event_id_t;
+
+typedef enum {
+    BTRC_NOTIFICATION_TYPE_INTERIM = 0,
+    BTRC_NOTIFICATION_TYPE_CHANGED = 1,
+} btrc_notification_type_t;
+
+typedef enum {
+    BTRC_PLAYER_ATTR_EQUALIZER = 0x01,
+    BTRC_PLAYER_ATTR_REPEAT = 0x02,
+    BTRC_PLAYER_ATTR_SHUFFLE = 0x03,
+    BTRC_PLAYER_ATTR_SCAN = 0x04,
+} btrc_player_attr_t;
+
+typedef enum {
+    BTRC_MEDIA_ATTR_TITLE = 0x01,
+    BTRC_MEDIA_ATTR_ARTIST = 0x02,
+    BTRC_MEDIA_ATTR_ALBUM = 0x03,
+    BTRC_MEDIA_ATTR_TRACK_NUM = 0x04,
+    BTRC_MEDIA_ATTR_NUM_TRACKS = 0x05,
+    BTRC_MEDIA_ATTR_GENRE = 0x06,
+    BTRC_MEDIA_ATTR_PLAYING_TIME = 0x07,
+} btrc_media_attr_t;
+
+typedef enum {
+    BTRC_PLAYER_VAL_OFF_REPEAT = 0x01,
+    BTRC_PLAYER_VAL_SINGLE_REPEAT = 0x02,
+    BTRC_PLAYER_VAL_ALL_REPEAT = 0x03,
+    BTRC_PLAYER_VAL_GROUP_REPEAT = 0x04
+} btrc_player_repeat_val_t;
+
+typedef enum {
+    BTRC_PLAYER_VAL_OFF_SHUFFLE = 0x01,
+    BTRC_PLAYER_VAL_ALL_SHUFFLE = 0x02,
+    BTRC_PLAYER_VAL_GROUP_SHUFFLE = 0x03
+} btrc_player_shuffle_val_t;
+
+typedef enum {
+    BTRC_STS_BAD_CMD        = 0x00, /* Invalid command */
+    BTRC_STS_BAD_PARAM      = 0x01, /* Invalid parameter */
+    BTRC_STS_NOT_FOUND      = 0x02, /* Specified parameter is wrong or not found */
+    BTRC_STS_INTERNAL_ERR   = 0x03, /* Internal Error */
+    BTRC_STS_NO_ERROR       = 0x04  /* Operation Success */
+} btrc_status_t;
+
+typedef struct {
+    uint8_t num_attr;
+    uint8_t attr_ids[BTRC_MAX_APP_SETTINGS];
+    uint8_t attr_values[BTRC_MAX_APP_SETTINGS];
+} btrc_player_settings_t;
+
+typedef union
+{
+    btrc_play_status_t play_status;
+    btrc_uid_t track; /* queue position in NowPlaying */
+    uint32_t song_pos;
+    btrc_player_settings_t player_setting;
+} btrc_register_notification_t;
+
+typedef struct {
+    uint8_t id; /* can be attr_id or value_id */
+    uint8_t text[BTRC_MAX_ATTR_STR_LEN];
+} btrc_player_setting_text_t;
+
+typedef struct {
+    uint32_t attr_id;
+    uint8_t text[BTRC_MAX_ATTR_STR_LEN];
+} btrc_element_attr_val_t;
+
+/** Callback for the controller's supported feautres */
+typedef void (* btrc_remote_features_callback)(bt_bdaddr_t *bd_addr,
+                                                      btrc_remote_features_t features);
+
+/** Callback for play status request */
+typedef void (* btrc_get_play_status_callback)();
+
+/** Callback for list player application attributes (Shuffle, Repeat,...) */
+typedef void (* btrc_list_player_app_attr_callback)();
+
+/** Callback for list player application attributes (Shuffle, Repeat,...) */
+typedef void (* btrc_list_player_app_values_callback)(btrc_player_attr_t attr_id);
+
+/** Callback for getting the current player application settings value
+**  num_attr: specifies the number of attribute ids contained in p_attrs
+*/
+typedef void (* btrc_get_player_app_value_callback) (uint8_t num_attr, btrc_player_attr_t *p_attrs);
+
+/** Callback for getting the player application settings attributes' text
+**  num_attr: specifies the number of attribute ids contained in p_attrs
+*/
+typedef void (* btrc_get_player_app_attrs_text_callback) (uint8_t num_attr, btrc_player_attr_t *p_attrs);
+
+/** Callback for getting the player application settings values' text
+**  num_attr: specifies the number of value ids contained in p_vals
+*/
+typedef void (* btrc_get_player_app_values_text_callback) (uint8_t attr_id, uint8_t num_val, uint8_t *p_vals);
+
+/** Callback for setting the player application settings values */
+typedef void (* btrc_set_player_app_value_callback) (btrc_player_settings_t *p_vals);
+
+/** Callback to fetch the get element attributes of the current song
+**  num_attr: specifies the number of attributes requested in p_attrs
+*/
+typedef void (* btrc_get_element_attr_callback) (uint8_t num_attr, btrc_media_attr_t *p_attrs);
+
+/** Callback for register notification (Play state change/track change/...)
+**  param: Is only valid if event_id is BTRC_EVT_PLAY_POS_CHANGED
+*/
+typedef void (* btrc_register_notification_callback) (btrc_event_id_t event_id, uint32_t param);
+
+/* AVRCP 1.4 Enhancements */
+/** Callback for volume change on CT
+**  volume: Current volume setting on the CT (0-127)
+*/
+typedef void (* btrc_volume_change_callback) (uint8_t volume, uint8_t ctype);
+
+/** Callback for passthrough commands */
+typedef void (* btrc_passthrough_cmd_callback) (int id, int key_state);
+
+/** BT-RC Target callback structure. */
+typedef struct {
+    /** set to sizeof(BtRcCallbacks) */
+    size_t      size;
+    btrc_remote_features_callback               remote_features_cb;
+    btrc_get_play_status_callback               get_play_status_cb;
+    btrc_list_player_app_attr_callback          list_player_app_attr_cb;
+    btrc_list_player_app_values_callback        list_player_app_values_cb;
+    btrc_get_player_app_value_callback          get_player_app_value_cb;
+    btrc_get_player_app_attrs_text_callback     get_player_app_attrs_text_cb;
+    btrc_get_player_app_values_text_callback    get_player_app_values_text_cb;
+    btrc_set_player_app_value_callback          set_player_app_value_cb;
+    btrc_get_element_attr_callback              get_element_attr_cb;
+    btrc_register_notification_callback         register_notification_cb;
+    btrc_volume_change_callback                 volume_change_cb;
+    btrc_passthrough_cmd_callback               passthrough_cmd_cb;
+} btrc_callbacks_t;
+
+/** Represents the standard BT-RC AVRCP Target interface. */
+typedef struct {
+
+    /** set to sizeof(BtRcInterface) */
+    size_t          size;
+    /**
+     * Register the BtRc callbacks
+     */
+    bt_status_t (*init)( btrc_callbacks_t* callbacks );
+
+    /** Respose to GetPlayStatus request. Contains the current
+    **  1. Play status
+    **  2. Song duration/length
+    **  3. Song position
+    */
+    bt_status_t (*get_play_status_rsp)( btrc_play_status_t play_status, uint32_t song_len, uint32_t song_pos);
+
+    /** Lists the support player application attributes (Shuffle/Repeat/...)
+    **  num_attr: Specifies the number of attributes contained in the pointer p_attrs
+    */
+    bt_status_t (*list_player_app_attr_rsp)( int num_attr, btrc_player_attr_t *p_attrs);
+
+    /** Lists the support player application attributes (Shuffle Off/On/Group)
+    **  num_val: Specifies the number of values contained in the pointer p_vals
+    */
+    bt_status_t (*list_player_app_value_rsp)( int num_val, uint8_t *p_vals);
+
+    /** Returns the current application attribute values for each of the specified attr_id */
+    bt_status_t (*get_player_app_value_rsp)( btrc_player_settings_t *p_vals);
+
+    /** Returns the application attributes text ("Shuffle"/"Repeat"/...)
+    **  num_attr: Specifies the number of attributes' text contained in the pointer p_attrs
+    */
+    bt_status_t (*get_player_app_attr_text_rsp)( int num_attr, btrc_player_setting_text_t *p_attrs);
+
+    /** Returns the application attributes text ("Shuffle"/"Repeat"/...)
+    **  num_attr: Specifies the number of attribute values' text contained in the pointer p_vals
+    */
+    bt_status_t (*get_player_app_value_text_rsp)( int num_val, btrc_player_setting_text_t *p_vals);
+
+    /** Returns the current songs' element attributes text ("Title"/"Album"/"Artist")
+    **  num_attr: Specifies the number of attributes' text contained in the pointer p_attrs
+    */
+    bt_status_t (*get_element_attr_rsp)( uint8_t num_attr, btrc_element_attr_val_t *p_attrs);
+
+    /** Response to set player attribute request ("Shuffle"/"Repeat")
+    **  rsp_status: Status of setting the player attributes for the current media player
+    */
+    bt_status_t (*set_player_app_value_rsp)(btrc_status_t rsp_status);
+
+    /* Response to the register notification request (Play state change/track change/...).
+    ** event_id: Refers to the event_id this notification change corresponds too
+    ** type: Response type - interim/changed
+    ** p_params: Based on the event_id, this parameter should be populated
+    */
+    bt_status_t (*register_notification_rsp)(btrc_event_id_t event_id,
+                                             btrc_notification_type_t type,
+                                             btrc_register_notification_t *p_param);
+
+    /* AVRCP 1.4 enhancements */
+
+    /**Send current volume setting to remote side. Support limited to SetAbsoluteVolume
+    ** This can be enhanced to support Relative Volume (AVRCP 1.0).
+    ** With RelateVolume, we will send VOLUME_UP/VOLUME_DOWN opposed to absolute volume level
+    ** volume: Should be in the range 0-127. bit7 is reseved and cannot be set
+    */
+    bt_status_t (*set_volume)(uint8_t volume);
+
+    /** Closes the interface. */
+    void  (*cleanup)( void );
+} btrc_interface_t;
+
+
+typedef void (* btrc_passthrough_rsp_callback) (int id, int key_state);
+
+typedef void (* btrc_connection_state_callback) (bool state, bt_bdaddr_t *bd_addr);
+
+/** BT-RC Controller callback structure. */
+typedef struct {
+    /** set to sizeof(BtRcCallbacks) */
+    size_t      size;
+    btrc_passthrough_rsp_callback               passthrough_rsp_cb;
+    btrc_connection_state_callback              connection_state_cb;
+} btrc_ctrl_callbacks_t;
+
+/** Represents the standard BT-RC AVRCP Controller interface. */
+typedef struct {
+
+    /** set to sizeof(BtRcInterface) */
+    size_t          size;
+    /**
+     * Register the BtRc callbacks
+     */
+    bt_status_t (*init)( btrc_ctrl_callbacks_t* callbacks );
+
+    /** send pass through command to target */
+    bt_status_t (*send_pass_through_cmd) ( bt_bdaddr_t *bd_addr, uint8_t key_code, uint8_t key_state );
+
+    /** Closes the interface. */
+    void  (*cleanup)( void );
+} btrc_ctrl_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_RC_H */
diff --git a/repo/android/hardware/bt_sock.h b/repo/android/hardware/bt_sock.h
new file mode 100644
index 0000000..a4aa046
--- /dev/null
+++ b/repo/android/hardware/bt_sock.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_SOCK_H
+#define ANDROID_INCLUDE_BT_SOCK_H
+
+__BEGIN_DECLS
+
+#define BTSOCK_FLAG_ENCRYPT 1
+#define BTSOCK_FLAG_AUTH (1 << 1)
+
+typedef enum {
+    BTSOCK_RFCOMM = 1,
+    BTSOCK_SCO = 2,
+    BTSOCK_L2CAP = 3
+} btsock_type_t;
+
+/** Represents the standard BT SOCKET interface. */
+typedef struct {
+    short size;
+    bt_bdaddr_t bd_addr;
+    int channel;
+    int status;
+} __attribute__((packed)) sock_connect_signal_t;
+
+typedef struct {
+
+    /** set to size of this struct*/
+    size_t          size;
+    /**
+     * listen to a rfcomm uuid or channel. It returns the socket fd from which
+     * btsock_connect_signal can be read out when a remote device connected
+     */
+    bt_status_t (*listen)(btsock_type_t type, const char* service_name, const uint8_t* service_uuid, int channel, int* sock_fd, int flags);
+    /*
+     * connect to a rfcomm uuid channel of remote device, It returns the socket fd from which
+     * the btsock_connect_signal and a new socket fd to be accepted can be read out when connected
+     */
+    bt_status_t (*connect)(const bt_bdaddr_t *bd_addr, btsock_type_t type, const uint8_t* uuid, int channel, int* sock_fd, int flags);
+
+} btsock_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_SOCK_H */
diff --git a/repo/android/hardware/hardware.c b/repo/android/hardware/hardware.c
new file mode 100644
index 0000000..4bd5eba
--- /dev/null
+++ b/repo/android/hardware/hardware.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hardware/hardware.h>
+
+#include <dlfcn.h>
+#include <string.h>
+#include <pthread.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+
+#define LOG_TAG "HAL"
+
+#define LOG_INFO " I"
+#define LOG_WARN " W"
+#define LOG_ERROR " E"
+#define LOG_DEBUG " D"
+#define ALOG(pri, tag, fmt, arg...) fprintf(stderr, tag pri": " fmt"\n", ##arg)
+
+#define info(fmt, arg...) ALOG(LOG_INFO, LOG_TAG, fmt, ##arg)
+#define warn(fmt, arg...) ALOG(LOG_WARN, LOG_TAG, fmt, ##arg)
+#define error(fmt, arg...) ALOG(LOG_ERROR, LOG_TAG, fmt, ##arg)
+
+/**
+ * Load the file defined by the variant and if successful
+ * return the dlopen handle and the hmi.
+ * @return 0 = success, !0 = failure.
+ */
+static int load(const char *id,
+        const char *path,
+        const struct hw_module_t **pHmi)
+{
+    int status;
+    void *handle;
+    struct hw_module_t *hmi;
+    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
+
+    /*
+     * load the symbols resolving undefined symbols before
+     * dlopen returns. Since RTLD_GLOBAL is not or'd in with
+     * RTLD_NOW the external symbols will not be global
+     */
+    handle = dlopen(path, RTLD_NOW);
+    if (handle == NULL) {
+        char const *err_str = dlerror();
+        error("load: module=%s\n%s", path, err_str?err_str:"unknown");
+        status = -EINVAL;
+        goto done;
+    }
+
+    /* Get the address of the struct hal_module_info. */
+    hmi = (struct hw_module_t *)dlsym(handle, sym);
+    if (hmi == NULL) {
+        error("load: couldn't find symbol %s", sym);
+        status = -EINVAL;
+        goto done;
+    }
+
+    /* Check that the id matches */
+    if (strcmp(id, hmi->id) != 0) {
+        error("load: id=%s != hmi->id=%s", id, hmi->id);
+        status = -EINVAL;
+        goto done;
+    }
+
+    hmi->dso = handle;
+
+    *pHmi = hmi;
+
+    info("loaded HAL id=%s path=%s hmi=%p handle=%p",
+                id, path, *pHmi, handle);
+
+    return 0;
+
+done:
+    hmi = NULL;
+    if (handle != NULL) {
+        dlclose(handle);
+        handle = NULL;
+    }
+
+    return status;
+}
+
+int hw_get_module_by_class(const char *class_id, const char *inst,
+                           const struct hw_module_t **module)
+{
+    char path[PATH_MAX];
+    char name[PATH_MAX];
+
+    if (inst)
+        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
+    else
+        snprintf(name, PATH_MAX, "%s", class_id);
+
+    /*
+     * Here we rely on the fact that calling dlopen multiple times on
+     * the same .so will simply increment a refcount (and not load
+     * a new copy of the library).
+     * We also assume that dlopen() is thread-safe.
+     */
+    snprintf(path, sizeof(path), "%s/%s.default.so", PLUGINDIR, name);
+
+    return load(class_id, path, module);
+}
+
+int hw_get_module(const char *id, const struct hw_module_t **module)
+{
+    return hw_get_module_by_class(id, NULL, module);
+}
diff --git a/repo/android/hardware/hardware.h b/repo/android/hardware/hardware.h
new file mode 100644
index 0000000..c7e8cc7
--- /dev/null
+++ b/repo/android/hardware/hardware.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_HARDWARE_HARDWARE_H
+#define ANDROID_INCLUDE_HARDWARE_HARDWARE_H
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/*
+ * Value for the hw_module_t.tag field
+ */
+
+#define MAKE_TAG_CONSTANT(A,B,C,D) (((A) << 24) | ((B) << 16) | ((C) << 8) | (D))
+
+#define HARDWARE_MODULE_TAG MAKE_TAG_CONSTANT('H', 'W', 'M', 'T')
+#define HARDWARE_DEVICE_TAG MAKE_TAG_CONSTANT('H', 'W', 'D', 'T')
+
+#define HARDWARE_MAKE_API_VERSION(maj,min) \
+            ((((maj) & 0xff) << 8) | ((min) & 0xff))
+
+#define HARDWARE_MAKE_API_VERSION_2(maj,min,hdr) \
+            ((((maj) & 0xff) << 24) | (((min) & 0xff) << 16) | ((hdr) & 0xffff))
+#define HARDWARE_API_VERSION_2_MAJ_MIN_MASK 0xffff0000
+#define HARDWARE_API_VERSION_2_HEADER_MASK  0x0000ffff
+
+
+/*
+ * The current HAL API version.
+ *
+ * All module implementations must set the hw_module_t.hal_api_version field
+ * to this value when declaring the module with HAL_MODULE_INFO_SYM.
+ *
+ * Note that previous implementations have always set this field to 0.
+ * Therefore, libhardware HAL API will always consider versions 0.0 and 1.0
+ * to be 100% binary compatible.
+ *
+ */
+#define HARDWARE_HAL_API_VERSION HARDWARE_MAKE_API_VERSION(1, 0)
+
+/*
+ * Helper macros for module implementors.
+ *
+ * The derived modules should provide convenience macros for supported
+ * versions so that implementations can explicitly specify module/device
+ * versions at definition time.
+ *
+ * Use this macro to set the hw_module_t.module_api_version field.
+ */
+#define HARDWARE_MODULE_API_VERSION(maj,min) HARDWARE_MAKE_API_VERSION(maj,min)
+#define HARDWARE_MODULE_API_VERSION_2(maj,min,hdr) HARDWARE_MAKE_API_VERSION_2(maj,min,hdr)
+
+/*
+ * Use this macro to set the hw_device_t.version field
+ */
+#define HARDWARE_DEVICE_API_VERSION(maj,min) HARDWARE_MAKE_API_VERSION(maj,min)
+#define HARDWARE_DEVICE_API_VERSION_2(maj,min,hdr) HARDWARE_MAKE_API_VERSION_2(maj,min,hdr)
+
+struct hw_module_t;
+struct hw_module_methods_t;
+struct hw_device_t;
+
+/**
+ * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
+ * and the fields of this data structure must begin with hw_module_t
+ * followed by module specific information.
+ */
+typedef struct hw_module_t {
+    /** tag must be initialized to HARDWARE_MODULE_TAG */
+    uint32_t tag;
+
+    /**
+     * The API version of the implemented module. The module owner is
+     * responsible for updating the version when a module interface has
+     * changed.
+     *
+     * The derived modules such as gralloc and audio own and manage this field.
+     * The module user must interpret the version field to decide whether or
+     * not to inter-operate with the supplied module implementation.
+     * For example, SurfaceFlinger is responsible for making sure that
+     * it knows how to manage different versions of the gralloc-module API,
+     * and AudioFlinger must know how to do the same for audio-module API.
+     *
+     * The module API version should include a major and a minor component.
+     * For example, version 1.0 could be represented as 0x0100. This format
+     * implies that versions 0x0100-0x01ff are all API-compatible.
+     *
+     * In the future, libhardware will expose a hw_get_module_version()
+     * (or equivalent) function that will take minimum/maximum supported
+     * versions as arguments and would be able to reject modules with
+     * versions outside of the supplied range.
+     */
+    uint16_t module_api_version;
+#define version_major module_api_version
+    /**
+     * version_major/version_minor defines are supplied here for temporary
+     * source code compatibility. They will be removed in the next version.
+     * ALL clients must convert to the new version format.
+     */
+
+    /**
+     * The API version of the HAL module interface. This is meant to
+     * version the hw_module_t, hw_module_methods_t, and hw_device_t
+     * structures and definitions.
+     *
+     * The HAL interface owns this field. Module users/implementations
+     * must NOT rely on this value for version information.
+     *
+     * Presently, 0 is the only valid value.
+     */
+    uint16_t hal_api_version;
+#define version_minor hal_api_version
+
+    /** Identifier of module */
+    const char *id;
+
+    /** Name of this module */
+    const char *name;
+
+    /** Author/owner/implementor of the module */
+    const char *author;
+
+    /** Modules methods */
+    struct hw_module_methods_t* methods;
+
+    /** module's dso */
+    void* dso;
+
+    /** padding to 128 bytes, reserved for future use */
+    uint32_t reserved[32-7];
+
+} hw_module_t;
+
+typedef struct hw_module_methods_t {
+    /** Open a specific device */
+    int (*open)(const struct hw_module_t* module, const char* id,
+            struct hw_device_t** device);
+
+} hw_module_methods_t;
+
+/**
+ * Every device data structure must begin with hw_device_t
+ * followed by module specific public methods and attributes.
+ */
+typedef struct hw_device_t {
+    /** tag must be initialized to HARDWARE_DEVICE_TAG */
+    uint32_t tag;
+
+    /**
+     * Version of the module-specific device API. This value is used by
+     * the derived-module user to manage different device implementations.
+     *
+     * The module user is responsible for checking the module_api_version
+     * and device version fields to ensure that the user is capable of
+     * communicating with the specific module implementation.
+     *
+     * One module can support multiple devices with different versions. This
+     * can be useful when a device interface changes in an incompatible way
+     * but it is still necessary to support older implementations at the same
+     * time. One such example is the Camera 2.0 API.
+     *
+     * This field is interpreted by the module user and is ignored by the
+     * HAL interface itself.
+     */
+    uint32_t version;
+
+    /** reference to the module this device belongs to */
+    struct hw_module_t* module;
+
+    /** padding reserved for future use */
+    uint32_t reserved[12];
+
+    /** Close this device */
+    int (*close)(struct hw_device_t* device);
+
+} hw_device_t;
+
+/**
+ * Name of the hal_module_info
+ */
+#define HAL_MODULE_INFO_SYM         HMI
+
+/**
+ * Name of the hal_module_info as a string
+ */
+#define HAL_MODULE_INFO_SYM_AS_STR  "HMI"
+
+/**
+ * Get the module info associated with a module by id.
+ *
+ * @return: 0 == success, <0 == error and *module == NULL
+ */
+int hw_get_module(const char *id, const struct hw_module_t **module);
+
+/**
+ * Get the module info associated with a module instance by class 'class_id'
+ * and instance 'inst'.
+ *
+ * Some modules types necessitate multiple instances. For example audio supports
+ * multiple concurrent interfaces and thus 'audio' is the module class
+ * and 'primary' or 'a2dp' are module interfaces. This implies that the files
+ * providing these modules would be named audio.primary.<variant>.so and
+ * audio.a2dp.<variant>.so
+ *
+ * @return: 0 == success, <0 == error and *module == NULL
+ */
+int hw_get_module_by_class(const char *class_id, const char *inst,
+                           const struct hw_module_t **module);
+
+__END_DECLS
+
+#endif  /* ANDROID_INCLUDE_HARDWARE_HARDWARE_H */
diff --git a/repo/android/health.c b/repo/android/health.c
new file mode 100644
index 0000000..eb02a64
--- /dev/null
+++ b/repo/android/health.c
@@ -0,0 +1,2047 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *
+ *  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+#include "lib/l2cap.h"
+#include "src/log.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/uuid-helper.h"
+#include "src/sdp-client.h"
+#include "profiles/health/mcap.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "utils.h"
+#include "bluetooth.h"
+#include "health.h"
+
+#define SVC_HINT_HEALTH			0x00
+#define HDP_VERSION			0x0101
+#define DATA_EXCHANGE_SPEC_11073	0x01
+
+#define CHANNEL_TYPE_ANY       0x00
+#define CHANNEL_TYPE_RELIABLE  0x01
+#define CHANNEL_TYPE_STREAM    0x02
+
+#define MDEP_ECHO		0x00
+#define MDEP_INITIAL		0x01
+#define MDEP_FINAL		0x7F
+
+static bdaddr_t adapter_addr;
+static struct ipc *hal_ipc = NULL;
+static struct queue *apps = NULL;
+static struct mcap_instance *mcap = NULL;
+static uint32_t record_id = 0;
+static uint32_t record_state = 0;
+
+struct mdep_cfg {
+	uint8_t role;
+	uint16_t data_type;
+	uint8_t channel_type;
+	char *descr;
+
+	uint8_t id; /* mdep id */
+};
+
+struct health_device {
+	bdaddr_t dst;
+	uint16_t app_id;
+
+	struct mcap_mcl *mcl;
+
+	struct queue *channels;     /* data channels */
+
+	uint16_t ccpsm;
+	uint16_t dcpsm;
+};
+
+struct health_channel {
+	uint8_t mdep_id;
+	uint8_t type;
+
+	struct health_device *dev;
+
+	uint8_t remote_mdep;
+	struct mcap_mdl *mdl;
+	bool mdl_conn;
+	uint16_t mdl_id; /* MDL ID */
+
+	uint16_t id; /* channel id */
+};
+
+struct health_app {
+	char *app_name;
+	char *provider_name;
+	char *service_name;
+	char *service_descr;
+	uint8_t num_of_mdep;
+	struct queue *mdeps;
+
+	uint16_t id; /* app id */
+	struct queue *devices;
+};
+
+static void send_app_reg_notify(struct health_app *app, uint8_t state)
+{
+	struct hal_ev_health_app_reg_state ev;
+
+	DBG("");
+
+	ev.id = app->id;
+	ev.state = state;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HEALTH,
+				HAL_EV_HEALTH_APP_REG_STATE, sizeof(ev), &ev);
+}
+
+static void send_channel_state_notify(struct health_channel *channel,
+						uint8_t state, int fd)
+{
+	struct hal_ev_health_channel_state ev;
+
+	DBG("");
+
+	bdaddr2android(&channel->dev->dst, ev.bdaddr);
+	ev.app_id = channel->dev->app_id;
+	ev.mdep_index = channel->mdep_id - 1;
+	ev.channel_id = channel->id;
+	ev.channel_state = state;
+
+	ipc_send_notif_with_fd(hal_ipc, HAL_SERVICE_ID_HEALTH,
+					HAL_EV_HEALTH_CHANNEL_STATE,
+					sizeof(ev), &ev, fd);
+}
+
+static void unref_mdl(struct health_channel *channel)
+{
+	if (!channel || !channel->mdl)
+		return;
+
+	mcap_mdl_unref(channel->mdl);
+	channel->mdl = NULL;
+	channel->mdl_conn = false;
+}
+
+static void free_health_channel(void *data)
+{
+	struct health_channel *channel = data;
+	int fd;
+
+	DBG("channel %p", channel);
+
+	if (!channel)
+		return;
+
+	fd = mcap_mdl_get_fd(channel->mdl);
+	if (fd >= 0)
+		shutdown(fd, SHUT_RDWR);
+
+	unref_mdl(channel);
+	free(channel);
+}
+
+static void destroy_channel(void *data)
+{
+	struct health_channel *channel = data;
+
+	if (!channel)
+		return;
+
+	send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_DESTROYED, -1);
+	queue_remove(channel->dev->channels, channel);
+	free_health_channel(channel);
+}
+
+static void unref_mcl(struct health_device *dev)
+{
+	if (!dev || !dev->mcl)
+		return;
+
+	mcap_close_mcl(dev->mcl, FALSE);
+	mcap_mcl_unref(dev->mcl);
+	dev->mcl = NULL;
+}
+
+static void free_health_device(void *data)
+{
+	struct health_device *dev = data;
+
+	if (!dev)
+		return;
+
+	unref_mcl(dev);
+	queue_destroy(dev->channels, free_health_channel);
+	free(dev);
+}
+
+static void free_mdep_cfg(void *data)
+{
+	struct mdep_cfg *cfg = data;
+
+	if (!cfg)
+		return;
+
+	free(cfg->descr);
+	free(cfg);
+}
+
+static void free_health_app(void *data)
+{
+	struct health_app *app = data;
+
+	if (!app)
+		return;
+
+	free(app->app_name);
+	free(app->provider_name);
+	free(app->service_name);
+	free(app->service_descr);
+	queue_destroy(app->mdeps, free_mdep_cfg);
+	queue_destroy(app->devices, free_health_device);
+	free(app);
+}
+
+static bool match_channel_by_mdl(const void *data, const void *user_data)
+{
+	const struct health_channel *channel = data;
+	const struct mcap_mdl *mdl = user_data;
+
+	return channel->mdl == mdl;
+}
+
+static bool match_channel_by_id(const void *data, const void *user_data)
+{
+	const struct health_channel *channel = data;
+	uint16_t channel_id = PTR_TO_INT(user_data);
+
+	return channel->id == channel_id;
+}
+
+static bool match_dev_by_mcl(const void *data, const void *user_data)
+{
+	const struct health_device *dev = data;
+	const struct mcap_mcl *mcl = user_data;
+
+	return dev->mcl == mcl;
+}
+
+static bool match_dev_by_addr(const void *data, const void *user_data)
+{
+	const struct health_device *dev = data;
+	const bdaddr_t *addr = user_data;
+
+	return !bacmp(&dev->dst, addr);
+}
+
+static bool match_channel_by_mdep_id(const void *data, const void *user_data)
+{
+	const struct health_channel *channel = data;
+	uint16_t mdep_id = PTR_TO_INT(user_data);
+
+	return channel->mdep_id == mdep_id;
+}
+
+static bool match_mdep_by_role(const void *data, const void *user_data)
+{
+	const struct mdep_cfg *mdep = data;
+	uint16_t role = PTR_TO_INT(user_data);
+
+	return mdep->role == role;
+}
+
+static bool match_mdep_by_id(const void *data, const void *user_data)
+{
+	const struct mdep_cfg *mdep = data;
+	uint16_t mdep_id = PTR_TO_INT(user_data);
+
+	return mdep->id == mdep_id;
+}
+
+static bool match_app_by_id(const void *data, const void *user_data)
+{
+	const struct health_app *app = data;
+	uint16_t app_id = PTR_TO_INT(user_data);
+
+	return app->id == app_id;
+}
+
+static struct health_channel *search_channel_by_id(uint16_t id)
+{
+	const struct queue_entry *apps_entry, *devices_entry;
+	struct health_app *app;
+	struct health_channel *channel;
+	struct health_device *dev;
+
+	DBG("");
+
+	apps_entry = queue_get_entries(apps);
+	while (apps_entry) {
+		app = apps_entry->data;
+		devices_entry = queue_get_entries(app->devices);
+		while (devices_entry) {
+			dev = devices_entry->data;
+			channel = queue_find(dev->channels, match_channel_by_id,
+								INT_TO_PTR(id));
+
+			if (channel)
+				return channel;
+
+			devices_entry = devices_entry->next;
+		}
+
+		apps_entry = apps_entry->next;
+	}
+
+	return NULL;
+}
+
+static struct health_channel *search_channel_by_mdl(struct mcap_mdl *mdl)
+{
+	const struct queue_entry *apps_entry, *devices_entry;
+	struct health_app *app;
+	struct health_channel *channel;
+	struct health_device *dev;
+
+	DBG("");
+
+	apps_entry = queue_get_entries(apps);
+	while (apps_entry) {
+		app = apps_entry->data;
+		devices_entry = queue_get_entries(app->devices);
+		while (devices_entry) {
+			dev = devices_entry->data;
+			channel = queue_find(dev->channels,
+						match_channel_by_mdl, mdl);
+
+			if (channel)
+				return channel;
+
+			devices_entry = devices_entry->next;
+		}
+
+		apps_entry = apps_entry->next;
+	}
+
+	return NULL;
+}
+
+static struct health_device *search_dev_by_mcl(struct mcap_mcl *mcl)
+{
+	const struct queue_entry *apps_entry;
+	struct health_app *app;
+	struct health_device *dev;
+
+	DBG("");
+
+	apps_entry = queue_get_entries(apps);
+	while (apps_entry) {
+		app = apps_entry->data;
+
+		dev = queue_find(app->devices, match_dev_by_mcl, mcl);
+
+		if (dev)
+			return dev;
+
+		apps_entry = apps_entry->next;
+	}
+
+	return NULL;
+}
+
+static struct health_app *search_app_by_mdepid(uint8_t mdepid)
+{
+	const struct queue_entry *apps_entry;
+	struct health_app *app;
+
+	DBG("");
+
+	apps_entry = queue_get_entries(apps);
+	while (apps_entry) {
+		app = apps_entry->data;
+
+		if (queue_find(app->mdeps, match_mdep_by_id,
+							INT_TO_PTR(mdepid)))
+			return app;
+
+		apps_entry = apps_entry->next;
+	}
+
+	return NULL;
+}
+
+static int register_service_protocols(sdp_record_t *rec,
+					struct health_app *app)
+{
+	uuid_t l2cap_uuid, mcap_c_uuid;
+	sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+	sdp_list_t *access_proto_list = NULL;
+	sdp_data_t *psm = NULL, *mcap_ver = NULL;
+	uint32_t ccpsm;
+	uint16_t version = MCAP_VERSION;
+	GError *err = NULL;
+	int ret = -1;
+
+	DBG("");
+
+	/* set l2cap information */
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+	if (!l2cap_list)
+		goto fail;
+
+	ccpsm = mcap_get_ctrl_psm(mcap, &err);
+	if (err)
+		goto fail;
+
+	psm = sdp_data_alloc(SDP_UINT16, &ccpsm);
+	if (!psm)
+		goto fail;
+
+	if (!sdp_list_append(l2cap_list, psm))
+		goto fail;
+
+	proto_list = sdp_list_append(NULL, l2cap_list);
+	if (!proto_list)
+		goto fail;
+
+	/* set mcap information */
+	sdp_uuid16_create(&mcap_c_uuid, MCAP_CTRL_UUID);
+	mcap_list = sdp_list_append(NULL, &mcap_c_uuid);
+	if (!mcap_list)
+		goto fail;
+
+	mcap_ver = sdp_data_alloc(SDP_UINT16, &version);
+	if (!mcap_ver)
+		goto fail;
+
+	if (!sdp_list_append(mcap_list, mcap_ver))
+		goto fail;
+
+	if (!sdp_list_append(proto_list, mcap_list))
+		goto fail;
+
+	/* attach protocol information to service record */
+	access_proto_list = sdp_list_append(NULL, proto_list);
+	if (!access_proto_list)
+		goto fail;
+
+	sdp_set_access_protos(rec, access_proto_list);
+	ret = 0;
+
+fail:
+	sdp_list_free(l2cap_list, NULL);
+	sdp_list_free(mcap_list, NULL);
+	sdp_list_free(proto_list, NULL);
+	sdp_list_free(access_proto_list, NULL);
+
+	if (psm)
+		sdp_data_free(psm);
+
+	if (mcap_ver)
+		sdp_data_free(mcap_ver);
+
+	if (err)
+		g_error_free(err);
+
+	return ret;
+}
+
+static int register_service_profiles(sdp_record_t *rec)
+{
+	int ret;
+	sdp_list_t *profile_list;
+	sdp_profile_desc_t hdp_profile;
+
+	DBG("");
+
+	/* set hdp information */
+	sdp_uuid16_create(&hdp_profile.uuid, HDP_SVCLASS_ID);
+	hdp_profile.version = HDP_VERSION;
+	profile_list = sdp_list_append(NULL, &hdp_profile);
+	if (!profile_list)
+		return -1;
+
+	/* set profile descriptor list */
+	ret = sdp_set_profile_descs(rec, profile_list);
+	sdp_list_free(profile_list, NULL);
+
+	return ret;
+}
+
+static int register_service_additional_protocols(sdp_record_t *rec,
+						struct health_app *app)
+{
+	int ret = -1;
+	uuid_t l2cap_uuid, mcap_d_uuid;
+	sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+	sdp_list_t *access_proto_list = NULL;
+	sdp_data_t *psm = NULL;
+	uint32_t dcpsm;
+	GError *err = NULL;
+
+	DBG("");
+
+	/* set l2cap information */
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+	if (!l2cap_list)
+		goto fail;
+
+	dcpsm = mcap_get_data_psm(mcap, &err);
+	if (err)
+		goto fail;
+
+	psm = sdp_data_alloc(SDP_UINT16, &dcpsm);
+	if (!psm)
+		goto fail;
+
+	if (!sdp_list_append(l2cap_list, psm))
+		goto fail;
+
+	proto_list = sdp_list_append(NULL, l2cap_list);
+	if (!proto_list)
+		goto fail;
+
+	/* set mcap information */
+	sdp_uuid16_create(&mcap_d_uuid, MCAP_DATA_UUID);
+	mcap_list = sdp_list_append(NULL, &mcap_d_uuid);
+	if (!mcap_list)
+		goto fail;
+
+	if (!sdp_list_append(proto_list, mcap_list))
+		goto fail;
+
+	/* attach protocol information to service record */
+	access_proto_list = sdp_list_append(NULL, proto_list);
+	if (!access_proto_list)
+		goto fail;
+
+	sdp_set_add_access_protos(rec, access_proto_list);
+	ret = 0;
+
+fail:
+	sdp_list_free(l2cap_list, NULL);
+	sdp_list_free(mcap_list, NULL);
+	sdp_list_free(proto_list, NULL);
+	sdp_list_free(access_proto_list, NULL);
+
+	if (psm)
+		sdp_data_free(psm);
+
+	if (err)
+		g_error_free(err);
+
+	return ret;
+}
+
+static sdp_list_t *mdeps_to_sdp_features(struct mdep_cfg *mdep)
+{
+	sdp_data_t *mdepid, *dtype = NULL, *role = NULL, *descr = NULL;
+	sdp_list_t *f_list = NULL;
+
+	DBG("");
+
+	mdepid = sdp_data_alloc(SDP_UINT8, &mdep->id);
+	if (!mdepid)
+		return NULL;
+
+	dtype = sdp_data_alloc(SDP_UINT16, &mdep->data_type);
+	if (!dtype)
+		goto fail;
+
+	role = sdp_data_alloc(SDP_UINT8, &mdep->role);
+	if (!role)
+		goto fail;
+
+	if (mdep->descr) {
+		descr = sdp_data_alloc(SDP_TEXT_STR8, mdep->descr);
+		if (!descr)
+			goto fail;
+	}
+
+	f_list = sdp_list_append(NULL, mdepid);
+	if (!f_list)
+		goto fail;
+
+	if (!sdp_list_append(f_list, dtype))
+		goto fail;
+
+	if (!sdp_list_append(f_list, role))
+		goto fail;
+
+	if (descr && !sdp_list_append(f_list, descr))
+		goto fail;
+
+	return f_list;
+
+fail:
+	sdp_list_free(f_list, NULL);
+
+	if (mdepid)
+		sdp_data_free(mdepid);
+
+	if (dtype)
+		sdp_data_free(dtype);
+
+	if (role)
+		sdp_data_free(role);
+
+	if (descr)
+		sdp_data_free(descr);
+
+	return NULL;
+}
+
+static void free_hdp_list(void *list)
+{
+	sdp_list_t *hdp_list = list;
+
+	sdp_list_free(hdp_list, (sdp_free_func_t)sdp_data_free);
+}
+
+static void register_features(void *data, void *user_data)
+{
+	struct mdep_cfg *mdep = data;
+	sdp_list_t **sup_features = user_data;
+	sdp_list_t *hdp_feature;
+
+	DBG("");
+
+	hdp_feature = mdeps_to_sdp_features(mdep);
+	if (!hdp_feature)
+		return;
+
+	if (!*sup_features) {
+		*sup_features = sdp_list_append(NULL, hdp_feature);
+		if (!*sup_features)
+			sdp_list_free(hdp_feature,
+					(sdp_free_func_t)sdp_data_free);
+	} else if (!sdp_list_append(*sup_features, hdp_feature)) {
+		sdp_list_free(hdp_feature,
+					(sdp_free_func_t)sdp_data_free);
+	}
+}
+
+static int register_service_sup_features(sdp_record_t *rec,
+						struct health_app *app)
+{
+	sdp_list_t *sup_features = NULL;
+
+	DBG("");
+
+	queue_foreach(app->mdeps, register_features, &sup_features);
+	if (!sup_features)
+		return -1;
+
+	if (sdp_set_supp_feat(rec, sup_features) < 0) {
+		sdp_list_free(sup_features, free_hdp_list);
+		return -1;
+	}
+
+	sdp_list_free(sup_features, free_hdp_list);
+	return 0;
+}
+
+static int register_data_exchange_spec(sdp_record_t *rec)
+{
+	sdp_data_t *spec;
+	uint8_t data_spec = DATA_EXCHANGE_SPEC_11073;
+	/* As of now only 11073 is supported, so we set it as default */
+
+	DBG("");
+
+	spec = sdp_data_alloc(SDP_UINT8, &data_spec);
+	if (!spec)
+		return -1;
+
+	if (sdp_attr_add(rec, SDP_ATTR_DATA_EXCHANGE_SPEC, spec) < 0) {
+		sdp_data_free(spec);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int register_mcap_features(sdp_record_t *rec)
+{
+	sdp_data_t *mcap_proc;
+	uint8_t mcap_sup_proc = MCAP_SUP_PROC;
+
+	DBG("");
+
+	mcap_proc = sdp_data_alloc(SDP_UINT8, &mcap_sup_proc);
+	if (!mcap_proc)
+		return -1;
+
+	if (sdp_attr_add(rec, SDP_ATTR_MCAP_SUPPORTED_PROCEDURES,
+							mcap_proc) < 0) {
+		sdp_data_free(mcap_proc);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int set_sdp_services_uuid(sdp_record_t *rec, uint8_t role)
+{
+	uuid_t source, sink;
+	sdp_list_t *list = NULL;
+
+	sdp_uuid16_create(&sink, HDP_SINK_SVCLASS_ID);
+	sdp_uuid16_create(&source, HDP_SOURCE_SVCLASS_ID);
+	sdp_get_service_classes(rec, &list);
+
+	switch (role) {
+	case HAL_HEALTH_MDEP_ROLE_SOURCE:
+		if (!sdp_list_find(list, &source, sdp_uuid_cmp))
+			list = sdp_list_append(list, &source);
+		break;
+	case HAL_HEALTH_MDEP_ROLE_SINK:
+		if (!sdp_list_find(list, &sink, sdp_uuid_cmp))
+			list = sdp_list_append(list, &sink);
+		break;
+	}
+
+	if (sdp_set_service_classes(rec, list) < 0) {
+		sdp_list_free(list, NULL);
+		return -1;
+	}
+
+	sdp_list_free(list, NULL);
+
+	return 0;
+}
+
+static int update_sdp_record(struct health_app *app)
+{
+	sdp_record_t *rec;
+	uint8_t role;
+
+	DBG("");
+
+	if (record_id > 0) {
+		bt_adapter_remove_record(record_id);
+		record_id = 0;
+	}
+
+	rec = sdp_record_alloc();
+	if (!rec)
+		return -1;
+
+	role = HAL_HEALTH_MDEP_ROLE_SOURCE;
+	if (queue_find(app->mdeps, match_mdep_by_role, INT_TO_PTR(role)))
+		set_sdp_services_uuid(rec, role);
+
+	role = HAL_HEALTH_MDEP_ROLE_SINK;
+	if (queue_find(app->mdeps, match_mdep_by_role, INT_TO_PTR(role)))
+		set_sdp_services_uuid(rec, role);
+
+	sdp_set_info_attr(rec, app->service_name, app->provider_name,
+							app->service_descr);
+
+	if (register_service_protocols(rec, app) < 0)
+		goto fail;
+
+	if (register_service_profiles(rec) < 0)
+		goto fail;
+
+	if (register_service_additional_protocols(rec, app) < 0)
+		goto fail;
+
+	if (register_service_sup_features(rec, app) < 0)
+		goto fail;
+
+	if (register_data_exchange_spec(rec) < 0)
+		goto fail;
+
+	if (register_mcap_features(rec) < 0)
+		goto fail;
+
+	if (sdp_set_record_state(rec, record_state++) < 0)
+		goto fail;
+
+	if (bt_adapter_add_record(rec, SVC_HINT_HEALTH) < 0) {
+		error("health: Failed to register HEALTH record");
+		goto fail;
+	}
+
+	record_id = rec->handle;
+
+	return 0;
+
+fail:
+	sdp_record_free(rec);
+
+	return -1;
+}
+
+static struct health_app *create_health_app(const char *app_name,
+				const char *provider, const char *srv_name,
+				const char *srv_descr, uint8_t mdeps)
+{
+	struct health_app *app;
+	static unsigned int app_id = 1;
+
+	DBG("");
+
+	app = new0(struct health_app, 1);
+	app->id = app_id++;
+	app->num_of_mdep = mdeps;
+	app->app_name = strdup(app_name);
+
+	if (provider) {
+		app->provider_name = strdup(provider);
+		if (!app->provider_name)
+			goto fail;
+	}
+
+	if (srv_name) {
+		app->service_name = strdup(srv_name);
+		if (!app->service_name)
+			goto fail;
+	}
+
+	if (srv_descr) {
+		app->service_descr = strdup(srv_descr);
+		if (!app->service_descr)
+			goto fail;
+	}
+
+	app->mdeps = queue_new();
+	app->devices = queue_new();
+
+	return app;
+
+fail:
+	free_health_app(app);
+	return NULL;
+}
+
+static void bt_health_register_app(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_health_reg_app *cmd = buf;
+	struct hal_rsp_health_reg_app rsp;
+	struct health_app *app;
+	uint16_t off;
+	uint16_t app_name_len, provider_len, srv_name_len, srv_descr_len;
+	char *app_name, *provider = NULL, *srv_name = NULL, *srv_descr = NULL;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + cmd->len ||
+			cmd->app_name_off > cmd->provider_name_off ||
+			cmd->provider_name_off > cmd->service_name_off ||
+			cmd->service_name_off > cmd->service_descr_off ||
+			cmd->service_descr_off > cmd->len) {
+		error("health: Invalid register app command, terminating");
+		raise(SIGTERM);
+		return;
+	}
+
+	app_name = (char *) cmd->data;
+	app_name_len = cmd->provider_name_off - cmd->app_name_off;
+
+	off = app_name_len;
+	provider_len = cmd->service_name_off - off;
+	if (provider_len > 0)
+		provider = (char *) cmd->data + off;
+
+	off += provider_len;
+	srv_name_len = cmd->service_descr_off - off;
+	if (srv_name_len > 0)
+		srv_name = (char *) cmd->data + off;
+
+	off += srv_name_len;
+	srv_descr_len = cmd->len - off;
+	if (srv_descr_len > 0)
+		srv_descr = (char *) cmd->data + off;
+
+	app = create_health_app(app_name, provider, srv_name, srv_descr,
+							cmd->num_of_mdep);
+	if (!app)
+		goto fail;
+
+	queue_push_tail(apps, app);
+
+	rsp.app_id = app->id;
+	ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP,
+							sizeof(rsp), &rsp, -1);
+	return;
+
+fail:
+	free_health_app(app);
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+							HAL_STATUS_FAILED);
+}
+
+static uint8_t android2channel_type(uint8_t type)
+{
+	switch (type) {
+	case HAL_HEALTH_CHANNEL_TYPE_RELIABLE:
+		return CHANNEL_TYPE_RELIABLE;
+	case HAL_HEALTH_CHANNEL_TYPE_STREAMING:
+		return CHANNEL_TYPE_STREAM;
+	default:
+		return CHANNEL_TYPE_ANY;
+	}
+}
+
+static void bt_health_mdep_cfg_data(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_health_mdep *cmd = buf;
+	struct health_app *app;
+	struct mdep_cfg *mdep = NULL;
+	uint8_t status;
+
+	DBG("");
+
+	app = queue_find(apps, match_app_by_id, INT_TO_PTR(cmd->app_id));
+	if (!app) {
+		status = HAL_STATUS_INVALID;
+		goto fail;
+	}
+
+	mdep = new0(struct mdep_cfg, 1);
+	mdep->role = cmd->role;
+	mdep->data_type = cmd->data_type;
+	mdep->channel_type = android2channel_type(cmd->channel_type);
+	mdep->id = queue_length(app->mdeps) + 1;
+
+	if (cmd->descr_len > 0) {
+		mdep->descr = malloc0(cmd->descr_len);
+		memcpy(mdep->descr, cmd->descr, cmd->descr_len);
+	}
+
+	queue_push_tail(app->mdeps, mdep);
+
+	if (app->num_of_mdep != queue_length(app->mdeps))
+		goto send_rsp;
+
+	/* add sdp record from app configuration data */
+	/*
+	 * TODO: Check what to be done if mupltple applications are trying to
+	 * register with different role and different configurations.
+	 * 1) Does device supports SOURCE and SINK at the same time ?
+	 * 2) Does it require different SDP records or one record with
+	 *    multile MDEP configurations ?
+	 */
+	if (update_sdp_record(app) < 0) {
+		error("health: HDP SDP record preparation failed");
+		status = HAL_STATUS_FAILED;
+		goto fail;
+	}
+
+	send_app_reg_notify(app, HAL_HEALTH_APP_REG_SUCCESS);
+
+send_rsp:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+							HAL_STATUS_SUCCESS);
+	return;
+
+fail:
+	if (status != HAL_STATUS_SUCCESS) {
+		free_mdep_cfg(mdep);
+		queue_remove(apps, app);
+		free_health_app(app);
+	}
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+								status);
+}
+
+static void bt_health_unregister_app(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_health_unreg_app *cmd = buf;
+	struct health_app *app;
+
+	DBG("");
+
+	app = queue_remove_if(apps, match_app_by_id, INT_TO_PTR(cmd->app_id));
+	if (!app) {
+		ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+				HAL_OP_HEALTH_UNREG_APP, HAL_STATUS_INVALID);
+		return;
+	}
+
+	send_app_reg_notify(app, HAL_HEALTH_APP_DEREG_SUCCESS);
+
+	if (record_id > 0) {
+		bt_adapter_remove_record(record_id);
+		record_id = 0;
+	}
+
+	free_health_app(app);
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+				HAL_OP_HEALTH_UNREG_APP, HAL_STATUS_SUCCESS);
+}
+
+static int get_prot_desc_entry(sdp_data_t *entry, int type, guint16 *val)
+{
+	sdp_data_t *iter;
+	int proto;
+
+	if (!entry || !SDP_IS_SEQ(entry->dtd))
+		return -1;
+
+	iter = entry->val.dataseq;
+	if (!(iter->dtd & SDP_UUID_UNSPEC))
+		return -1;
+
+	proto = sdp_uuid_to_proto(&iter->val.uuid);
+	if (proto != type)
+		return -1;
+
+	if (!val)
+		return 0;
+
+	iter = iter->next;
+	if (iter->dtd != SDP_UINT16)
+		return -1;
+
+	*val = iter->val.uint16;
+
+	return 0;
+}
+
+static int get_prot_desc_list(const sdp_record_t *rec, uint16_t *psm,
+							uint16_t *version)
+{
+	sdp_data_t *pdl, *p0, *p1;
+
+	if (!psm && !version)
+		return -1;
+
+	pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST);
+	if (!pdl || !SDP_IS_SEQ(pdl->dtd))
+		return -1;
+
+	p0 = pdl->val.dataseq;
+	if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0)
+		return -1;
+
+	p1 = p0->next;
+	if (get_prot_desc_entry(p1, MCAP_CTRL_UUID, version) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int get_ccpsm(sdp_list_t *recs, uint16_t *ccpsm)
+{
+	sdp_list_t *l;
+
+	for (l = recs; l; l = l->next) {
+		sdp_record_t *rec = l->data;
+
+		if (!get_prot_desc_list(rec, ccpsm, NULL))
+			return 0;
+	}
+
+	return -1;
+}
+
+static int get_add_prot_desc_list(const sdp_record_t *rec, uint16_t *psm)
+{
+	sdp_data_t *pdl, *p0, *p1;
+
+	if (!psm)
+		return -1;
+
+	pdl = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST);
+	if (!pdl || pdl->dtd != SDP_SEQ8)
+		return -1;
+
+	pdl = pdl->val.dataseq;
+	if (pdl->dtd != SDP_SEQ8)
+		return -1;
+
+	p0 = pdl->val.dataseq;
+
+	if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0)
+		return -1;
+
+	p1 = p0->next;
+	if (get_prot_desc_entry(p1, MCAP_DATA_UUID, NULL) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int get_dcpsm(sdp_list_t *recs, uint16_t *dcpsm)
+{
+	sdp_list_t *l;
+
+	for (l = recs; l; l = l->next) {
+		sdp_record_t *rec = l->data;
+
+		if (!get_add_prot_desc_list(rec, dcpsm))
+			return 0;
+	}
+
+	return -1;
+}
+
+static int send_echo_data(int sock, const void *buf, uint32_t size)
+{
+	const uint8_t *buf_b = buf;
+	uint32_t sent = 0;
+
+	while (sent < size) {
+		int n = write(sock, buf_b + sent, size - sent);
+		if (n < 0)
+			return -1;
+		sent += n;
+	}
+
+	return 0;
+}
+
+static gboolean serve_echo(GIOChannel *io, GIOCondition cond, gpointer data)
+{
+	struct health_channel *channel = data;
+	uint8_t buf[MCAP_DC_MTU];
+	int fd, len, ret;
+
+	DBG("channel %p", channel);
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+		DBG("Error condition on channel");
+		return FALSE;
+	}
+
+	fd = g_io_channel_unix_get_fd(io);
+
+	len = read(fd, buf, sizeof(buf));
+	if (len < 0) {
+		DBG("Error reading ECHO");
+		return FALSE;
+	}
+
+	ret = send_echo_data(fd, buf, len);
+	if (ret != len)
+		DBG("Error sending ECHO back");
+
+	return FALSE;
+}
+
+static void mcap_mdl_connected_cb(struct mcap_mdl *mdl, void *data)
+{
+	struct health_channel *channel = data;
+	int fd;
+
+	DBG("Data channel connected: mdl %p channel %p", mdl, channel);
+
+	if (!channel) {
+		channel = search_channel_by_mdl(mdl);
+		if (!channel) {
+			error("health: channel data does not exist");
+			return;
+		}
+	}
+
+	if (!channel->mdl)
+		channel->mdl = mcap_mdl_ref(mdl);
+
+	fd = mcap_mdl_get_fd(channel->mdl);
+	if (fd < 0) {
+		error("health: error retrieving fd");
+		goto fail;
+	}
+
+	if (channel->mdep_id == MDEP_ECHO) {
+		GIOChannel *io;
+
+		io = g_io_channel_unix_new(fd);
+		g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN,
+							serve_echo, channel);
+		g_io_channel_unref(io);
+
+		return;
+	}
+
+	info("health: MDL connected");
+	send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_CONNECTED, fd);
+
+	return;
+fail:
+	/* TODO: mcap_mdl_abort */
+	destroy_channel(channel);
+}
+
+static void mcap_mdl_closed_cb(struct mcap_mdl *mdl, void *data)
+{
+	struct health_channel *channel = data;
+
+	info("health: MDL closed");
+
+	if (!channel)
+		return;
+
+	channel->mdl_conn = false;
+}
+
+static void mcap_mdl_deleted_cb(struct mcap_mdl *mdl, void *data)
+{
+	struct health_channel *channel;
+
+	info("health: MDL deleted");
+
+	channel = search_channel_by_mdl(mdl);
+	if (!channel)
+		return;
+
+	DBG("channel %p mdl %p", channel, mdl);
+	destroy_channel(channel);
+}
+
+static void mcap_mdl_aborted_cb(struct mcap_mdl *mdl, void *data)
+{
+	DBG("Not Implemeneted");
+}
+
+static struct health_device *create_device(struct health_app *app,
+							const uint8_t *addr)
+{
+	struct health_device *dev;
+
+	/* create device and push it to devices queue */
+	dev = new0(struct health_device, 1);
+
+	android2bdaddr(addr, &dev->dst);
+	dev->channels = queue_new();
+	dev->app_id = app->id;
+
+	queue_push_tail(app->devices, dev);
+
+	return dev;
+}
+
+static struct health_device *get_device(struct health_app *app,
+							const uint8_t *addr)
+{
+	struct health_device *dev;
+	bdaddr_t bdaddr;
+
+	android2bdaddr(addr, &bdaddr);
+	dev = queue_find(app->devices, match_dev_by_addr, &bdaddr);
+	if (dev)
+		return dev;
+
+	return create_device(app, addr);
+}
+
+static struct health_channel *create_channel(struct health_app *app,
+						uint8_t mdep_index,
+						struct health_device *dev)
+{
+	struct mdep_cfg *mdep;
+	struct health_channel *channel;
+	static unsigned int channel_id = 1;
+
+	DBG("mdep %u", mdep_index);
+
+	if (!dev || !app)
+		return NULL;
+
+	mdep = queue_find(app->mdeps, match_mdep_by_id, INT_TO_PTR(mdep_index));
+	if (!mdep) {
+		if (mdep_index == MDEP_ECHO) {
+			mdep = new0(struct mdep_cfg, 1);
+
+			/* Leave other configuration zeroes */
+			mdep->id = MDEP_ECHO;
+
+			queue_push_tail(app->mdeps, mdep);
+		} else {
+			return NULL;
+		}
+	}
+
+	/* create channel and push it to device */
+	channel = new0(struct health_channel, 1);
+	channel->mdep_id = mdep->id;
+	channel->type = mdep->channel_type;
+	channel->id = channel_id++;
+	channel->dev = dev;
+
+	queue_push_tail(dev->channels, channel);
+
+	return channel;
+}
+
+static struct health_channel *connect_channel(struct health_app *app,
+							struct mcap_mcl *mcl,
+							uint8_t mdepid)
+{
+	struct health_device *device;
+	bdaddr_t addr;
+
+	DBG("app %p mdepid %u", app, mdepid);
+
+	mcap_mcl_get_addr(mcl, &addr);
+
+	if (!app) {
+		DBG("No app found for mdepid %u", mdepid);
+		return NULL;
+	}
+
+	device = get_device(app, (uint8_t *) &addr);
+
+	return create_channel(app, mdepid, device);
+}
+
+static uint8_t conf_to_l2cap(uint8_t conf)
+{
+	return conf == CHANNEL_TYPE_STREAM ? L2CAP_MODE_STREAMING :
+								L2CAP_MODE_ERTM;
+}
+
+static uint8_t mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
+				uint16_t mdlid, uint8_t *conf, void *data)
+{
+	GError *gerr = NULL;
+	struct health_channel *channel;
+	struct health_app *app;
+	struct mdep_cfg *mdep;
+
+	DBG("Data channel request: mdepid %u mdlid %u conf %u",
+							mdepid, mdlid, *conf);
+
+	if (mdepid == MDEP_ECHO)
+		/* For echo service take last app */
+		app = queue_peek_tail(apps);
+	else
+		app = search_app_by_mdepid(mdepid);
+
+	if (!app)
+		return MCAP_MDL_BUSY;
+
+	channel = connect_channel(app, mcl, mdepid);
+	if (!channel)
+		return MCAP_MDL_BUSY;
+
+	/* Channel is assigned here after creation */
+	mcl->cb->user_data = channel;
+
+	if (mdepid == MDEP_ECHO) {
+		switch (*conf) {
+		case CHANNEL_TYPE_ANY:
+			*conf = CHANNEL_TYPE_RELIABLE;
+			break;
+		case CHANNEL_TYPE_RELIABLE:
+			break;
+		case CHANNEL_TYPE_STREAM:
+			return MCAP_CONFIGURATION_REJECTED;
+		default:
+			/*
+			 * Special case defined in HDP spec 3.4.
+			 * When an invalid configuration is received we shall
+			 * close the MCL when we are still processing the
+			 * callback.
+			 */
+			/* TODO close device */
+			return MCAP_CONFIGURATION_REJECTED; /* not processed */
+		}
+
+		if (!mcap_set_data_chan_mode(mcap, L2CAP_MODE_ERTM, &gerr)) {
+			error("Error: %s", gerr->message);
+			g_error_free(gerr);
+			return MCAP_MDL_BUSY;
+		}
+
+		/* TODO: Create channel */
+
+		return MCAP_SUCCESS;
+	}
+
+	mdep = queue_find(app->mdeps, match_mdep_by_id, INT_TO_PTR(mdepid));
+	if (!mdep)
+		return MCAP_MDL_BUSY;
+
+	switch (*conf) {
+	case CHANNEL_TYPE_ANY:
+		if (mdep->role == HAL_HEALTH_MDEP_ROLE_SINK) {
+			return MCAP_CONFIGURATION_REJECTED;
+		} else {
+			if (queue_length(channel->dev->channels) <= 1)
+				*conf = CHANNEL_TYPE_RELIABLE;
+			else
+				*conf = CHANNEL_TYPE_STREAM;
+		}
+		break;
+	case CHANNEL_TYPE_STREAM:
+		if (mdep->role == HAL_HEALTH_MDEP_ROLE_SOURCE)
+			return MCAP_CONFIGURATION_REJECTED;
+		break;
+	case CHANNEL_TYPE_RELIABLE:
+		if (mdep->role == HAL_HEALTH_MDEP_ROLE_SOURCE)
+			return MCAP_CONFIGURATION_REJECTED;
+		break;
+	default:
+		/*
+		 * Special case defined in HDP spec 3.4. When an invalid
+		 * configuration is received we shall close the MCL when
+		 * we are still processing the callback.
+		 */
+		/* TODO: close device */
+		return MCAP_CONFIGURATION_REJECTED; /* not processed */
+	}
+
+	if (!mcap_set_data_chan_mode(mcap, conf_to_l2cap(*conf), &gerr)) {
+		error("health: error setting L2CAP mode: %s", gerr->message);
+		g_error_free(gerr);
+		return MCAP_MDL_BUSY;
+	}
+
+	return MCAP_SUCCESS;
+}
+
+static uint8_t mcap_mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data)
+{
+	struct health_channel *channel;
+	GError *err = NULL;
+
+	DBG("");
+
+	channel = search_channel_by_mdl(mdl);
+	if (!channel) {
+		error("health: channel data does not exist");
+		return MCAP_UNSPECIFIED_ERROR;
+	}
+
+	if (!mcap_set_data_chan_mode(mcap,
+			conf_to_l2cap(channel->type), &err)) {
+		error("health: %s", err->message);
+		g_error_free(err);
+		return MCAP_MDL_BUSY;
+	}
+
+	return MCAP_SUCCESS;
+}
+
+static void connect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data)
+{
+	struct health_channel *channel = data;
+	int fd;
+
+	DBG("");
+
+	if (gerr) {
+		error("health: error connecting to MDL %s", gerr->message);
+		goto fail;
+	}
+
+	fd = mcap_mdl_get_fd(channel->mdl);
+	if (fd < 0) {
+		error("health: error retrieving fd");
+		goto fail;
+	}
+
+	info("health: MDL connected");
+	channel->mdl_conn = true;
+
+	/* first data channel should be reliable data channel */
+	if (!queue_length(channel->dev->channels))
+		if (channel->type != CHANNEL_TYPE_RELIABLE)
+			goto fail;
+
+	send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_CONNECTED, fd);
+
+	return;
+
+fail:
+	/* TODO: mcap_mdl_abort */
+	destroy_channel(channel);
+}
+
+static void reconnect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data)
+{
+	struct health_channel *channel = data;
+	uint8_t mode;
+	GError *err = NULL;
+
+	DBG("");
+
+	if (gerr) {
+		error("health: error reconnecting to MDL %s", gerr->message);
+		goto fail;
+	}
+
+	channel->mdl_id = mcap_mdl_get_mdlid(mdl);
+
+	if (channel->type == CHANNEL_TYPE_RELIABLE)
+		mode = L2CAP_MODE_ERTM;
+	else
+		mode = L2CAP_MODE_STREAMING;
+
+	if (!mcap_connect_mdl(channel->mdl, mode, channel->dev->dcpsm,
+						connect_mdl_cb, channel,
+						NULL, &err)) {
+		error("health: error connecting to mdl");
+		g_error_free(err);
+		goto fail;
+	}
+
+	return;
+
+fail:
+	/* TODO: mcap_mdl_abort */
+	destroy_channel(channel);
+}
+
+static int reconnect_mdl(struct health_channel *channel)
+{
+	GError *gerr = NULL;
+
+	DBG("");
+
+	if (!channel)
+		return -1;
+
+	if (!mcap_reconnect_mdl(channel->mdl, reconnect_mdl_cb, channel,
+								NULL, &gerr)){
+		error("health: reconnect failed %s", gerr->message);
+		destroy_channel(channel);
+	}
+
+	return 0;
+}
+
+static void create_mdl_cb(struct mcap_mdl *mdl, uint8_t type, GError *gerr,
+								gpointer data)
+{
+	struct health_channel *channel = data;
+	uint8_t mode;
+	GError *err = NULL;
+
+	DBG("");
+	if (gerr) {
+		error("health: error creating MDL %s", gerr->message);
+		goto fail;
+	}
+
+	if (channel->type == CHANNEL_TYPE_ANY && type != CHANNEL_TYPE_ANY)
+		channel->type = type;
+
+	/*
+	 * if requested channel type is not same as preferred
+	 * channel type from remote device, then abort the connection.
+	 */
+	if (channel->type != type) {
+		/* TODO: abort mdl */
+		error("health: channel type requested %d preferred %d not same",
+							channel->type, type);
+		goto fail;
+	}
+
+	if (!channel->mdl)
+		channel->mdl = mcap_mdl_ref(mdl);
+
+	channel->type = type;
+	channel->mdl_id = mcap_mdl_get_mdlid(mdl);
+
+	if (channel->type == CHANNEL_TYPE_RELIABLE)
+		mode = L2CAP_MODE_ERTM;
+	else
+		mode = L2CAP_MODE_STREAMING;
+
+	if (!mcap_connect_mdl(channel->mdl, mode, channel->dev->dcpsm,
+						connect_mdl_cb, channel,
+						NULL, &err)) {
+		error("health: error connecting to mdl");
+		g_error_free(err);
+		goto fail;
+	}
+
+	return;
+
+fail:
+	destroy_channel(channel);
+}
+
+static bool check_role(uint8_t rec_role, uint8_t app_role)
+{
+	if ((rec_role == HAL_HEALTH_MDEP_ROLE_SINK &&
+			app_role == HAL_HEALTH_MDEP_ROLE_SOURCE) ||
+			(rec_role == HAL_HEALTH_MDEP_ROLE_SOURCE &&
+			app_role == HAL_HEALTH_MDEP_ROLE_SINK))
+		return true;
+
+	return false;
+}
+
+static bool get_mdep_from_rec(const sdp_record_t *rec, uint8_t role,
+						uint16_t d_type, uint8_t *mdep)
+{
+	sdp_data_t *list, *feat;
+
+	if (!mdep)
+		return false;
+
+	list = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST);
+	if (!list || !SDP_IS_SEQ(list->dtd))
+		return false;
+
+	for (feat = list->val.dataseq; feat; feat = feat->next) {
+		sdp_data_t *data_type, *mdepid, *role_t;
+
+		if (!SDP_IS_SEQ(feat->dtd))
+			continue;
+
+		mdepid = feat->val.dataseq;
+		if (!mdepid)
+			continue;
+
+		data_type = mdepid->next;
+		if (!data_type)
+			continue;
+
+		role_t = data_type->next;
+		if (!role_t)
+			continue;
+
+		if (data_type->dtd != SDP_UINT16 || mdepid->dtd != SDP_UINT8 ||
+						role_t->dtd != SDP_UINT8)
+			continue;
+
+		if (data_type->val.uint16 != d_type ||
+					!check_role(role_t->val.uint8, role))
+			continue;
+
+		*mdep = mdepid->val.uint8;
+
+		return true;
+	}
+
+	return false;
+}
+
+static bool get_remote_mdep(sdp_list_t *recs, struct health_channel *channel)
+{
+	struct health_app *app;
+	struct mdep_cfg *mdep;
+	uint8_t mdep_id;
+
+	app = queue_find(apps, match_app_by_id,
+					INT_TO_PTR(channel->dev->app_id));
+	if (!app)
+		return false;
+
+	mdep = queue_find(app->mdeps, match_mdep_by_id,
+						INT_TO_PTR(channel->mdep_id));
+	if (!mdep)
+		return false;
+
+	if (!get_mdep_from_rec(recs->data, mdep->role, mdep->data_type,
+								&mdep_id)) {
+		error("health: no matching MDEP: %u", channel->mdep_id);
+		return false;
+	}
+
+	channel->remote_mdep = mdep_id;
+	return true;
+}
+
+static bool create_mdl(struct health_channel *channel)
+{
+	struct health_app *app;
+	struct mdep_cfg *mdep;
+	uint8_t type;
+	GError *gerr = NULL;
+
+	app = queue_find(apps, match_app_by_id,
+					INT_TO_PTR(channel->dev->app_id));
+	if (!app)
+		return false;
+
+	mdep = queue_find(app->mdeps, match_mdep_by_id,
+						INT_TO_PTR(channel->mdep_id));
+	if (!mdep)
+		return false;
+
+	if (mdep->role == HAL_HEALTH_MDEP_ROLE_SOURCE)
+		type = channel->type;
+	else
+		type = CHANNEL_TYPE_ANY;
+
+	if (!mcap_create_mdl(channel->dev->mcl, channel->remote_mdep,
+				type, create_mdl_cb, channel, NULL, &gerr)) {
+		error("health: error creating mdl %s", gerr->message);
+		g_error_free(gerr);
+		return false;
+	}
+
+	return true;
+}
+
+static bool set_mcl_cb(struct mcap_mcl *mcl, gpointer user_data, GError **err)
+{
+	return mcap_mcl_set_cb(mcl, user_data, err,
+			MCAP_MDL_CB_CONNECTED, mcap_mdl_connected_cb,
+			MCAP_MDL_CB_CLOSED, mcap_mdl_closed_cb,
+			MCAP_MDL_CB_DELETED, mcap_mdl_deleted_cb,
+			MCAP_MDL_CB_ABORTED, mcap_mdl_aborted_cb,
+			MCAP_MDL_CB_REMOTE_CONN_REQ, mcap_mdl_conn_req_cb,
+			MCAP_MDL_CB_REMOTE_RECONN_REQ, mcap_mdl_reconn_req_cb,
+			MCAP_MDL_CB_INVALID);
+}
+
+static void create_mcl_cb(struct mcap_mcl *mcl, GError *err, gpointer data)
+{
+	struct health_channel *channel = data;
+	gboolean ret;
+	GError *gerr = NULL;
+
+	DBG("");
+
+	if (err) {
+		error("health: error creating MCL : %s", err->message);
+		goto fail;
+	}
+
+	if (!channel->dev->mcl)
+		channel->dev->mcl = mcap_mcl_ref(mcl);
+
+	info("health: MCL connected");
+
+	ret = set_mcl_cb(channel->dev->mcl, channel, &gerr);
+	if (!ret) {
+		error("health: error setting mdl callbacks: %s", gerr->message);
+		g_error_free(gerr);
+		goto fail;
+	}
+
+	if (!create_mdl(channel))
+		goto fail;
+
+	return;
+
+fail:
+	destroy_channel(channel);
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	struct health_channel *channel = data;
+	GError *gerr = NULL;
+
+	DBG("");
+
+	if (err < 0 || !recs) {
+		error("health: Error getting remote SDP records");
+		goto fail;
+	}
+
+	if (get_ccpsm(recs, &channel->dev->ccpsm) < 0) {
+		error("health: Can't get remote PSM for control channel");
+		goto fail;
+	}
+
+	if (get_dcpsm(recs, &channel->dev->dcpsm) < 0) {
+		error("health: Can't get remote PSM for data channel");
+		goto fail;
+	}
+
+	if (!get_remote_mdep(recs, channel)) {
+		error("health: Can't get remote MDEP data");
+		goto fail;
+	}
+
+	if (!mcap_create_mcl(mcap, &channel->dev->dst, channel->dev->ccpsm,
+					create_mcl_cb, channel, NULL, &gerr)) {
+		error("health: error creating mcl %s", gerr->message);
+		g_error_free(gerr);
+		goto fail;
+	}
+
+	return;
+
+fail:
+	destroy_channel(channel);
+}
+
+static int connect_mcl(struct health_channel *channel)
+{
+	uuid_t uuid;
+	int err;
+
+	DBG("");
+
+	bt_string2uuid(&uuid, HDP_UUID);
+
+	err = bt_search_service(&adapter_addr, &channel->dev->dst, &uuid,
+						search_cb, channel, NULL, 0);
+	if (!err)
+		send_channel_state_notify(channel,
+					HAL_HEALTH_CHANNEL_CONNECTING, -1);
+
+	return err;
+}
+
+static struct health_app *get_app(uint16_t app_id)
+{
+	return queue_find(apps, match_app_by_id, INT_TO_PTR(app_id));
+}
+
+static struct health_channel *get_channel(struct health_app *app,
+						uint8_t mdep_index,
+						struct health_device *dev)
+{
+	struct health_channel *channel;
+	uint8_t index;
+
+	if (!dev)
+		return NULL;
+
+	index = mdep_index + 1;
+	channel = queue_find(dev->channels, match_channel_by_mdep_id,
+							INT_TO_PTR(index));
+	if (channel)
+		return channel;
+
+	return create_channel(app, index, dev);
+}
+
+static void bt_health_connect_channel(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_health_connect_channel *cmd = buf;
+	struct hal_rsp_health_connect_channel rsp;
+	struct health_device *dev = NULL;
+	struct health_channel *channel = NULL;
+	struct health_app *app;
+
+	DBG("");
+
+	app = get_app(cmd->app_id);
+	if (!app)
+		goto send_rsp;
+
+	dev = get_device(app, cmd->bdaddr);
+
+	channel = get_channel(app, cmd->mdep_index, dev);
+	if (!channel)
+		goto send_rsp;
+
+	if (!queue_length(dev->channels)) {
+		if (channel->type != CHANNEL_TYPE_RELIABLE) {
+			error("health: first data shannel should be reliable");
+			goto fail;
+		}
+	}
+
+	if (!dev->mcl) {
+		if (connect_mcl(channel) < 0) {
+			error("health: error retrieving HDP SDP record");
+			goto fail;
+		}
+	} else {
+		/* data channel is already connected */
+		if (channel->mdl && channel->mdl_conn)
+			goto fail;
+
+		/* create mdl if it does not exists */
+		if (!channel->mdl && !create_mdl(channel))
+			goto fail;
+
+		/* reconnect mdl if it exists */
+		if (channel->mdl && !channel->mdl_conn) {
+			if (reconnect_mdl(channel) < 0)
+				goto fail;
+		}
+
+	}
+
+	rsp.channel_id = channel->id;
+	ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_HEALTH,
+				HAL_OP_HEALTH_CONNECT_CHANNEL,
+				sizeof(rsp), &rsp, -1);
+	return;
+
+fail:
+	queue_remove(channel->dev->channels, channel);
+	free_health_channel(channel);
+
+send_rsp:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+			HAL_OP_HEALTH_CONNECT_CHANNEL, HAL_STATUS_FAILED);
+}
+
+static void channel_delete_cb(GError *gerr, gpointer data)
+{
+	struct health_channel *channel = data;
+
+	DBG("");
+
+	if (gerr) {
+		error("health: channel delete failed %s", gerr->message);
+		return;
+	}
+
+	destroy_channel(channel);
+}
+
+static void bt_health_destroy_channel(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_health_destroy_channel *cmd = buf;
+	struct health_channel *channel;
+	GError *gerr = NULL;
+
+	DBG("");
+
+	channel = search_channel_by_id(cmd->channel_id);
+	if (!channel)
+		goto fail;
+
+	if (!mcap_delete_mdl(channel->mdl, channel_delete_cb, channel,
+							NULL, &gerr)) {
+		error("health: channel delete failed %s", gerr->message);
+		goto fail;
+	}
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+			HAL_OP_HEALTH_DESTROY_CHANNEL, HAL_STATUS_SUCCESS);
+
+	return;
+
+fail:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+			HAL_OP_HEALTH_DESTROY_CHANNEL, HAL_STATUS_INVALID);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_HEALTH_REG_APP */
+	{ bt_health_register_app, true,
+				sizeof(struct hal_cmd_health_reg_app) },
+	/* HAL_OP_HEALTH_MDEP */
+	{ bt_health_mdep_cfg_data, true,
+				sizeof(struct hal_cmd_health_mdep) },
+	/* HAL_OP_HEALTH_UNREG_APP */
+	{ bt_health_unregister_app, false,
+				sizeof(struct hal_cmd_health_unreg_app) },
+	/* HAL_OP_HEALTH_CONNECT_CHANNEL */
+	{ bt_health_connect_channel, false,
+				sizeof(struct hal_cmd_health_connect_channel) },
+	/* HAL_OP_HEALTH_DESTROY_CHANNEL */
+	{ bt_health_destroy_channel, false,
+				sizeof(struct hal_cmd_health_destroy_channel) },
+};
+
+static void mcl_connected(struct mcap_mcl *mcl, gpointer data)
+{
+	GError *gerr = NULL;
+	bool ret;
+
+	DBG("");
+
+	info("health: MCL connected");
+	ret = set_mcl_cb(mcl, NULL, &gerr);
+	if (!ret) {
+		error("health: error setting mcl callbacks: %s", gerr->message);
+		g_error_free(gerr);
+	}
+}
+
+static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
+{
+	struct health_device *dev;
+
+	DBG("");
+
+	info("health: MCL reconnected");
+	dev = search_dev_by_mcl(mcl);
+	if (!dev) {
+		error("device data does not exists");
+		return;
+	}
+}
+
+static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data)
+{
+	struct health_device *dev;
+
+	DBG("");
+
+	info("health: MCL disconnected");
+	dev = search_dev_by_mcl(mcl);
+	unref_mcl(dev);
+}
+
+static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
+{
+	/* mcap library maintains cache of mcls, not required here */
+}
+
+bool bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	GError *err = NULL;
+
+	DBG("");
+
+	bacpy(&adapter_addr, addr);
+
+	mcap = mcap_create_instance(&adapter_addr, BT_IO_SEC_MEDIUM, 0, 0,
+					mcl_connected, mcl_reconnected,
+					mcl_disconnected, mcl_uncached,
+					NULL, /* CSP is not used right now */
+					NULL, &err);
+	if (!mcap) {
+		error("health: MCAP instance creation failed %s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	hal_ipc = ipc;
+	apps = queue_new();
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_HEALTH, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+}
+
+void bt_health_unregister(void)
+{
+	DBG("");
+
+	mcap_instance_unref(mcap);
+	queue_destroy(apps, free_health_app);
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_HEALTH);
+	hal_ipc = NULL;
+}
diff --git a/repo/android/health.h b/repo/android/health.h
new file mode 100644
index 0000000..0b32fd3
--- /dev/null
+++ b/repo/android/health.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  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
+ *
+ */
+
+bool bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_health_unregister(void);
diff --git a/repo/android/hidhost.c b/repo/android/hidhost.c
new file mode 100644
index 0000000..591ca95
--- /dev/null
+++ b/repo/android/hidhost.c
@@ -0,0 +1,1595 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/util.h"
+#include "src/shared/uhid.h"
+#include "src/sdp-client.h"
+#include "src/uuid-helper.h"
+#include "src/log.h"
+#include "profiles/input/hog-lib.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "bluetooth.h"
+#include "gatt.h"
+#include "hidhost.h"
+#include "utils.h"
+
+#define L2CAP_PSM_HIDP_CTRL	0x11
+#define L2CAP_PSM_HIDP_INTR	0x13
+
+/* HID message types */
+#define HID_MSG_HANDSHAKE	0x00
+#define HID_MSG_CONTROL		0x10
+#define HID_MSG_GET_REPORT	0x40
+#define HID_MSG_SET_REPORT	0x50
+#define HID_MSG_GET_PROTOCOL	0x60
+#define HID_MSG_SET_PROTOCOL	0x70
+#define HID_MSG_DATA		0xa0
+
+#define HID_MSG_TYPE_MASK	0xf0
+
+/* HID data types */
+#define HID_DATA_TYPE_INPUT	0x01
+#define HID_DATA_TYPE_OUTPUT	0x02
+#define HID_DATA_TYPE_FEATURE	0x03
+
+/* HID protocol header parameters */
+#define HID_PROTO_BOOT		0x00
+#define HID_PROTO_REPORT	0x01
+
+/* HID GET REPORT Size Field */
+#define HID_GET_REPORT_SIZE_FIELD	0x08
+
+/* HID Virtual Cable Unplug */
+#define HID_VIRTUAL_CABLE_UNPLUG	0x05
+
+#define HOG_UUID		"00001812-0000-1000-8000-00805f9b34fb"
+
+static bdaddr_t adapter_addr;
+
+static GIOChannel *ctrl_io = NULL;
+static GIOChannel *intr_io = NULL;
+static GSList *devices = NULL;
+static unsigned int hog_app = 0;
+
+static struct ipc *hal_ipc = NULL;
+
+struct hid_device {
+	bdaddr_t	dst;
+	uint8_t		state;
+	uint8_t		subclass;
+	uint16_t	vendor;
+	uint16_t	product;
+	uint16_t	version;
+	uint8_t		country;
+	int		rd_size;
+	void		*rd_data;
+	uint8_t		boot_dev;
+	GIOChannel	*ctrl_io;
+	GIOChannel	*intr_io;
+	guint		ctrl_watch;
+	guint		intr_watch;
+	struct bt_uhid	*uhid;
+	uint8_t		last_hid_msg;
+	struct bt_hog	*hog;
+	int		sec_level;
+};
+
+static int device_cmp(gconstpointer s, gconstpointer user_data)
+{
+	const struct hid_device *dev = s;
+	const bdaddr_t *dst = user_data;
+
+	return bacmp(&dev->dst, dst);
+}
+
+static void hid_device_free(void *data)
+{
+	struct hid_device *dev = data;
+
+	if (dev->ctrl_watch > 0)
+		g_source_remove(dev->ctrl_watch);
+
+	if (dev->intr_watch > 0)
+		g_source_remove(dev->intr_watch);
+
+	if (dev->intr_io)
+		g_io_channel_unref(dev->intr_io);
+
+	if (dev->ctrl_io)
+		g_io_channel_unref(dev->ctrl_io);
+
+	if (dev->uhid)
+		bt_uhid_unref(dev->uhid);
+
+	if (dev->hog)
+		bt_hog_unref(dev->hog);
+
+	g_free(dev->rd_data);
+	g_free(dev);
+}
+
+static void hid_device_remove(struct hid_device *dev)
+{
+	devices = g_slist_remove(devices, dev);
+	hid_device_free(dev);
+}
+
+static struct hid_device *hid_device_new(const bdaddr_t *addr)
+{
+	struct hid_device *dev;
+
+	dev = g_new0(struct hid_device, 1);
+	bacpy(&dev->dst, addr);
+	dev->state = HAL_HIDHOST_STATE_DISCONNECTED;
+	dev->sec_level = BT_IO_SEC_LOW;
+
+	devices = g_slist_append(devices, dev);
+
+	return dev;
+}
+
+static bool hex2buf(const uint8_t *hex, uint8_t *buf, int buf_size)
+{
+	int i, j;
+	char c;
+	uint8_t b;
+
+	for (i = 0, j = 0; i < buf_size; i++, j++) {
+		c = toupper(hex[j]);
+
+		if (c >= '0' && c <= '9')
+			b = c - '0';
+		else if (c >= 'A' && c <= 'F')
+			b = 10 + c - 'A';
+		else
+			return false;
+
+		j++;
+
+		c = toupper(hex[j]);
+
+		if (c >= '0' && c <= '9')
+			b = b * 16 + c - '0';
+		else if (c >= 'A' && c <= 'F')
+			b = b * 16 + 10 + c - 'A';
+		else
+			return false;
+
+		buf[i] = b;
+	}
+
+	return true;
+}
+
+static void handle_uhid_output(struct uhid_event *event, void *user_data)
+{
+	struct uhid_output_req *output = &event->u.output;
+	struct hid_device *dev = user_data;
+	int fd, req_size;
+	uint8_t *req;
+
+	if (!dev->ctrl_io)
+		return;
+
+	req_size = 1 + output->size;
+	req = malloc0(req_size);
+	if (!req)
+		return;
+
+	req[0] = HID_MSG_SET_REPORT | output->rtype;
+	memcpy(req + 1, output->data, req_size - 1);
+
+	fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+	if (write(fd, req, req_size) < 0)
+		error("hidhost: error writing set_report: %s (%d)",
+							strerror(errno), errno);
+
+	free(req);
+}
+
+static gboolean intr_io_watch_cb(GIOChannel *chan, gpointer data)
+{
+	struct hid_device *dev = data;
+	uint8_t buf[UHID_DATA_MAX];
+	struct uhid_event ev;
+	int fd, bread, err;
+
+	/* Wait uHID if not ready */
+	if (!dev->uhid)
+		return TRUE;
+
+	fd = g_io_channel_unix_get_fd(chan);
+	bread = read(fd, buf, sizeof(buf));
+	if (bread < 0) {
+		error("hidhost: read from interrupt failed: %s(%d)",
+						strerror(errno), -errno);
+		return TRUE;
+	}
+
+	/* Discard non-data packets */
+	if (bread == 0 || buf[0] != (HID_MSG_DATA | HID_DATA_TYPE_INPUT))
+		return TRUE;
+
+	/* send data to uHID device skipping HIDP header byte */
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_INPUT;
+	ev.u.input.size = bread - 1;
+	memcpy(ev.u.input.data, &buf[1], ev.u.input.size);
+
+	err = bt_uhid_send(dev->uhid, &ev);
+	if (err < 0)
+		DBG("bt_uhid_send: %s (%d)", strerror(-err), -err);
+
+	return TRUE;
+}
+
+static void bt_hid_notify_state(struct hid_device *dev, uint8_t state)
+{
+	struct hal_ev_hidhost_conn_state ev;
+	char address[18];
+
+	if (dev->state == state)
+		return;
+
+	dev->state = state;
+
+	ba2str(&dev->dst, address);
+	DBG("device %s state %u", address, state);
+
+	bdaddr2android(&dev->dst, ev.bdaddr);
+	ev.state = state;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+				HAL_EV_HIDHOST_CONN_STATE, sizeof(ev), &ev);
+}
+
+static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond,
+								gpointer data)
+{
+	struct hid_device *dev = data;
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+		goto error;
+
+	if (cond & G_IO_IN)
+		return intr_io_watch_cb(chan, data);
+
+error:
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+
+	/*
+	 * Checking for ctrl_watch avoids a double g_io_channel_shutdown since
+	 * it's likely that ctrl_watch_cb has been queued for dispatching in
+	 * this mainloop iteration
+	 */
+	if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->ctrl_watch)
+		g_io_channel_shutdown(chan, TRUE, NULL);
+
+	/* Close control channel */
+	if (dev->ctrl_io && !(cond & G_IO_NVAL))
+		g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL);
+
+	hid_device_remove(dev);
+
+	return FALSE;
+}
+
+static void bt_hid_notify_proto_mode(struct hid_device *dev, uint8_t *buf,
+									int len)
+{
+	struct hal_ev_hidhost_proto_mode ev;
+	char address[18];
+
+	ba2str(&dev->dst, address);
+	DBG("device %s", address);
+
+	memset(&ev, 0, sizeof(ev));
+	bdaddr2android(&dev->dst, ev.bdaddr);
+
+	if (buf[0] == HID_MSG_DATA) {
+		ev.status = HAL_HIDHOST_STATUS_OK;
+		if (buf[1] == HID_PROTO_REPORT)
+			ev.mode = HAL_HIDHOST_REPORT_PROTOCOL;
+		else if (buf[1] == HID_PROTO_BOOT)
+			ev.mode = HAL_HIDHOST_BOOT_PROTOCOL;
+		else
+			ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL;
+
+	} else {
+		ev.status = buf[0];
+		ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL;
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+				HAL_EV_HIDHOST_PROTO_MODE, sizeof(ev), &ev);
+}
+
+static void bt_hid_notify_get_report(struct hid_device *dev, uint8_t *buf,
+									int len)
+{
+	struct hal_ev_hidhost_get_report *ev;
+	int ev_len;
+	char address[18];
+
+	ba2str(&dev->dst, address);
+	DBG("device %s", address);
+
+	ev_len = sizeof(*ev);
+
+	if (!((buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_INPUT)) ||
+			(buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_OUTPUT)) ||
+			(buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_FEATURE)))) {
+		ev = g_malloc0(ev_len);
+		ev->status = buf[0];
+		bdaddr2android(&dev->dst, ev->bdaddr);
+		goto send;
+	}
+
+	/*
+	 * Report porotocol mode reply contains id after hdr, in boot
+	 * protocol mode id doesn't exist
+	 */
+	ev_len += (dev->boot_dev) ? (len - 1) : (len - 2);
+	ev = g_malloc0(ev_len);
+	ev->status = HAL_HIDHOST_STATUS_OK;
+	bdaddr2android(&dev->dst, ev->bdaddr);
+
+	/*
+	 * Report porotocol mode reply contains id after hdr, in boot
+	 * protocol mode id doesn't exist
+	 */
+	if (dev->boot_dev) {
+		ev->len = len - 1;
+		memcpy(ev->data, buf + 1, ev->len);
+	} else {
+		ev->len = len - 2;
+		memcpy(ev->data, buf + 2, ev->len);
+	}
+
+send:
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+				HAL_EV_HIDHOST_GET_REPORT, ev_len, ev);
+	g_free(ev);
+}
+
+static void bt_hid_notify_handshake(struct hid_device *dev, uint8_t *buf,
+									int len)
+{
+	struct hal_ev_hidhost_handshake ev;
+
+	bdaddr2android(&dev->dst, ev.bdaddr);
+
+	/* crop result code to handshake status range from HAL */
+	ev.status = buf[0];
+	if (ev.status > HAL_HIDHOST_HS_ERROR)
+		ev.status = HAL_HIDHOST_HS_ERROR;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+				HAL_EV_HIDHOST_HANDSHAKE, sizeof(ev), &ev);
+}
+
+static void bt_hid_notify_virtual_unplug(struct hid_device *dev,
+							uint8_t *buf, int len)
+{
+	struct hal_ev_hidhost_virtual_unplug ev;
+	char address[18];
+
+	ba2str(&dev->dst, address);
+	DBG("device %s", address);
+	bdaddr2android(&dev->dst, ev.bdaddr);
+
+	ev.status = HAL_HIDHOST_GENERAL_ERROR;
+
+	/* Wait either channels to HUP */
+	if (dev->intr_io && dev->ctrl_io) {
+		g_io_channel_shutdown(dev->intr_io, TRUE, NULL);
+		g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL);
+		bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
+		ev.status = HAL_HIDHOST_STATUS_OK;
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+				HAL_EV_HIDHOST_VIRTUAL_UNPLUG, sizeof(ev), &ev);
+}
+
+static gboolean ctrl_io_watch_cb(GIOChannel *chan, gpointer data)
+{
+	struct hid_device *dev = data;
+	int fd, bread;
+	uint8_t buf[UHID_DATA_MAX];
+
+	DBG("");
+
+	fd = g_io_channel_unix_get_fd(chan);
+	bread = read(fd, buf, sizeof(buf));
+	if (bread < 0) {
+		error("hidhost: read from control failed: %s(%d)",
+						strerror(errno), -errno);
+		return TRUE;
+	}
+
+	switch (dev->last_hid_msg) {
+	case HID_MSG_GET_PROTOCOL:
+	case HID_MSG_SET_PROTOCOL:
+		bt_hid_notify_proto_mode(dev, buf, bread);
+		break;
+	case HID_MSG_GET_REPORT:
+		bt_hid_notify_get_report(dev, buf, bread);
+		break;
+	}
+
+	switch (buf[0] & HID_MSG_TYPE_MASK) {
+	case HID_MSG_HANDSHAKE:
+		bt_hid_notify_handshake(dev, buf, bread);
+		break;
+	case HID_MSG_CONTROL:
+		if ((buf[0] & ~HID_MSG_TYPE_MASK) == HID_VIRTUAL_CABLE_UNPLUG)
+			bt_hid_notify_virtual_unplug(dev, buf, bread);
+		break;
+	default:
+		break;
+	}
+
+	/* reset msg type request */
+	dev->last_hid_msg = 0;
+
+	return TRUE;
+}
+
+static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond,
+								gpointer data)
+{
+	struct hid_device *dev = data;
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+		goto error;
+
+	if (cond & G_IO_IN)
+		return ctrl_io_watch_cb(chan, data);
+
+error:
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+
+	/*
+	 * Checking for intr_watch avoids a double g_io_channel_shutdown since
+	 * it's likely that intr_watch_cb has been queued for dispatching in
+	 * this mainloop iteration
+	 */
+	if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->intr_watch)
+		g_io_channel_shutdown(chan, TRUE, NULL);
+
+	if (dev->intr_io && !(cond & G_IO_NVAL))
+		g_io_channel_shutdown(dev->intr_io, TRUE, NULL);
+
+	hid_device_remove(dev);
+
+	return FALSE;
+}
+
+static void bt_hid_set_info(struct hid_device *dev)
+{
+	struct hal_ev_hidhost_info ev;
+
+	DBG("");
+
+	bdaddr2android(&dev->dst, ev.bdaddr);
+	ev.attr = 0; /* TODO: Check what is this field */
+	ev.subclass = dev->subclass;
+	ev.app_id = 0; /* TODO: Check what is this field */
+	ev.vendor = dev->vendor;
+	ev.product = dev->product;
+	ev.version = dev->version;
+	ev.country = dev->country;
+	ev.descr_len = dev->rd_size;
+	memset(ev.descr, 0, sizeof(ev.descr));
+	memcpy(ev.descr, dev->rd_data, ev.descr_len);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_INFO,
+							sizeof(ev), &ev);
+}
+
+static int uhid_create(struct hid_device *dev)
+{
+	struct uhid_event ev;
+	int err;
+
+	dev->uhid = bt_uhid_new_default();
+	if (!dev->uhid) {
+		err = -errno;
+		error("hidhost: Failed to create bt_uhid instance");
+		return err;
+	}
+
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_CREATE;
+	strcpy((char *) ev.u.create.name, "bluez-input-device");
+	ev.u.create.bus = BUS_BLUETOOTH;
+	ev.u.create.vendor = dev->vendor;
+	ev.u.create.product = dev->product;
+	ev.u.create.version = dev->version;
+	ev.u.create.country = dev->country;
+	ev.u.create.rd_size = dev->rd_size;
+	ev.u.create.rd_data = dev->rd_data;
+
+	err = bt_uhid_send(dev->uhid, &ev);
+	if (err < 0) {
+		error("hidhost: Failed to create uHID device: %s",
+							strerror(-err));
+		bt_uhid_unref(dev->uhid);
+		dev->uhid = NULL;
+		return err;
+	}
+
+	bt_uhid_register(dev->uhid, UHID_OUTPUT, handle_uhid_output, dev);
+	bt_hid_set_info(dev);
+
+	return 0;
+}
+
+static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err,
+							gpointer user_data)
+{
+	struct hid_device *dev = user_data;
+	uint8_t state;
+
+	DBG("");
+
+	if (conn_err) {
+		error("hidhost: Failed to connect interrupt channel (%s)",
+							conn_err->message);
+		state = HAL_HIDHOST_STATE_FAILED;
+		goto failed;
+	}
+
+	if (uhid_create(dev) < 0) {
+		state = HAL_HIDHOST_STATE_NO_HID;
+		goto failed;
+	}
+
+	dev->intr_watch = g_io_add_watch(dev->intr_io,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				intr_watch_cb, dev);
+
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED);
+
+	return;
+
+failed:
+	bt_hid_notify_state(dev, state);
+	hid_device_remove(dev);
+}
+
+static void control_connect_cb(GIOChannel *chan, GError *conn_err,
+							gpointer user_data)
+{
+	struct hid_device *dev = user_data;
+	GError *err = NULL;
+
+	DBG("");
+
+	if (conn_err) {
+		bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+		error("hidhost: Failed to connect control channel (%s)",
+							conn_err->message);
+		goto failed;
+	}
+
+	/* Connect to the HID interrupt channel */
+	dev->intr_io = bt_io_connect(interrupt_connect_cb, dev, NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_DEST_BDADDR, &dev->dst,
+					BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
+					BT_IO_OPT_SEC_LEVEL, dev->sec_level,
+					BT_IO_OPT_INVALID);
+	if (!dev->intr_io) {
+		error("hidhost: Failed to connect interrupt channel (%s)",
+								err->message);
+		g_error_free(err);
+		goto failed;
+	}
+
+	dev->ctrl_watch = g_io_add_watch(dev->ctrl_io,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				ctrl_watch_cb, dev);
+
+	return;
+
+failed:
+	hid_device_remove(dev);
+}
+
+static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	struct hid_device *dev = data;
+	sdp_list_t *list;
+	GError *gerr = NULL;
+
+	DBG("");
+
+	if (err < 0) {
+		error("hidhost: Unable to get SDP record: %s", strerror(-err));
+		goto fail;
+	}
+
+	if (!recs || !recs->data) {
+		error("hidhost: No SDP records found");
+		goto fail;
+	}
+
+	for (list = recs; list != NULL; list = list->next) {
+		sdp_record_t *rec = list->data;
+		sdp_data_t *data;
+
+		data = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE);
+		if (data)
+			dev->country = data->val.uint8;
+
+		data = sdp_data_get(rec, SDP_ATTR_HID_DEVICE_SUBCLASS);
+		if (data) {
+			dev->subclass = data->val.uint8;
+
+			/* Encryption is mandatory for keyboards */
+			if (dev->subclass & 0x40)
+				dev->sec_level = BT_IO_SEC_MEDIUM;
+		}
+
+		data = sdp_data_get(rec, SDP_ATTR_HID_BOOT_DEVICE);
+		if (data)
+			dev->boot_dev = data->val.uint8;
+
+		data = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST);
+		if (data) {
+			if (!SDP_IS_SEQ(data->dtd))
+				goto fail;
+
+			/* First HIDDescriptor */
+			data = data->val.dataseq;
+			if (!SDP_IS_SEQ(data->dtd))
+				goto fail;
+
+			/* ClassDescriptorType */
+			data = data->val.dataseq;
+			if (data->dtd != SDP_UINT8)
+				goto fail;
+
+			/* ClassDescriptorData */
+			data = data->next;
+			if (!data || !SDP_IS_TEXT_STR(data->dtd))
+				goto fail;
+
+			dev->rd_size = data->unitSize;
+			dev->rd_data = g_memdup(data->val.str, data->unitSize);
+		}
+	}
+
+	if (dev->ctrl_io) {
+		/* Raise the security level for this device if needed. */
+		if ((dev->sec_level > BT_IO_SEC_LOW) &&
+			!bt_io_set(dev->ctrl_io, &gerr,
+					BT_IO_OPT_SEC_LEVEL, dev->sec_level,
+					BT_IO_OPT_INVALID)) {
+			error("hidhost: Cannot raise security level: %s",
+								gerr->message);
+			g_error_free(gerr);
+
+			goto fail;
+		}
+
+		if (uhid_create(dev) < 0)
+			goto fail;
+		return;
+	}
+
+	dev->ctrl_io = bt_io_connect(control_connect_cb, dev, NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_DEST_BDADDR, &dev->dst,
+					BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
+					BT_IO_OPT_SEC_LEVEL, dev->sec_level,
+					BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("hidhost: Failed to connect control channel (%s)",
+								gerr->message);
+		g_error_free(gerr);
+		goto fail;
+	}
+
+	return;
+
+fail:
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+	hid_device_remove(dev);
+}
+
+static void hid_sdp_did_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	struct hid_device *dev = data;
+	sdp_list_t *list;
+	uuid_t uuid;
+
+	DBG("");
+
+	if (err < 0) {
+		error("hidhost: Unable to get Device ID SDP record: %s",
+								strerror(-err));
+		goto fail;
+	}
+
+	if (!recs || !recs->data) {
+		error("hidhost: No Device ID SDP records found");
+		goto fail;
+	}
+
+	for (list = recs; list; list = list->next) {
+		sdp_record_t *rec = list->data;
+		sdp_data_t *data;
+
+		data = sdp_data_get(rec, SDP_ATTR_VENDOR_ID);
+		if (data)
+			dev->vendor = data->val.uint16;
+
+		data = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID);
+		if (data)
+			dev->product = data->val.uint16;
+
+		data = sdp_data_get(rec, SDP_ATTR_VERSION);
+		if (data)
+			dev->version = data->val.uint16;
+	}
+
+	sdp_uuid16_create(&uuid, HID_SVCLASS_ID);
+	if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
+				hid_sdp_search_cb, dev, NULL, 0) < 0) {
+		error("hidhost: Failed to search SDP details");
+		goto fail;
+	}
+
+	return;
+
+fail:
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+	hid_device_remove(dev);
+}
+
+static void hog_conn_cb(const bdaddr_t *addr, int err, void *attrib)
+{
+	GSList *l;
+	struct hid_device *dev;
+
+	l = g_slist_find_custom(devices, addr, device_cmp);
+	dev = l ? l->data : NULL;
+
+	if (err < 0) {
+		if (!dev)
+			return;
+		if (dev->hog) {
+			bt_hid_notify_state(dev,
+						HAL_HIDHOST_STATE_DISCONNECTED);
+			bt_hog_detach(dev->hog);
+			return;
+		}
+		goto fail;
+	}
+
+	if (!dev)
+		dev = hid_device_new(addr);
+
+	if (!dev->hog) {
+		/* TODO: Get device details and primary */
+		dev->hog = bt_hog_new_default("bluez-input-device", dev->vendor,
+					dev->product, dev->version, NULL);
+		if (!dev->hog) {
+			error("HoG: unable to create session");
+			goto fail;
+		}
+	}
+
+	if (!bt_hog_attach(dev->hog, attrib)) {
+		error("HoG: unable to attach");
+		goto fail;
+	}
+
+	if (!bt_gatt_set_security(addr, BT_IO_SEC_MEDIUM)) {
+		error("Failed to set security level");
+		goto fail;
+	}
+
+	DBG("");
+
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED);
+
+	if (!bt_gatt_add_autoconnect(hog_app, &dev->dst))
+		error("hidhost: Could not add to autoconnect list");
+
+	return;
+
+fail:
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+	hid_device_remove(dev);
+}
+
+static bool hog_connect(struct hid_device *dev)
+{
+	DBG("");
+
+	if (hog_app)
+		return bt_gatt_connect_app(hog_app, &dev->dst);
+
+	hog_app = bt_gatt_register_app(HOG_UUID, GATT_CLIENT, hog_conn_cb);
+	if (!hog_app) {
+		error("hidhost: bt_gatt_register_app failed");
+		return false;
+	}
+
+	return bt_gatt_connect_app(hog_app, &dev->dst);
+}
+
+static void bt_hid_connect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_connect *cmd = buf;
+	struct hid_device *dev;
+	uint8_t status;
+	char addr[18];
+	bdaddr_t dst;
+	GSList *l;
+	uuid_t uuid;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (l)
+		dev = l->data;
+	else
+		dev = hid_device_new(&dst);
+
+	if (dev->state != HAL_HIDHOST_STATE_DISCONNECTED)
+		goto done;
+
+	ba2str(&dev->dst, addr);
+	DBG("connecting to %s", addr);
+
+	if (bt_device_last_seen_bearer(&dev->dst) != BDADDR_BREDR) {
+		if (!hog_connect(dev)) {
+			status = HAL_STATUS_FAILED;
+			hid_device_remove(dev);
+			goto failed;
+		}
+		goto done;
+	}
+
+	sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID);
+	if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
+				hid_sdp_did_search_cb, dev, NULL, 0) < 0) {
+		error("hidhost: Failed to search DeviceID SDP details");
+		hid_device_remove(dev);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+done:
+	if (dev->state == HAL_HIDHOST_STATE_DISCONNECTED)
+		bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT,
+									status);
+}
+
+static bool hog_disconnect(struct hid_device *dev)
+{
+	DBG("");
+
+	if (dev->state == HAL_HIDHOST_STATE_DISCONNECTED)
+		return false;
+
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
+
+	if (!bt_gatt_disconnect_app(hog_app, &dev->dst)) {
+		bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+		hid_device_remove(dev);
+	}
+
+	return true;
+}
+
+static void bt_hid_disconnect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_disconnect *cmd = buf;
+	struct hid_device *dev;
+	uint8_t status;
+	GSList *l;
+	bdaddr_t dst;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+	if (bt_is_device_le(&dst)) {
+		if (!hog_disconnect(dev)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+		goto done;
+	}
+
+	/* Wait either channels to HUP */
+	if (dev->intr_io)
+		g_io_channel_shutdown(dev->intr_io, TRUE, NULL);
+
+	if (dev->ctrl_io)
+		g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL);
+
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
+
+
+done:
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT,
+									status);
+}
+
+static bool bt_hid_write_virtual_unplug(GIOChannel *chan)
+{
+	uint8_t hdr = HID_MSG_CONTROL | HID_VIRTUAL_CABLE_UNPLUG;
+	int fd = g_io_channel_unix_get_fd(chan);
+
+	if (write(fd, &hdr, sizeof(hdr)) == sizeof(hdr))
+		return true;
+
+	error("hidhost: Error writing virtual unplug command: %s (%d)",
+							strerror(errno), errno);
+	return false;
+}
+
+static void bt_hid_virtual_unplug(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_virtual_unplug *cmd = buf;
+	struct hid_device *dev;
+	GSList *l;
+	uint8_t status;
+	bdaddr_t dst;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+
+	if (!(dev->ctrl_io)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (!bt_hid_write_virtual_unplug(dev->ctrl_io)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	/* Wait either channels to HUP */
+	if (dev->intr_io)
+		g_io_channel_shutdown(dev->intr_io, TRUE, NULL);
+
+	if (dev->ctrl_io)
+		g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL);
+
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+					HAL_OP_HIDHOST_VIRTUAL_UNPLUG, status);
+}
+
+static void bt_hid_info(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_set_info *cmd = buf;
+
+	if (len != sizeof(*cmd) + cmd->descr_len) {
+		error("Invalid hid set info size (%u bytes), terminating", len);
+		raise(SIGTERM);
+		return;
+	}
+
+	/*
+	 * Data from hal_cmd_hidhost_set_info is usefull only when we create
+	 * UHID device. Once device is created all the transactions will be
+	 * done through the fd. There is no way to use this information
+	 * once device is created with HID internals.
+	 */
+	DBG("Not supported");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO,
+							HAL_STATUS_UNSUPPORTED);
+}
+
+static void bt_hid_get_protocol(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_get_protocol *cmd = buf;
+	struct hid_device *dev;
+	GSList *l;
+	bdaddr_t dst;
+	int fd;
+	uint8_t hdr;
+	uint8_t status;
+
+	DBG("");
+
+	switch (cmd->mode) {
+	case HAL_HIDHOST_REPORT_PROTOCOL:
+	case HAL_HIDHOST_BOOT_PROTOCOL:
+		break;
+	default:
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+
+	hdr = HID_MSG_GET_PROTOCOL | cmd->mode;
+	fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+	if (write(fd, &hdr, sizeof(hdr)) < 0) {
+		error("hidhost: Error writing device_get_protocol: %s (%d)",
+						strerror(errno), errno);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev->last_hid_msg = HID_MSG_GET_PROTOCOL;
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+					HAL_OP_HIDHOST_GET_PROTOCOL, status);
+}
+
+static void bt_hid_set_protocol(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_set_protocol *cmd = buf;
+	struct hid_device *dev;
+	GSList *l;
+	bdaddr_t dst;
+	int fd;
+	uint8_t hdr;
+	uint8_t status;
+
+	DBG("");
+
+	switch (cmd->mode) {
+	case HAL_HIDHOST_REPORT_PROTOCOL:
+	case HAL_HIDHOST_BOOT_PROTOCOL:
+		break;
+	default:
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+
+	hdr = HID_MSG_SET_PROTOCOL | cmd->mode;
+	fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+	if (write(fd, &hdr, sizeof(hdr)) < 0) {
+		error("hidhost: error writing device_set_protocol: %s (%d)",
+						strerror(errno), errno);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev->last_hid_msg = HID_MSG_SET_PROTOCOL;
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+					HAL_OP_HIDHOST_SET_PROTOCOL, status);
+}
+
+static void bt_hid_get_report(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_get_report *cmd = buf;
+	struct hid_device *dev;
+	GSList *l;
+	bdaddr_t dst;
+	int fd;
+	uint8_t *req;
+	uint8_t req_size;
+	uint8_t status;
+
+	DBG("");
+
+	switch (cmd->type) {
+	case HAL_HIDHOST_INPUT_REPORT:
+	case HAL_HIDHOST_OUTPUT_REPORT:
+	case HAL_HIDHOST_FEATURE_REPORT:
+		break;
+	default:
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+	req_size = (cmd->buf_size > 0) ? 4 : 2;
+	req = g_try_malloc0(req_size);
+	if (!req) {
+		status = HAL_STATUS_NOMEM;
+		goto failed;
+	}
+
+	req[0] = HID_MSG_GET_REPORT | cmd->type;
+	req[1] = cmd->id;
+
+	if (cmd->buf_size > 0) {
+		req[0] = req[0] | HID_GET_REPORT_SIZE_FIELD;
+		put_le16(cmd->buf_size, &req[2]);
+	}
+
+	fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+	if (write(fd, req, req_size) < 0) {
+		error("hidhost: error writing hid_get_report: %s (%d)",
+						strerror(errno), errno);
+		g_free(req);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev->last_hid_msg = HID_MSG_GET_REPORT;
+	g_free(req);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT,
+									status);
+}
+
+static void bt_hid_set_report(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_set_report *cmd = buf;
+	struct hid_device *dev;
+	GSList *l;
+	bdaddr_t dst;
+	int fd;
+	uint8_t *req = NULL;
+	uint8_t req_size;
+	uint8_t status;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid hid set report size (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return;
+	}
+
+	switch (cmd->type) {
+	case HAL_HIDHOST_INPUT_REPORT:
+	case HAL_HIDHOST_OUTPUT_REPORT:
+	case HAL_HIDHOST_FEATURE_REPORT:
+		break;
+	default:
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+
+	if (!dev->ctrl_io && !dev->hog) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	req_size = 1 + (cmd->len / 2);
+	req = g_try_malloc0(req_size);
+	if (!req) {
+		status = HAL_STATUS_NOMEM;
+		goto failed;
+	}
+
+	req[0] = HID_MSG_SET_REPORT | cmd->type;
+	/*
+	 * Report data coming to HAL is in ascii format, HAL sends
+	 * data in hex to daemon, so convert to binary.
+	 */
+	if (!hex2buf(cmd->data, req + 1, req_size - 1)) {
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	if (dev->hog) {
+		if (bt_hog_send_report(dev->hog, req + 1, req_size - 1,
+							cmd->type) < 0) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		goto done;
+	}
+
+	fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+	if (write(fd, req, req_size) < 0) {
+		error("hidhost: error writing hid_set_report: %s (%d)",
+						strerror(errno), errno);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev->last_hid_msg = HID_MSG_SET_REPORT;
+
+done:
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	g_free(req);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT,
+									status);
+}
+
+static void bt_hid_send_data(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_send_data *cmd = buf;
+	struct hid_device *dev;
+	GSList *l;
+	bdaddr_t dst;
+	int fd;
+	uint8_t *req = NULL;
+	uint8_t req_size;
+	uint8_t status;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid hid send data size (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return;
+	}
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+
+	if (!(dev->intr_io)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	req_size = 1 + (cmd->len / 2);
+	req = g_try_malloc0(req_size);
+	if (!req) {
+		status = HAL_STATUS_NOMEM;
+		goto failed;
+	}
+
+	req[0] = HID_MSG_DATA | HID_DATA_TYPE_OUTPUT;
+	/*
+	 * Report data coming to HAL is in ascii format, HAL sends
+	 * data in hex to daemon, so convert to binary.
+	 */
+	if (!hex2buf(cmd->data, req + 1, req_size - 1)) {
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	fd = g_io_channel_unix_get_fd(dev->intr_io);
+
+	if (write(fd, req, req_size) < 0) {
+		error("hidhost: error writing data to HID device: %s (%d)",
+						strerror(errno), errno);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	g_free(req);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA,
+									status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_HIDHOST_CONNECT */
+	{ bt_hid_connect, false, sizeof(struct hal_cmd_hidhost_connect) },
+	/* HAL_OP_HIDHOST_DISCONNECT */
+	{ bt_hid_disconnect, false, sizeof(struct hal_cmd_hidhost_disconnect) },
+	/* HAL_OP_HIDHOST_VIRTUAL_UNPLUG */
+	{ bt_hid_virtual_unplug, false,
+				sizeof(struct hal_cmd_hidhost_virtual_unplug) },
+	/* HAL_OP_HIDHOST_SET_INFO */
+	{ bt_hid_info, true, sizeof(struct hal_cmd_hidhost_set_info) },
+	/* HAL_OP_HIDHOST_GET_PROTOCOL */
+	{ bt_hid_get_protocol, false,
+				sizeof(struct hal_cmd_hidhost_get_protocol) },
+	/* HAL_OP_HIDHOST_SET_PROTOCOL */
+	{ bt_hid_set_protocol, false,
+				sizeof(struct hal_cmd_hidhost_get_protocol) },
+	/* HAL_OP_HIDHOST_GET_REPORT */
+	{ bt_hid_get_report, false, sizeof(struct hal_cmd_hidhost_get_report) },
+	/* HAL_OP_HIDHOST_SET_REPORT */
+	{ bt_hid_set_report, true, sizeof(struct hal_cmd_hidhost_set_report) },
+	/* HAL_OP_HIDHOST_SEND_DATA */
+	{ bt_hid_send_data, true, sizeof(struct hal_cmd_hidhost_send_data)  },
+};
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct hid_device *dev;
+	bdaddr_t dst;
+	char address[18];
+	uint16_t psm;
+	GError *gerr = NULL;
+	GSList *l;
+	uuid_t uuid;
+
+	if (err) {
+		error("hidhost: Connect failed (%s)", err->message);
+		return;
+	}
+
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_PSM, &psm,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("hidhost: Failed to read remote address (%s)",
+								gerr->message);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		g_error_free(gerr);
+		return;
+	}
+
+	ba2str(&dst, address);
+	DBG("Incoming connection from %s on PSM %d", address, psm);
+
+	if (!bt_device_is_bonded(&dst)) {
+		warn("hidhost: Rejecting connection from unknown device %s",
+								address);
+		if (psm == L2CAP_PSM_HIDP_CTRL)
+			bt_hid_write_virtual_unplug(chan);
+
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	switch (psm) {
+	case L2CAP_PSM_HIDP_CTRL:
+		l = g_slist_find_custom(devices, &dst, device_cmp);
+		if (l)
+			return;
+
+		dev = hid_device_new(&dst);
+		dev->ctrl_io = g_io_channel_ref(chan);
+
+		sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID);
+		if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
+				hid_sdp_did_search_cb, dev, NULL, 0) < 0) {
+			error("hidhost: Failed to search DID SDP details");
+			hid_device_remove(dev);
+			return;
+		}
+
+		dev->ctrl_watch = g_io_add_watch(dev->ctrl_io,
+					G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+					ctrl_watch_cb, dev);
+		bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING);
+		break;
+
+	case L2CAP_PSM_HIDP_INTR:
+		l = g_slist_find_custom(devices, &dst, device_cmp);
+		if (!l)
+			return;
+
+		dev = l->data;
+		dev->intr_io = g_io_channel_ref(chan);
+		dev->intr_watch = g_io_add_watch(dev->intr_io,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				intr_watch_cb, dev);
+		bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED);
+		break;
+	}
+}
+
+static void hid_unpaired_cb(const bdaddr_t *addr)
+{
+	GSList *l;
+	struct hid_device *dev;
+	char address[18];
+
+	l = g_slist_find_custom(devices, addr, device_cmp);
+	if (!l)
+		return;
+
+	dev = l->data;
+
+	ba2str(addr, address);
+	DBG("Unpaired device %s", address);
+
+	if (hog_app)
+		bt_gatt_remove_autoconnect(hog_app, addr);
+
+	hid_device_remove(dev);
+}
+
+bool bt_hid_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	GError *err = NULL;
+
+	DBG("");
+
+	if (!bt_unpaired_register(hid_unpaired_cb)) {
+		error("hidhost: Could not register unpaired callback");
+		return false;
+	}
+
+	bacpy(&adapter_addr, addr);
+
+	ctrl_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_INVALID);
+	if (!ctrl_io) {
+		error("hidhost: Failed to listen on control channel: %s",
+								err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	intr_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_INVALID);
+	if (!intr_io) {
+		error("hidhost: Failed to listen on interrupt channel: %s",
+								err->message);
+		g_error_free(err);
+
+		g_io_channel_shutdown(ctrl_io, TRUE, NULL);
+		g_io_channel_unref(ctrl_io);
+		ctrl_io = NULL;
+
+		return false;
+	}
+
+	hal_ipc = ipc;
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_HIDHOST, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+}
+
+void bt_hid_unregister(void)
+{
+	DBG("");
+
+	if (hog_app > 0)
+		bt_gatt_unregister_app(hog_app);
+
+	g_slist_free_full(devices, hid_device_free);
+	devices = NULL;
+
+	if (ctrl_io) {
+		g_io_channel_shutdown(ctrl_io, TRUE, NULL);
+		g_io_channel_unref(ctrl_io);
+		ctrl_io = NULL;
+	}
+
+	if (intr_io) {
+		g_io_channel_shutdown(intr_io, TRUE, NULL);
+		g_io_channel_unref(intr_io);
+		intr_io = NULL;
+	}
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_HIDHOST);
+	hal_ipc = NULL;
+
+	bt_unpaired_unregister(hid_unpaired_cb);
+}
diff --git a/repo/android/hidhost.h b/repo/android/hidhost.h
new file mode 100644
index 0000000..e6b87ed
--- /dev/null
+++ b/repo/android/hidhost.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+bool bt_hid_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_hid_unregister(void);
diff --git a/repo/android/init.bluetooth.rc b/repo/android/init.bluetooth.rc
new file mode 100644
index 0000000..2d43f73
--- /dev/null
+++ b/repo/android/init.bluetooth.rc
@@ -0,0 +1,38 @@
+# required permissions
+on boot
+    chown bluetooth bluetooth /data/misc/bluetooth
+    chown bluetooth bluetooth /dev/uhid
+    chown system    bluetooth /dev/uinput
+
+# services
+on property:bluetooth.start=daemon
+    setprop bluetooth.start none
+    start bluetoothd
+
+on property:bluetooth.stop=daemon
+    setprop bluetooth.stop none
+    stop bluetoothd
+
+on property:bluetooth.start=snoop
+    setprop bluetooth.start none
+    start bluetoothd-snoop
+
+on property:bluetooth.stop=snoop
+    setprop bluetooth.stop none
+    stop bluetoothd-snoop
+
+service bluetoothd /system/bin/bluetoothd
+    class main
+    # init does not yet support setting capabilities so run as root,
+    # bluetoothd drop uid to bluetooth with the right linux capabilities
+    group bluetooth
+    disabled
+    oneshot
+
+service bluetoothd-snoop /system/bin/bluetoothd-snoop
+    class main
+    # init does not yet support setting capabilities so run as root,
+    # bluetoothd-snoop drops unneeded linux capabilities
+    group nobody
+    disabled
+    oneshot
diff --git a/repo/android/ipc-common.h b/repo/android/ipc-common.h
new file mode 100644
index 0000000..27736e4
--- /dev/null
+++ b/repo/android/ipc-common.h
@@ -0,0 +1,38 @@
+/*
+ *
+ *  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
+ *
+ */
+
+#define IPC_MTU 1024
+
+#define IPC_STATUS_SUCCESS	0x00
+
+struct ipc_hdr {
+	uint8_t  service_id;
+	uint8_t  opcode;
+	uint16_t len;
+	uint8_t  payload[0];
+} __attribute__((packed));
+
+#define IPC_OP_STATUS		0x00
+struct ipc_status {
+	uint8_t code;
+} __attribute__((packed));
diff --git a/repo/android/ipc-tester.c b/repo/android/ipc-tester.c
new file mode 100644
index 0000000..1aa17d2
--- /dev/null
+++ b/repo/android/ipc-tester.c
@@ -0,0 +1,1512 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <libgen.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+#include "emulator/hciemu.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+
+#include <cutils/properties.h>
+
+#define WAIT_FOR_SIGNAL_TIME 2 /* in seconds */
+#define EMULATOR_SIGNAL "emulator_started"
+
+struct test_data {
+	struct mgmt *mgmt;
+	uint16_t mgmt_index;
+	struct hciemu *hciemu;
+	enum hciemu_type hciemu_type;
+	pid_t bluetoothd_pid;
+	bool setup_done;
+};
+
+struct ipc_data {
+	void *buffer;
+	size_t len;
+};
+
+struct generic_data {
+	struct ipc_data ipc_data;
+
+	unsigned int num_services;
+	int init_services[];
+};
+
+struct regmod_msg {
+	struct ipc_hdr header;
+	struct hal_cmd_register_module cmd;
+} __attribute__((packed));
+
+#define CONNECT_TIMEOUT (5 * 1000)
+#define SERVICE_NAME "bluetoothd"
+
+static char exec_dir[PATH_MAX];
+
+static int cmd_sk = -1;
+static int notif_sk = -1;
+
+static void read_info_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct mgmt_rp_read_info *rp = param;
+	char addr[18];
+	uint16_t manufacturer;
+	uint32_t supported_settings, current_settings;
+
+	tester_print("Read Info callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	ba2str(&rp->bdaddr, addr);
+	manufacturer = btohs(rp->manufacturer);
+	supported_settings = btohl(rp->supported_settings);
+	current_settings = btohl(rp->current_settings);
+
+	tester_print("  Address: %s", addr);
+	tester_print("  Version: 0x%02x", rp->version);
+	tester_print("  Manufacturer: 0x%04x", manufacturer);
+	tester_print("  Supported settings: 0x%08x", supported_settings);
+	tester_print("  Current settings: 0x%08x", current_settings);
+	tester_print("  Class: 0x%02x%02x%02x",
+			rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+	tester_print("  Name: %s", rp->name);
+	tester_print("  Short name: %s", rp->short_name);
+
+	if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Added callback");
+	tester_print("  Index: 0x%04x", index);
+
+	data->mgmt_index = index;
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+					read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Removed callback");
+	tester_print("  Index: 0x%04x", index);
+
+	if (index != data->mgmt_index)
+		return;
+
+	mgmt_unregister_index(data->mgmt, data->mgmt_index);
+
+	mgmt_unref(data->mgmt);
+	data->mgmt = NULL;
+
+	tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Read Index List callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+					index_added_callback, NULL, NULL);
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+					index_removed_callback, NULL, NULL);
+
+	data->hciemu = hciemu_new(data->hciemu_type);
+	if (!data->hciemu) {
+		tester_warn("Failed to setup HCI emulation");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	tester_print("New hciemu instance created");
+}
+
+static void test_pre_setup(const void *data)
+{
+	struct test_data *test_data = tester_get_data();
+
+	if (!tester_use_debug())
+		fclose(stderr);
+
+	test_data->mgmt = mgmt_new_default();
+	if (!test_data->mgmt) {
+		tester_warn("Failed to setup management interface");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	mgmt_send(test_data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0,
+				NULL, read_index_list_callback, NULL, NULL);
+}
+
+static void test_post_teardown(const void *data)
+{
+	struct test_data *test_data = tester_get_data();
+
+	if (test_data->hciemu) {
+		hciemu_unref(test_data->hciemu);
+		test_data->hciemu = NULL;
+	}
+}
+
+static void bluetoothd_start(int hci_index)
+{
+	char prg_name[PATH_MAX];
+	char index[8];
+	char *prg_argv[4];
+
+	snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd");
+	snprintf(index, sizeof(index), "%d", hci_index);
+
+	prg_argv[0] = prg_name;
+	prg_argv[1] = "-i";
+	prg_argv[2] = index;
+	prg_argv[3] = NULL;
+
+	if (!tester_use_debug())
+		fclose(stderr);
+
+	execve(prg_argv[0], prg_argv, NULL);
+}
+
+static void emulator(int pipe, int hci_index)
+{
+	static const char SYSTEM_SOCKET_PATH[] = "\0android_system";
+	char buf[1024];
+	struct sockaddr_un addr;
+	struct timeval tv;
+	int fd;
+	ssize_t len;
+
+	fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+	if (fd < 0)
+		goto failed;
+
+	tv.tv_sec = WAIT_FOR_SIGNAL_TIME;
+	tv.tv_usec = 0;
+	setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH));
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to bind system socket");
+		goto failed;
+	}
+
+	len = write(pipe, EMULATOR_SIGNAL, sizeof(EMULATOR_SIGNAL));
+
+	if (len != sizeof(EMULATOR_SIGNAL))
+		goto failed;
+
+	memset(buf, 0, sizeof(buf));
+
+	len = read(fd, buf, sizeof(buf));
+	if (len <= 0 || strcmp(buf, "ctl.start=bluetoothd"))
+		goto failed;
+
+	close(pipe);
+	close(fd);
+	return bluetoothd_start(hci_index);
+
+failed:
+	close(pipe);
+	if (fd >= 0)
+		close(fd);
+}
+
+static int accept_connection(int sk)
+{
+	int err;
+	struct pollfd pfd;
+	int new_sk;
+
+	memset(&pfd, 0 , sizeof(pfd));
+	pfd.fd = sk;
+	pfd.events = POLLIN;
+
+	err = poll(&pfd, 1, CONNECT_TIMEOUT);
+	if (err < 0) {
+		err = errno;
+		tester_warn("Failed to poll: %d (%s)", err, strerror(err));
+		return -errno;
+	}
+
+	if (err == 0) {
+		tester_warn("bluetoothd connect timeout");
+		return -errno;
+	}
+
+	new_sk = accept(sk, NULL, NULL);
+	if (new_sk < 0) {
+		err = errno;
+		tester_warn("Failed to accept socket: %d (%s)",
+							err, strerror(err));
+		return -errno;
+	}
+
+	return new_sk;
+}
+
+static bool init_ipc(void)
+{
+	struct sockaddr_un addr;
+
+	int sk;
+	int err;
+
+	sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
+	if (sk < 0) {
+		err = errno;
+		tester_warn("Failed to create socket: %d (%s)", err,
+							strerror(err));
+		return false;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+
+	memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH));
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		err = errno;
+		tester_warn("Failed to bind socket: %d (%s)", err,
+								strerror(err));
+		close(sk);
+		return false;
+	}
+
+	if (listen(sk, 2) < 0) {
+		err = errno;
+		tester_warn("Failed to listen on socket: %d (%s)", err,
+								strerror(err));
+		close(sk);
+		return false;
+	}
+
+	/* Start Android Bluetooth daemon service */
+	if (property_set("ctl.start", SERVICE_NAME) < 0) {
+		tester_warn("Failed to start service %s", SERVICE_NAME);
+		close(sk);
+		return false;
+	}
+
+	cmd_sk = accept_connection(sk);
+	if (cmd_sk < 0) {
+		close(sk);
+		return false;
+	}
+
+	notif_sk = accept_connection(sk);
+	if (notif_sk < 0) {
+		close(sk);
+		close(cmd_sk);
+		cmd_sk = -1;
+		return false;
+	}
+
+	tester_print("bluetoothd connected");
+
+	close(sk);
+
+	return true;
+}
+
+static void cleanup_ipc(void)
+{
+	if (cmd_sk < 0)
+		return;
+
+	close(cmd_sk);
+	cmd_sk = -1;
+}
+
+static gboolean check_for_daemon(gpointer user_data)
+{
+	int status;
+	struct test_data *data = user_data;
+
+	if ((waitpid(data->bluetoothd_pid, &status, WNOHANG))
+							!= data->bluetoothd_pid)
+		return true;
+
+	if (data->setup_done) {
+		if (WIFEXITED(status) &&
+				(WEXITSTATUS(status) == EXIT_SUCCESS)) {
+			tester_test_passed();
+			return false;
+		}
+		tester_test_failed();
+	} else {
+		tester_setup_failed();
+		test_post_teardown(data);
+	}
+
+	tester_warn("Unexpected Daemon shutdown with status %d", status);
+	return false;
+}
+
+static bool setup_module(int service_id)
+{
+	struct ipc_hdr response;
+	struct ipc_hdr expected_response;
+
+	struct regmod_msg btmodule_msg = {
+		.header = {
+			.service_id = HAL_SERVICE_ID_CORE,
+			.opcode = HAL_OP_REGISTER_MODULE,
+			.len = sizeof(struct hal_cmd_register_module),
+			},
+		.cmd = {
+			.service_id = service_id,
+			.mode = HAL_MODE_DEFAULT,
+			.max_clients = 1,
+			},
+	};
+
+	if (write(cmd_sk, &btmodule_msg, sizeof(btmodule_msg)) < 0)
+		goto fail;
+
+	if (read(cmd_sk, &response, sizeof(response)) < 0)
+		goto fail;
+
+	expected_response = btmodule_msg.header;
+	expected_response.len = 0;
+
+	if (memcmp(&response, &expected_response, sizeof(response)) == 0)
+		return true;
+
+fail:
+	tester_warn("Module registration failed.");
+	return false;
+}
+
+static void setup(const void *data)
+{
+	const struct generic_data *generic_data = data;
+	struct test_data *test_data = tester_get_data();
+	int signal_fd[2];
+	char buf[1024];
+	pid_t pid;
+	int len;
+	unsigned int i;
+
+	if (pipe(signal_fd))
+		goto failed;
+
+	pid = fork();
+
+	if (pid < 0) {
+		close(signal_fd[0]);
+		close(signal_fd[1]);
+		goto failed;
+	}
+
+	if (pid == 0) {
+		if (!tester_use_debug())
+			fclose(stderr);
+
+		close(signal_fd[0]);
+		emulator(signal_fd[1], test_data->mgmt_index);
+		exit(0);
+	}
+
+	close(signal_fd[1]);
+	test_data->bluetoothd_pid = pid;
+
+	len = read(signal_fd[0], buf, sizeof(buf));
+	if (len <= 0 || (strcmp(buf, EMULATOR_SIGNAL))) {
+		close(signal_fd[0]);
+		goto failed;
+	}
+
+	g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, check_for_daemon, test_data,
+									NULL);
+
+	if (!init_ipc()) {
+		tester_warn("Cannot initialize IPC mechanism!");
+		goto failed;
+	}
+	tester_print("Will init %d services.", generic_data->num_services);
+
+	for (i = 0; i < generic_data->num_services; i++)
+		if (!setup_module(generic_data->init_services[i])) {
+			cleanup_ipc();
+			goto failed;
+		}
+
+	test_data->setup_done = true;
+
+	tester_setup_complete();
+	return;
+
+failed:
+	g_idle_remove_by_data(test_data);
+	tester_setup_failed();
+	test_post_teardown(data);
+}
+
+static void teardown(const void *data)
+{
+	struct test_data *test_data = tester_get_data();
+
+	g_idle_remove_by_data(test_data);
+	cleanup_ipc();
+
+	if (test_data->bluetoothd_pid)
+		waitpid(test_data->bluetoothd_pid, NULL, 0);
+
+	tester_teardown_complete();
+}
+
+static void ipc_send_tc(const void *data)
+{
+	const struct generic_data *generic_data = data;
+	const struct ipc_data *ipc_data = &generic_data->ipc_data;
+
+	if (ipc_data->len) {
+		if (write(cmd_sk, ipc_data->buffer, ipc_data->len) < 0)
+			tester_test_failed();
+	}
+}
+
+#define service_data(args...) { args }
+
+#define gen_data(writelen, writebuf, servicelist...) \
+	{								\
+		.ipc_data = {						\
+			.buffer = writebuf,				\
+			.len = writelen,				\
+		},							\
+		.init_services = service_data(servicelist),		\
+		.num_services = sizeof((const int[])			\
+					service_data(servicelist)) /	\
+					sizeof(int),			\
+	}
+
+#define test_generic(name, test, setup, teardown, buffer, writelen, \
+							services...) \
+	do {								\
+		struct test_data *user;					\
+		static const struct generic_data data =			\
+				gen_data(writelen, buffer, services);	\
+		user = g_malloc0(sizeof(struct test_data));		\
+		if (!user)						\
+			break;						\
+		user->hciemu_type = HCIEMU_TYPE_BREDRLE;		\
+		tester_add_full(name, &data, test_pre_setup, setup,	\
+				test, teardown, test_post_teardown,	\
+				3, user, g_free);			\
+	} while (0)
+
+#define test_opcode_valid(_name, _service, _opcode, _len, _servicelist...) \
+	do {								\
+		static struct ipc_hdr hdr = {				\
+			.service_id = _service,				\
+			.opcode = _opcode,				\
+			.len = _len,					\
+		};							\
+									\
+		test_generic("Opcode out of range: "_name,		\
+				ipc_send_tc, setup, teardown,		\
+				&hdr,					\
+				sizeof(hdr),				\
+				_servicelist);				\
+	} while (0)
+
+struct vardata {
+	struct ipc_hdr hdr;
+	uint8_t buf[IPC_MTU];
+} __attribute__((packed));
+
+#define test_datasize_valid(_name, _service, _opcode, _hlen, _addatasize, \
+							_servicelist...) \
+	do {								\
+		static struct vardata vdata = {				\
+			.hdr.service_id = _service,			\
+			.hdr.opcode = _opcode,				\
+			.hdr.len = (_hlen) + (_addatasize),		\
+			.buf = {},					\
+		};							\
+		test_generic("Data size "_name,				\
+				ipc_send_tc, setup, teardown,		\
+				&vdata,					\
+				sizeof(vdata.hdr) + (_hlen) + (_addatasize),\
+				_servicelist);				\
+	} while (0)
+
+static struct regmod_msg register_bt_msg = {
+	.header = {
+		.service_id = HAL_SERVICE_ID_CORE,
+		.opcode = HAL_OP_REGISTER_MODULE,
+		.len = sizeof(struct hal_cmd_register_module),
+		},
+	.cmd = {
+		.service_id = HAL_SERVICE_ID_BLUETOOTH,
+		},
+};
+
+static struct regmod_msg register_bt_malformed_size_msg = {
+	.header = {
+		.service_id = HAL_SERVICE_ID_CORE,
+		.opcode = HAL_OP_REGISTER_MODULE,
+		/* wrong payload size declared */
+		.len = sizeof(struct hal_cmd_register_module) - 1,
+		},
+	.cmd = {
+		.service_id = HAL_SERVICE_ID_CORE,
+		},
+};
+
+struct malformed_data3_struct {
+	struct regmod_msg valid_msg;
+	int redundant_data;
+}  __attribute__((packed));
+
+static struct malformed_data3_struct malformed_data3_msg = {
+	/* valid register service message */
+	.valid_msg = {
+		.header = {
+			.service_id = HAL_SERVICE_ID_CORE,
+			.opcode = HAL_OP_REGISTER_MODULE,
+			.len = sizeof(struct hal_cmd_register_module),
+			},
+		.cmd = {
+			.service_id = HAL_SERVICE_ID_CORE,
+			},
+	},
+	/* plus redundant data */
+	. redundant_data = 666,
+};
+
+static struct ipc_hdr enable_unknown_service_hdr = {
+	.service_id = HAL_SERVICE_ID_MAX + 1,
+	.opcode = HAL_OP_REGISTER_MODULE,
+	.len = 0,
+};
+
+static struct ipc_hdr enable_bt_service_hdr = {
+	.service_id = HAL_SERVICE_ID_BLUETOOTH,
+	.opcode = HAL_OP_ENABLE,
+	.len = 0,
+};
+
+struct bt_set_adapter_prop_data {
+	struct ipc_hdr hdr;
+	struct hal_cmd_set_adapter_prop prop;
+
+	/* data placeholder for hal_cmd_set_adapter_prop.val[0] */
+	uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+				sizeof(struct hal_cmd_set_adapter_prop)];
+} __attribute__((packed));
+
+#define set_name "new name"
+
+static struct bt_set_adapter_prop_data bt_set_adapter_prop_data_overs = {
+	.hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+	.hdr.opcode = HAL_OP_SET_ADAPTER_PROP,
+	.hdr.len = sizeof(struct hal_cmd_set_adapter_prop) + sizeof(set_name),
+
+	.prop.type = HAL_PROP_ADAPTER_NAME,
+	/* declare wrong descriptor length */
+	.prop.len = sizeof(set_name) + 1,
+	/* init prop.val[0] */
+	.buf = set_name,
+};
+
+static struct bt_set_adapter_prop_data bt_set_adapter_prop_data_unders = {
+	.hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+	.hdr.opcode = HAL_OP_SET_ADAPTER_PROP,
+	.hdr.len = sizeof(struct hal_cmd_set_adapter_prop) + sizeof(set_name),
+
+	.prop.type = HAL_PROP_ADAPTER_NAME,
+	/* declare wrong descriptor length */
+	.prop.len = sizeof(set_name) - 1,
+	/* init prop.val[0] */
+	.buf = set_name,
+};
+
+struct bt_set_remote_prop_data {
+	struct ipc_hdr hdr;
+	struct hal_cmd_set_remote_device_prop prop;
+
+	/* data placeholder for hal_cmd_set_remote_device_prop.val[0] */
+	uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+				sizeof(struct hal_cmd_set_remote_device_prop)];
+} __attribute__((packed));
+
+static struct bt_set_remote_prop_data bt_set_remote_prop_data_overs = {
+	.hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+	.hdr.opcode = HAL_OP_SET_REMOTE_DEVICE_PROP,
+	.hdr.len = sizeof(struct hal_cmd_set_remote_device_prop) +
+							sizeof(set_name),
+
+	.prop.bdaddr = {},
+	.prop.type = HAL_PROP_DEVICE_NAME,
+	/* declare wrong descriptor length */
+	.prop.len = sizeof(set_name) + 1,
+	.buf = set_name,
+};
+
+static struct bt_set_remote_prop_data bt_set_remote_prop_data_unders = {
+	.hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+	.hdr.opcode = HAL_OP_SET_REMOTE_DEVICE_PROP,
+	.hdr.len = sizeof(struct hal_cmd_set_remote_device_prop) +
+						sizeof(set_name),
+
+	.prop.bdaddr = {},
+	.prop.type = HAL_PROP_DEVICE_NAME,
+	/* declare wrong descriptor length */
+	.prop.len = sizeof(set_name) - 1,
+	.buf = set_name,
+};
+
+struct hidhost_set_info_data {
+	struct ipc_hdr hdr;
+	struct hal_cmd_hidhost_set_info info;
+
+	/* data placeholder for hal_cmd_hidhost_set_info.descr[0] field */
+	uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+				sizeof(struct hal_cmd_hidhost_set_info)];
+} __attribute__((packed));
+
+#define set_info_data "some descriptor"
+
+static struct hidhost_set_info_data hidhost_set_info_data_overs = {
+	.hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+	.hdr.opcode = HAL_OP_HIDHOST_SET_INFO,
+	.hdr.len = sizeof(struct hal_cmd_hidhost_set_info) +
+							sizeof(set_info_data),
+
+	/* declare wrong descriptor length */
+	.info.descr_len = sizeof(set_info_data) + 1,
+	/* init .info.descr[0] */
+	.buf = set_info_data,
+};
+
+static struct hidhost_set_info_data hidhost_set_info_data_unders = {
+	.hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+	.hdr.opcode = HAL_OP_HIDHOST_SET_INFO,
+	.hdr.len = sizeof(struct hal_cmd_hidhost_set_info) +
+							sizeof(set_info_data),
+
+	/* declare wrong descriptor length */
+	.info.descr_len = sizeof(set_info_data) - 1,
+	/* init .info.descr[0] */
+	.buf = set_info_data,
+};
+
+struct hidhost_set_report_data {
+	struct ipc_hdr hdr;
+	struct hal_cmd_hidhost_set_report report;
+
+	/* data placeholder for hal_cmd_hidhost_set_report.data[0] field */
+	uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+				sizeof(struct hal_cmd_hidhost_set_report)];
+} __attribute__((packed));
+
+#define set_rep_data "1234567890"
+
+static struct hidhost_set_report_data hidhost_set_report_data_overs = {
+	.hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+	.hdr.opcode = HAL_OP_HIDHOST_SET_REPORT,
+	.hdr.len = sizeof(struct hal_cmd_hidhost_set_report) +
+							sizeof(set_rep_data),
+
+	/* declare wrong descriptor length */
+	.report.len = sizeof(set_rep_data) + 1,
+	/* init report.data[0] */
+	.buf = set_rep_data,
+};
+
+static struct hidhost_set_report_data hidhost_set_report_data_unders = {
+	.hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+	.hdr.opcode = HAL_OP_HIDHOST_SET_REPORT,
+	.hdr.len = sizeof(struct hal_cmd_hidhost_set_report) +
+							sizeof(set_rep_data),
+
+	/* declare wrong descriptor length */
+	.report.len = sizeof(set_rep_data) - 1,
+	/* init report.data[0] */
+	.buf = set_rep_data,
+};
+
+struct hidhost_send_data_data {
+	struct ipc_hdr hdr;
+	struct hal_cmd_hidhost_send_data hiddata;
+
+	/* data placeholder for hal_cmd_hidhost_send_data.data[0] field */
+	uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+				sizeof(struct hal_cmd_hidhost_send_data)];
+} __attribute__((packed));
+
+#define send_data_data "1234567890"
+
+static struct hidhost_send_data_data hidhost_send_data_overs = {
+	.hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+	.hdr.opcode = HAL_OP_HIDHOST_SEND_DATA,
+	.hdr.len = sizeof(struct hal_cmd_hidhost_send_data) +
+							sizeof(send_data_data),
+
+	/* declare wrong descriptor length */
+	.hiddata.len = sizeof(send_data_data) + 1,
+	/* init .hiddata.data[0] */
+	.buf = send_data_data,
+};
+
+static struct hidhost_send_data_data hidhost_send_data_unders = {
+	.hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+	.hdr.opcode = HAL_OP_HIDHOST_SEND_DATA,
+	.hdr.len = sizeof(struct hal_cmd_hidhost_send_data) +
+							sizeof(send_data_data),
+
+	/* declare wrong descriptor length */
+	.hiddata.len = sizeof(send_data_data) - 1,
+	/* init .hiddata.data[0] */
+	.buf = send_data_data,
+};
+
+#define hfp_number "#1234567890"
+
+struct hfp_dial_data {
+	struct ipc_hdr hdr;
+	struct hal_cmd_hf_client_dial data;
+
+	uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+				sizeof(struct hal_cmd_hf_client_dial)];
+} __attribute__((packed));
+
+static struct hfp_dial_data hfp_dial_overs = {
+	.hdr.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT,
+	.hdr.opcode = HAL_OP_HF_CLIENT_DIAL,
+	.hdr.len = sizeof(struct hal_cmd_hf_client_dial) + sizeof(hfp_number),
+
+	.data.number_len = sizeof(hfp_number) + 1,
+	.buf = hfp_number,
+};
+
+static struct hfp_dial_data hfp_dial_unders = {
+	.hdr.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT,
+	.hdr.opcode = HAL_OP_HF_CLIENT_DIAL,
+	.hdr.len = sizeof(struct hal_cmd_hf_client_dial) + sizeof(hfp_number),
+
+	.data.number_len = sizeof(hfp_number) - 1,
+	.buf = hfp_number,
+};
+
+int main(int argc, char *argv[])
+{
+	snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0]));
+
+	tester_init(&argc, &argv);
+
+	/* check general IPC errors */
+	test_generic("Too small data",
+				ipc_send_tc, setup, teardown,
+				&register_bt_msg, 1);
+
+	test_generic("Malformed data (wrong payload declared)",
+				ipc_send_tc, setup, teardown,
+				&register_bt_malformed_size_msg,
+				sizeof(register_bt_malformed_size_msg),
+				HAL_SERVICE_ID_BLUETOOTH);
+
+	test_generic("Malformed data2 (undersized msg)",
+				ipc_send_tc, setup, teardown,
+				&register_bt_msg,
+				sizeof(register_bt_msg) - 1,
+				HAL_SERVICE_ID_BLUETOOTH);
+
+	test_generic("Malformed data3 (oversized msg)",
+				ipc_send_tc, setup, teardown,
+				&malformed_data3_msg,
+				sizeof(malformed_data3_msg),
+				HAL_SERVICE_ID_BLUETOOTH);
+
+	test_generic("Invalid service",
+				ipc_send_tc, setup, teardown,
+				&enable_unknown_service_hdr,
+				sizeof(enable_unknown_service_hdr),
+				HAL_SERVICE_ID_BLUETOOTH);
+
+	test_generic("Enable unregistered service",
+				ipc_send_tc, setup, teardown,
+				&enable_bt_service_hdr,
+				sizeof(enable_bt_service_hdr));
+
+	/* check service handler's max opcode value */
+	test_opcode_valid("CORE", HAL_SERVICE_ID_CORE, 0x03, 0);
+
+	test_opcode_valid("BLUETOOTH", HAL_SERVICE_ID_BLUETOOTH, 0x15, 0,
+			HAL_SERVICE_ID_BLUETOOTH);
+
+	test_opcode_valid("SOCK", HAL_SERVICE_ID_SOCKET, 0x03, 0,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+
+	test_opcode_valid("HIDHOST", HAL_SERVICE_ID_HIDHOST, 0x10, 0,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+
+	test_opcode_valid("PAN", HAL_SERVICE_ID_PAN, 0x05, 0,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+
+	test_opcode_valid("HANDSFREE", HAL_SERVICE_ID_HANDSFREE, 0x10, 0,
+						HAL_SERVICE_ID_BLUETOOTH,
+						HAL_SERVICE_ID_HANDSFREE);
+
+	test_opcode_valid("A2DP", HAL_SERVICE_ID_A2DP, 0x03, 0,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+
+	test_opcode_valid("HEALTH", HAL_SERVICE_ID_HEALTH, 0x06, 0,
+						HAL_SERVICE_ID_BLUETOOTH,
+						HAL_SERVICE_ID_HEALTH);
+
+	test_opcode_valid("AVRCP", HAL_SERVICE_ID_AVRCP, 0x0b, 0,
+				HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_AVRCP);
+
+	test_opcode_valid("GATT", HAL_SERVICE_ID_GATT, 0x24, 0,
+				HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_GATT);
+
+	test_opcode_valid("HF_CLIENT", HAL_SERVICE_ID_HANDSFREE_CLIENT, 0x10, 0,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+
+	test_opcode_valid("MAP_CLIENT", HAL_SERVICE_ID_MAP_CLIENT, 0x01, 0,
+						HAL_SERVICE_ID_BLUETOOTH,
+						HAL_SERVICE_ID_MAP_CLIENT);
+
+	/* check for valid data size */
+	test_datasize_valid("CORE Register+", HAL_SERVICE_ID_CORE,
+			HAL_OP_REGISTER_MODULE,
+			sizeof(struct hal_cmd_register_module), 1);
+	test_datasize_valid("CORE Register-", HAL_SERVICE_ID_CORE,
+			HAL_OP_REGISTER_MODULE,
+			sizeof(struct hal_cmd_register_module), -1);
+	test_datasize_valid("CORE Unregister+", HAL_SERVICE_ID_CORE,
+			HAL_OP_UNREGISTER_MODULE,
+			sizeof(struct hal_cmd_unregister_module), 1);
+	test_datasize_valid("CORE Unregister-", HAL_SERVICE_ID_CORE,
+			HAL_OP_UNREGISTER_MODULE,
+			sizeof(struct hal_cmd_unregister_module), -1);
+
+	/* check for valid data size for BLUETOOTH */
+	test_datasize_valid("BT Enable+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_ENABLE,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Disable+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_DISABLE,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Adapter Props+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_ADAPTER_PROPS,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Adapter Prop+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_ADAPTER_PROP,
+			sizeof(struct hal_cmd_get_adapter_prop), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Adapter Prop-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_ADAPTER_PROP,
+			sizeof(struct hal_cmd_get_adapter_prop), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Set Adapter Prop+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_SET_ADAPTER_PROP,
+			sizeof(struct hal_cmd_set_adapter_prop), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Set Adapter Prop-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_SET_ADAPTER_PROP,
+			sizeof(struct hal_cmd_set_adapter_prop), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_generic("Data size BT Set Adapter Prop Vardata+",
+			ipc_send_tc, setup, teardown,
+			&bt_set_adapter_prop_data_overs,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_set_adapter_prop) +
+				sizeof(set_name)),
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_generic("Data size BT Set Adapter Prop Vardata+",
+			ipc_send_tc, setup, teardown,
+			&bt_set_adapter_prop_data_unders,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_set_adapter_prop) +
+				sizeof(set_name)),
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote Props+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_DEVICE_PROPS,
+			sizeof(struct hal_cmd_get_remote_device_props), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote Props-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_DEVICE_PROPS,
+			sizeof(struct hal_cmd_get_remote_device_props), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote Prop+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_DEVICE_PROP,
+			sizeof(struct hal_cmd_get_remote_device_prop), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote Prop-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_DEVICE_PROP,
+			sizeof(struct hal_cmd_get_remote_device_prop), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Set Remote Prop+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_SET_REMOTE_DEVICE_PROP,
+			sizeof(struct hal_cmd_set_remote_device_prop), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Set Remote Prop-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_SET_REMOTE_DEVICE_PROP,
+			sizeof(struct hal_cmd_set_remote_device_prop), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_generic("Data size BT Set Remote Prop Vardata+",
+			ipc_send_tc, setup, teardown,
+			&bt_set_remote_prop_data_overs,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_set_remote_device_prop) +
+				sizeof(set_name)),
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_generic("Data size BT Set Remote Prop Vardata-",
+			ipc_send_tc, setup, teardown,
+			&bt_set_remote_prop_data_unders,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_set_remote_device_prop) +
+				sizeof(set_name)),
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote SV Rec+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_SERVICE_REC,
+			sizeof(struct hal_cmd_get_remote_service_rec), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote SV Rec-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_SERVICE_REC,
+			sizeof(struct hal_cmd_get_remote_service_rec), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote Services+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_SERVICES,
+			sizeof(struct hal_cmd_get_remote_services), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote Services-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_SERVICES,
+			sizeof(struct hal_cmd_get_remote_services), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Start Discovery+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_START_DISCOVERY,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Cancel Discovery+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_CANCEL_DISCOVERY,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Create Bond+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_CREATE_BOND,
+			sizeof(struct hal_cmd_create_bond), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Create Bond-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_CREATE_BOND,
+			sizeof(struct hal_cmd_create_bond), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Remove Bond+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_REMOVE_BOND,
+			sizeof(struct hal_cmd_remove_bond), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Remove Bond-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_REMOVE_BOND,
+			sizeof(struct hal_cmd_remove_bond), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Cancel Bond+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_CANCEL_BOND,
+			sizeof(struct hal_cmd_cancel_bond), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Cancel Bond-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_CANCEL_BOND,
+			sizeof(struct hal_cmd_cancel_bond), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Pin Reply+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_PIN_REPLY,
+			sizeof(struct hal_cmd_pin_reply), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Pin Reply-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_PIN_REPLY,
+			sizeof(struct hal_cmd_pin_reply), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT SSP Reply+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_SSP_REPLY,
+			sizeof(struct hal_cmd_ssp_reply), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT SSP Reply-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_SSP_REPLY,
+			sizeof(struct hal_cmd_ssp_reply), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT DUT Mode Conf+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_DUT_MODE_CONF,
+			sizeof(struct hal_cmd_dut_mode_conf), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT DUT Mode Conf-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_DUT_MODE_CONF,
+			sizeof(struct hal_cmd_dut_mode_conf), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT DUT Mode Send+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_DUT_MODE_SEND,
+			sizeof(struct hal_cmd_dut_mode_send), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT DUT Mode Send-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_DUT_MODE_SEND,
+			sizeof(struct hal_cmd_dut_mode_send), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT LE Test+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_LE_TEST_MODE,
+			sizeof(struct hal_cmd_le_test_mode), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT LE Test-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_LE_TEST_MODE,
+			sizeof(struct hal_cmd_le_test_mode), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+
+	/* check for valid data size for SOCK */
+	test_datasize_valid("SOCKET Listen+", HAL_SERVICE_ID_SOCKET,
+			HAL_OP_SOCKET_LISTEN,
+			sizeof(struct hal_cmd_socket_listen), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+	test_datasize_valid("SOCKET Listen-", HAL_SERVICE_ID_SOCKET,
+			HAL_OP_SOCKET_LISTEN,
+			sizeof(struct hal_cmd_socket_listen), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+	test_datasize_valid("SOCKET Connect+", HAL_SERVICE_ID_SOCKET,
+			HAL_OP_SOCKET_CONNECT,
+			sizeof(struct hal_cmd_socket_connect), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+	test_datasize_valid("SOCKET Connect-", HAL_SERVICE_ID_SOCKET,
+			HAL_OP_SOCKET_CONNECT,
+			sizeof(struct hal_cmd_socket_connect), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+
+	/* check for valid data size for HID Host */
+	test_datasize_valid("HIDHOST Connect+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_CONNECT,
+			sizeof(struct hal_cmd_hidhost_connect), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Connect-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_CONNECT,
+			sizeof(struct hal_cmd_hidhost_connect), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Disconnect+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_DISCONNECT,
+			sizeof(struct hal_cmd_hidhost_disconnect), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Disconnect-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_DISCONNECT,
+			sizeof(struct hal_cmd_hidhost_disconnect), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Virt. Unplug+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_VIRTUAL_UNPLUG,
+			sizeof(struct hal_cmd_hidhost_virtual_unplug), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Virt. Unplug-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_VIRTUAL_UNPLUG,
+			sizeof(struct hal_cmd_hidhost_virtual_unplug), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Set Info+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SET_INFO,
+			sizeof(struct hal_cmd_hidhost_set_info), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Set Info-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SET_INFO,
+			sizeof(struct hal_cmd_hidhost_set_info), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_generic("Data size HIDHOST Set Info Vardata+",
+			ipc_send_tc, setup, teardown,
+			&hidhost_set_info_data_overs,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hidhost_set_info) +
+				sizeof(set_info_data)),
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_generic("Data size HIDHOST Set Info Vardata-",
+			ipc_send_tc, setup, teardown,
+			&hidhost_set_info_data_unders,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hidhost_set_info) +
+				sizeof(set_info_data)),
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Get Protocol+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_GET_PROTOCOL,
+			sizeof(struct hal_cmd_hidhost_get_protocol), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Get Protocol-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_GET_PROTOCOL,
+			sizeof(struct hal_cmd_hidhost_get_protocol), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Set Protocol+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SET_PROTOCOL,
+			sizeof(struct hal_cmd_hidhost_set_protocol), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Set Protocol-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SET_PROTOCOL,
+			sizeof(struct hal_cmd_hidhost_set_protocol), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Get Report+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_GET_REPORT,
+			sizeof(struct hal_cmd_hidhost_get_report), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Get Report-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_GET_REPORT,
+			sizeof(struct hal_cmd_hidhost_get_report), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Set Report+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SET_REPORT,
+			sizeof(struct hal_cmd_hidhost_set_report), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Set Report-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SET_REPORT,
+			sizeof(struct hal_cmd_hidhost_set_report), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_generic("Data size HIDHOST Set Report Vardata+",
+			ipc_send_tc, setup, teardown,
+			&hidhost_set_report_data_overs,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hidhost_set_report) +
+				sizeof(set_rep_data)),
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_generic("Data size HIDHOST Set Report Vardata-",
+			ipc_send_tc, setup, teardown,
+			&hidhost_set_report_data_unders,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hidhost_set_report) +
+				sizeof(set_rep_data)),
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Send Data+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SEND_DATA,
+			sizeof(struct hal_cmd_hidhost_send_data), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Send Data-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SEND_DATA,
+			sizeof(struct hal_cmd_hidhost_send_data), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_generic("Data size HIDHOST Send Vardata+",
+			ipc_send_tc, setup, teardown,
+			&hidhost_send_data_overs,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hidhost_send_data) +
+				sizeof(send_data_data)),
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_generic("Data size HIDHOST Send Vardata-",
+			ipc_send_tc, setup, teardown,
+			&hidhost_send_data_unders,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hidhost_send_data) +
+				sizeof(send_data_data)),
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+
+	/* check for valid data size for PAN */
+	test_datasize_valid("PAN Enable+", HAL_SERVICE_ID_PAN,
+			HAL_OP_PAN_ENABLE,
+			sizeof(struct hal_cmd_pan_enable), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+	test_datasize_valid("PAN Enable-", HAL_SERVICE_ID_PAN,
+			HAL_OP_PAN_ENABLE,
+			sizeof(struct hal_cmd_pan_enable), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+	test_datasize_valid("PAN Get Role+", HAL_SERVICE_ID_PAN,
+			HAL_OP_PAN_GET_ROLE,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+	test_datasize_valid("PAN Connect+", HAL_SERVICE_ID_PAN,
+			HAL_OP_PAN_CONNECT,
+			sizeof(struct hal_cmd_pan_connect), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+	test_datasize_valid("PAN Connect-", HAL_SERVICE_ID_PAN,
+			HAL_OP_PAN_CONNECT,
+			sizeof(struct hal_cmd_pan_connect), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+	test_datasize_valid("PAN Disconnect+", HAL_SERVICE_ID_PAN,
+			HAL_OP_PAN_DISCONNECT,
+			sizeof(struct hal_cmd_pan_disconnect), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+	test_datasize_valid("PAN Disconnect-", HAL_SERVICE_ID_PAN,
+			HAL_OP_PAN_DISCONNECT,
+			sizeof(struct hal_cmd_pan_disconnect), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+
+	/* check for valid data size for A2DP */
+	test_datasize_valid("A2DP Connect+", HAL_SERVICE_ID_A2DP,
+			HAL_OP_A2DP_CONNECT,
+			sizeof(struct hal_cmd_a2dp_connect), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+	test_datasize_valid("A2DP Connect-", HAL_SERVICE_ID_A2DP,
+			HAL_OP_A2DP_CONNECT,
+			sizeof(struct hal_cmd_a2dp_connect), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+	test_datasize_valid("A2DP Disconnect+", HAL_SERVICE_ID_A2DP,
+			HAL_OP_A2DP_DISCONNECT,
+			sizeof(struct hal_cmd_a2dp_disconnect), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+	test_datasize_valid("A2DP Disconnect-", HAL_SERVICE_ID_A2DP,
+			HAL_OP_A2DP_DISCONNECT,
+			sizeof(struct hal_cmd_a2dp_disconnect), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+
+	/* Check for valid data size for Handsfree Client */
+	test_datasize_valid("HF_CLIENT Connect+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_CONNECT,
+			sizeof(struct hal_cmd_hf_client_connect), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Connect-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_CONNECT,
+			sizeof(struct hal_cmd_hf_client_connect), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Disconnect+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_DISCONNECT,
+			sizeof(struct hal_cmd_hf_client_disconnect), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Disconnect-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_DISCONNECT,
+			sizeof(struct hal_cmd_hf_client_disconnect), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Connect Audio+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_CONNECT_AUDIO,
+			sizeof(struct hal_cmd_hf_client_connect_audio), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Connect Audio-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_CONNECT_AUDIO,
+			sizeof(struct hal_cmd_hf_client_connect_audio), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Disconnect Audio+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_DISCONNECT_AUDIO,
+			sizeof(struct hal_cmd_hf_client_disconnect_audio), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Disconnect Audio-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_DISCONNECT_AUDIO,
+			sizeof(struct hal_cmd_hf_client_disconnect_audio), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Start VR+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_START_VR,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Start VR-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_START_VR,
+			0, -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Stop VR+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_STOP_VR,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Stop VR-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_STOP_VR,
+			0, -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Vol Contr.+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_VOLUME_CONTROL,
+			sizeof(struct hal_cmd_hf_client_volume_control), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Vol Contr.-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_VOLUME_CONTROL,
+			sizeof(struct hal_cmd_hf_client_volume_control), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_generic("Data size HF_CLIENT Dial Vardata+",
+			ipc_send_tc, setup, teardown,
+			&hfp_dial_overs,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hf_client_dial) +
+				sizeof(hfp_number)),
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_generic("Data size HF_CLIENT Dial Vardata-",
+			ipc_send_tc, setup, teardown,
+			&hfp_dial_unders,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hf_client_dial) +
+				sizeof(hfp_number)),
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Dial Memory+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_DIAL_MEMORY,
+			sizeof(struct hal_cmd_hf_client_dial_memory), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Dial Memory-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_DIAL_MEMORY,
+			sizeof(struct hal_cmd_hf_client_dial_memory), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Call Action+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_CALL_ACTION,
+			sizeof(struct hal_cmd_hf_client_call_action), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Call Action-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_CALL_ACTION,
+			sizeof(struct hal_cmd_hf_client_call_action), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Query Current Calls+",
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Query Current Calls-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS,
+			0, -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Query Operator Name+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Query Operator Name-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME,
+			0, -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Retrieve Subscrb. Info+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Retrieve Subscrb. Info-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO,
+			0, -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Send DTMF+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_SEND_DTMF,
+			sizeof(struct hal_cmd_hf_client_send_dtmf), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Send DTMF-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_SEND_DTMF,
+			sizeof(struct hal_cmd_hf_client_send_dtmf), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Get Last Voice Tag+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Get Last Voice Tag-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM,
+			0, -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+
+	/* check for valid data size for MAP CLIENT */
+	test_datasize_valid("MAP CLIENT Get instances+",
+				HAL_SERVICE_ID_MAP_CLIENT,
+				HAL_OP_MAP_CLIENT_GET_INSTANCES,
+				sizeof(struct hal_cmd_map_client_get_instances),
+				1, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_SERVICE_ID_MAP_CLIENT);
+	test_datasize_valid("MAP CLIENT Get instances-",
+				HAL_SERVICE_ID_MAP_CLIENT,
+				HAL_OP_MAP_CLIENT_GET_INSTANCES,
+				sizeof(struct hal_cmd_map_client_get_instances),
+				-1, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_SERVICE_ID_MAP_CLIENT);
+
+	return tester_run();
+}
diff --git a/repo/android/ipc.c b/repo/android/ipc.c
new file mode 100644
index 0000000..2e67428
--- /dev/null
+++ b/repo/android/ipc.c
@@ -0,0 +1,437 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stddef.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "ipc-common.h"
+#include "ipc.h"
+#include "src/log.h"
+
+struct service_handler {
+	const struct ipc_handler *handler;
+	uint8_t size;
+};
+
+struct ipc {
+	struct service_handler *services;
+	int service_max;
+
+	const char *path;
+	size_t size;
+
+	GIOChannel *cmd_io;
+	guint cmd_watch;
+
+	bool notifications;
+	GIOChannel *notif_io;
+	guint notif_watch;
+
+	ipc_disconnect_cb disconnect_cb;
+	void *disconnect_cb_data;
+};
+
+static void ipc_disconnect(struct ipc *ipc, bool in_cleanup)
+{
+	if (ipc->cmd_watch) {
+		g_source_remove(ipc->cmd_watch);
+		ipc->cmd_watch = 0;
+	}
+
+	if (ipc->cmd_io) {
+		g_io_channel_shutdown(ipc->cmd_io, TRUE, NULL);
+		g_io_channel_unref(ipc->cmd_io);
+		ipc->cmd_io = NULL;
+	}
+
+	if (ipc->notif_watch) {
+		g_source_remove(ipc->notif_watch);
+		ipc->notif_watch = 0;
+	}
+
+	if (ipc->notif_io) {
+		g_io_channel_shutdown(ipc->notif_io, TRUE, NULL);
+		g_io_channel_unref(ipc->notif_io);
+		ipc->notif_io = NULL;
+	}
+
+	if (in_cleanup)
+		return;
+
+	if (ipc->disconnect_cb)
+		ipc->disconnect_cb(ipc->disconnect_cb_data);
+}
+
+static int ipc_handle_msg(struct service_handler *handlers, size_t max_index,
+						const void *buf, ssize_t len)
+{
+	const struct ipc_hdr *msg = buf;
+	const struct ipc_handler *handler;
+
+	if (len < (ssize_t) sizeof(*msg)) {
+		DBG("message too small (%zd bytes)", len);
+		return -EBADMSG;
+	}
+
+	if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
+		DBG("message malformed (%zd bytes)", len);
+		return -EBADMSG;
+	}
+
+	/* if service is valid */
+	if (msg->service_id > max_index) {
+		DBG("unknown service (0x%x)", msg->service_id);
+		return -EOPNOTSUPP;
+	}
+
+	/* if service is registered */
+	if (!handlers[msg->service_id].handler) {
+		DBG("service not registered (0x%x)", msg->service_id);
+		return -EOPNOTSUPP;
+	}
+
+	/* if opcode is valid */
+	if (msg->opcode == IPC_OP_STATUS ||
+			msg->opcode > handlers[msg->service_id].size) {
+		DBG("invalid opcode 0x%x for service 0x%x", msg->opcode,
+							msg->service_id);
+		return -EOPNOTSUPP;
+	}
+
+	/* opcode is table offset + 1 */
+	handler = &handlers[msg->service_id].handler[msg->opcode - 1];
+
+	/* if payload size is valid */
+	if ((handler->var_len && handler->data_len > msg->len) ||
+			(!handler->var_len && handler->data_len != msg->len)) {
+		DBG("invalid size for opcode 0x%x service 0x%x",
+						msg->opcode, msg->service_id);
+		return -EMSGSIZE;
+	}
+
+	handler->handler(msg->payload, msg->len);
+
+	return 0;
+}
+
+static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct ipc *ipc = user_data;
+
+	char buf[IPC_MTU];
+	ssize_t ret;
+	int fd, err;
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		info("IPC: command socket closed");
+
+		ipc->cmd_watch = 0;
+		goto fail;
+	}
+
+	fd = g_io_channel_unix_get_fd(io);
+
+	ret = read(fd, buf, sizeof(buf));
+	if (ret < 0) {
+		error("IPC: command read failed (%s)", strerror(errno));
+		goto fail;
+	}
+
+	err = ipc_handle_msg(ipc->services, ipc->service_max, buf, ret);
+	if (err < 0) {
+		error("IPC: failed to handle message (%s)", strerror(-err));
+		goto fail;
+	}
+
+	return TRUE;
+
+fail:
+	ipc_disconnect(ipc, false);
+
+	return FALSE;
+}
+
+static gboolean notif_watch_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct ipc *ipc = user_data;
+
+	info("IPC: notification socket closed");
+
+	ipc->notif_watch = 0;
+
+	ipc_disconnect(ipc, false);
+
+	return FALSE;
+}
+
+static GIOChannel *ipc_connect(const char *path, size_t size,
+					GIOFunc connect_cb, void *user_data)
+{
+	struct sockaddr_un addr;
+	GIOCondition cond;
+	GIOChannel *io;
+	int sk;
+
+	sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+	if (sk < 0) {
+		error("IPC: failed to create socket: %d (%s)", errno,
+							strerror(errno));
+		return NULL;
+	}
+
+	io = g_io_channel_unix_new(sk);
+
+	g_io_channel_set_close_on_unref(io, TRUE);
+	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+
+	memcpy(addr.sun_path, path, size);
+
+	connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+
+	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+	g_io_add_watch(io, cond, connect_cb, user_data);
+
+	return io;
+}
+
+static gboolean notif_connect_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct ipc *ipc = user_data;
+
+	DBG("");
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		error("IPC: notification socket connect failed");
+
+		ipc_disconnect(ipc, false);
+
+		return FALSE;
+	}
+
+	cond = G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+	ipc->notif_watch = g_io_add_watch(io, cond, notif_watch_cb, ipc);
+
+	cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+	ipc->cmd_watch = g_io_add_watch(ipc->cmd_io, cond, cmd_watch_cb, ipc);
+
+	info("IPC: successfully connected (with notifications)");
+
+	return FALSE;
+}
+
+static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct ipc *ipc = user_data;
+
+	DBG("");
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		error("IPC: command socket connect failed");
+		ipc_disconnect(ipc, false);
+
+		return FALSE;
+	}
+
+	if (ipc->notifications) {
+		ipc->notif_io = ipc_connect(ipc->path, ipc->size,
+							notif_connect_cb, ipc);
+		if (!ipc->notif_io)
+			ipc_disconnect(ipc, false);
+
+		return FALSE;
+	}
+
+	cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+	ipc->cmd_watch = g_io_add_watch(ipc->cmd_io, cond, cmd_watch_cb, ipc);
+
+	info("IPC: successfully connected (without notifications)");
+
+	return FALSE;
+}
+
+struct ipc *ipc_init(const char *path, size_t size, int max_service_id,
+					bool notifications,
+					ipc_disconnect_cb cb, void *cb_data)
+{
+	struct ipc *ipc;
+
+	ipc = g_new0(struct ipc, 1);
+
+	ipc->services = g_new0(struct service_handler, max_service_id + 1);
+	ipc->service_max = max_service_id;
+
+	ipc->path = path;
+	ipc->size = size;
+
+	ipc->notifications = notifications;
+
+	ipc->cmd_io = ipc_connect(path, size, cmd_connect_cb, ipc);
+	if (!ipc->cmd_io) {
+		g_free(ipc->services);
+		g_free(ipc);
+		return NULL;
+	}
+
+	ipc->disconnect_cb = cb;
+	ipc->disconnect_cb_data = cb_data;
+
+	return ipc;
+}
+
+void ipc_cleanup(struct ipc *ipc)
+{
+	ipc_disconnect(ipc, true);
+
+	g_free(ipc->services);
+	g_free(ipc);
+}
+
+static void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len,
+							void *param, int fd)
+{
+	struct msghdr msg;
+	struct iovec iv[2];
+	struct ipc_hdr m;
+	char cmsgbuf[CMSG_SPACE(sizeof(int))];
+	struct cmsghdr *cmsg;
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&m, 0, sizeof(m));
+	memset(cmsgbuf, 0, sizeof(cmsgbuf));
+
+	m.service_id = service_id;
+	m.opcode = opcode;
+	m.len = len;
+
+	iv[0].iov_base = &m;
+	iv[0].iov_len = sizeof(m);
+
+	iv[1].iov_base = param;
+	iv[1].iov_len = len;
+
+	msg.msg_iov = iv;
+	msg.msg_iovlen = 2;
+
+	if (fd >= 0) {
+		msg.msg_control = cmsgbuf;
+		msg.msg_controllen = sizeof(cmsgbuf);
+
+		cmsg = CMSG_FIRSTHDR(&msg);
+		cmsg->cmsg_level = SOL_SOCKET;
+		cmsg->cmsg_type = SCM_RIGHTS;
+		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+
+		/* Initialize the payload */
+		memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+	}
+
+	if (sendmsg(sk, &msg, 0) < 0) {
+		error("IPC send failed :%s", strerror(errno));
+
+		/* TODO disconnect IPC here when this function becomes static */
+		raise(SIGTERM);
+	}
+}
+
+void ipc_send_rsp(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+								uint8_t status)
+{
+	struct ipc_status s;
+	int sk;
+
+	sk = g_io_channel_unix_get_fd(ipc->cmd_io);
+
+	if (status == IPC_STATUS_SUCCESS) {
+		ipc_send(sk, service_id, opcode, 0, NULL, -1);
+		return;
+	}
+
+	s.code = status;
+
+	ipc_send(sk, service_id, IPC_OP_STATUS, sizeof(s), &s, -1);
+}
+
+void ipc_send_rsp_full(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+					uint16_t len, void *param, int fd)
+{
+	ipc_send(g_io_channel_unix_get_fd(ipc->cmd_io), service_id, opcode, len,
+								param, fd);
+}
+
+void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+						uint16_t len, void *param)
+{
+	return ipc_send_notif_with_fd(ipc, service_id, opcode, len, param, -1);
+}
+
+void ipc_send_notif_with_fd(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+					uint16_t len, void *param, int fd)
+{
+	if (!ipc || !ipc->notif_io)
+		return;
+
+	ipc_send(g_io_channel_unix_get_fd(ipc->notif_io), service_id, opcode,
+								len, param, fd);
+}
+
+void ipc_register(struct ipc *ipc, uint8_t service,
+			const struct ipc_handler *handlers, uint8_t size)
+{
+	if (service > ipc->service_max)
+		return;
+
+	ipc->services[service].handler = handlers;
+	ipc->services[service].size = size;
+}
+
+void ipc_unregister(struct ipc *ipc, uint8_t service)
+{
+	if (service > ipc->service_max)
+		return;
+
+	ipc->services[service].handler = NULL;
+	ipc->services[service].size = 0;
+}
diff --git a/repo/android/ipc.h b/repo/android/ipc.h
new file mode 100644
index 0000000..fd2b985
--- /dev/null
+++ b/repo/android/ipc.h
@@ -0,0 +1,50 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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 ipc_handler {
+	void (*handler) (const void *buf, uint16_t len);
+	bool var_len;
+	size_t data_len;
+};
+
+struct ipc;
+
+typedef void (*ipc_disconnect_cb) (void *data);
+
+struct ipc *ipc_init(const char *path, size_t size, int max_service_id,
+					bool notifications,
+					ipc_disconnect_cb cb, void *cb_data);
+void ipc_cleanup(struct ipc *ipc);
+
+void ipc_send_rsp(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+								uint8_t status);
+void ipc_send_rsp_full(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+					uint16_t len, void *param, int fd);
+void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+						uint16_t len, void *param);
+void ipc_send_notif_with_fd(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+					uint16_t len, void *param, int fd);
+
+void ipc_register(struct ipc *ipc, uint8_t service,
+			const struct ipc_handler *handlers, uint8_t size);
+void ipc_unregister(struct ipc *ipc, uint8_t service);
diff --git a/repo/android/log.c b/repo/android/log.c
new file mode 100644
index 0000000..35917c6
--- /dev/null
+++ b/repo/android/log.c
@@ -0,0 +1,216 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. 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
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <time.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "src/log.h"
+
+#define LOG_TAG "bluetoothd"
+
+#define LOG_DEBUG 3
+#define LOG_INFO 4
+#define LOG_WARN 5
+#define LOG_ERR 6
+
+#define LOG_ID_SYSTEM 3
+
+struct logd_header {
+	uint8_t id;
+	uint16_t pid; /* Android logd expects only 2 bytes for PID */
+	uint32_t sec;
+	uint32_t nsec;
+} __attribute__ ((packed));
+
+static int log_fd = -1;
+static bool legacy_log = false;
+
+static void android_log(unsigned char level, const char *fmt, va_list ap)
+{
+	struct logd_header header;
+	struct iovec vec[4];
+	int cnt = 0;
+	char *msg;
+	static pid_t pid = 0;
+
+	if (log_fd < 0)
+		return;
+
+	/* no need to call getpid all the time since we don't fork */
+	if (!pid)
+		pid = getpid();
+
+	if (vasprintf(&msg, fmt, ap) < 0)
+		return;
+
+	if (!legacy_log) {
+		struct timespec ts;
+
+		clock_gettime(CLOCK_REALTIME, &ts);
+
+		header.id = LOG_ID_SYSTEM;
+		header.pid = pid;
+		header.sec = ts.tv_sec;
+		header.nsec = ts.tv_nsec;
+
+		vec[0].iov_base = &header;
+		vec[0].iov_len = sizeof(header);
+
+		cnt += 1;
+	}
+
+	vec[cnt + 0].iov_base = &level;
+	vec[cnt + 0].iov_len = sizeof(level);
+	vec[cnt + 1].iov_base = LOG_TAG;
+	vec[cnt + 1].iov_len = sizeof(LOG_TAG);
+	vec[cnt + 2].iov_base  = msg;
+	vec[cnt + 2].iov_len  = strlen(msg) + 1;
+
+	cnt += 3;
+
+	writev(log_fd, vec, cnt);
+
+	free(msg);
+}
+
+void info(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+
+	android_log(LOG_INFO, format, ap);
+
+	va_end(ap);
+}
+
+void warn(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+
+	android_log(LOG_WARN, format, ap);
+
+	va_end(ap);
+}
+
+void error(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+
+	android_log(LOG_ERR, format, ap);
+
+	va_end(ap);
+}
+
+void btd_debug(uint16_t index, const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+
+	android_log(LOG_DEBUG, format, ap);
+
+	va_end(ap);
+}
+
+static bool init_legacy_log(void)
+{
+	log_fd = open("/dev/log/system", O_WRONLY);
+	if (log_fd < 0)
+		return false;
+
+	legacy_log = true;
+
+	return true;
+}
+
+static bool init_logd(void)
+{
+	struct sockaddr_un addr;
+
+	log_fd = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+	if (log_fd < 0)
+		return false;
+
+	if (fcntl(log_fd, F_SETFL, O_NONBLOCK) < 0)
+		goto failed;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	strcpy(addr.sun_path, "/dev/socket/logdw");
+
+	if (connect(log_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+		goto failed;
+
+	return true;
+
+failed:
+	close(log_fd);
+	log_fd = -1;
+
+	return false;
+}
+
+extern struct btd_debug_desc __start___debug[];
+extern struct btd_debug_desc __stop___debug[];
+
+void __btd_log_init(const char *debug, int detach)
+{
+	if (!init_logd() && !init_legacy_log())
+		return;
+
+	if (debug) {
+		struct btd_debug_desc *desc;
+
+		for (desc = __start___debug; desc < __stop___debug; desc++)
+			desc->flags |= BTD_DEBUG_FLAG_PRINT;
+	}
+
+	info("Bluetooth daemon %s", VERSION);
+}
+
+void __btd_log_cleanup(void)
+{
+	if (log_fd < 0)
+		return;
+
+	close(log_fd);
+	log_fd = -1;
+}
diff --git a/repo/android/main.c b/repo/android/main.c
new file mode 100644
index 0000000..03c8760
--- /dev/null
+++ b/repo/android/main.c
@@ -0,0 +1,805 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/signalfd.h>
+#if defined(ANDROID)
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#endif
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "src/log.h"
+#include "src/sdpd.h"
+#include "src/shared/util.h"
+
+#include "ipc-common.h"
+#include "ipc.h"
+#include "bluetooth.h"
+#include "socket.h"
+#include "hidhost.h"
+#include "hal-msg.h"
+#include "a2dp.h"
+#include "pan.h"
+#include "avrcp.h"
+#include "handsfree.h"
+#include "gatt.h"
+#include "health.h"
+#include "handsfree-client.h"
+#include "map-client.h"
+#include "utils.h"
+
+#define DEFAULT_VENDOR "BlueZ"
+#define DEFAULT_MODEL "BlueZ for Android"
+#define DEFAULT_NAME "BlueZ for Android"
+
+#define STARTUP_GRACE_SECONDS 5
+#define SHUTDOWN_GRACE_SECONDS 5
+
+static char *config_vendor = NULL;
+static char *config_model = NULL;
+static char *config_name = NULL;
+static char *config_serial = NULL;
+static char *config_fw_rev = NULL;
+static char *config_hw_rev = NULL;
+static uint64_t config_system_id = 0;
+static uint16_t config_pnp_source = 0x0002;	/* USB */
+static uint16_t config_pnp_vendor = 0x1d6b;	/* Linux Foundation */
+static uint16_t config_pnp_product = 0x0247;	/* BlueZ for Android */
+static uint16_t config_pnp_version = 0x0000;
+
+static guint quit_timeout = 0;
+
+static bdaddr_t adapter_bdaddr;
+
+static GMainLoop *event_loop;
+
+static struct ipc *hal_ipc = NULL;
+
+static bool services[HAL_SERVICE_ID_MAX + 1] = { false };
+
+const char *bt_config_get_vendor(void)
+{
+	if (config_vendor)
+		return config_vendor;
+
+	return DEFAULT_VENDOR;
+}
+
+const char *bt_config_get_name(void)
+{
+	if (config_name)
+		return config_name;
+
+	return DEFAULT_NAME;
+}
+
+const char *bt_config_get_model(void)
+{
+	if (config_model)
+		return config_model;
+
+	return DEFAULT_MODEL;
+}
+
+const char *bt_config_get_serial(void)
+{
+	return config_serial;
+}
+
+const char *bt_config_get_fw_rev(void)
+{
+	return config_fw_rev;
+}
+
+const char *bt_config_get_hw_rev(void)
+{
+	return config_hw_rev;
+}
+
+uint64_t bt_config_get_system_id(void)
+{
+	return config_system_id;
+}
+
+uint16_t bt_config_get_pnp_source(void)
+{
+	return config_pnp_source;
+}
+
+uint16_t bt_config_get_pnp_vendor(void)
+{
+	return config_pnp_vendor;
+}
+
+uint16_t bt_config_get_pnp_product(void)
+{
+	return config_pnp_product;
+}
+
+uint16_t bt_config_get_pnp_version(void)
+{
+	return config_pnp_version;
+}
+
+static void service_register(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_register_module *m = buf;
+	uint8_t status;
+
+	if (m->service_id > HAL_SERVICE_ID_MAX || services[m->service_id]) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	switch (m->service_id) {
+	case HAL_SERVICE_ID_BLUETOOTH:
+		if (!bt_bluetooth_register(hal_ipc, m->mode)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_SOCKET:
+		bt_socket_register(hal_ipc, &adapter_bdaddr, m->mode);
+
+		break;
+	case HAL_SERVICE_ID_HIDHOST:
+		if (!bt_hid_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_A2DP:
+		if (!bt_a2dp_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_PAN:
+		if (!bt_pan_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_AVRCP:
+		if (!bt_avrcp_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_HANDSFREE:
+		if (!bt_handsfree_register(hal_ipc, &adapter_bdaddr, m->mode,
+							m->max_clients)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_GATT:
+		if (!bt_gatt_register(hal_ipc, &adapter_bdaddr)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_HEALTH:
+		if (!bt_health_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_HANDSFREE_CLIENT:
+		if (!bt_hf_client_register(hal_ipc, &adapter_bdaddr)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_MAP_CLIENT:
+		if (!bt_map_client_register(hal_ipc, &adapter_bdaddr,
+								m->mode)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	default:
+		DBG("service %u not supported", m->service_id);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	services[m->service_id] = true;
+
+	status = HAL_STATUS_SUCCESS;
+
+	info("Service ID=%u registered", m->service_id);
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+								status);
+}
+
+static bool unregister_service(uint8_t id)
+{
+	if (id > HAL_SERVICE_ID_MAX || !services[id])
+		return false;
+
+	switch (id) {
+	case HAL_SERVICE_ID_BLUETOOTH:
+		bt_bluetooth_unregister();
+		break;
+	case HAL_SERVICE_ID_SOCKET:
+		bt_socket_unregister();
+		break;
+	case HAL_SERVICE_ID_HIDHOST:
+		bt_hid_unregister();
+		break;
+	case HAL_SERVICE_ID_A2DP:
+		bt_a2dp_unregister();
+		break;
+	case HAL_SERVICE_ID_PAN:
+		bt_pan_unregister();
+		break;
+	case HAL_SERVICE_ID_AVRCP:
+		bt_avrcp_unregister();
+		break;
+	case HAL_SERVICE_ID_HANDSFREE:
+		bt_handsfree_unregister();
+		break;
+	case HAL_SERVICE_ID_GATT:
+		bt_gatt_unregister();
+		break;
+	case HAL_SERVICE_ID_HEALTH:
+		bt_health_unregister();
+		break;
+	case HAL_SERVICE_ID_HANDSFREE_CLIENT:
+		bt_hf_client_unregister();
+		break;
+	case HAL_SERVICE_ID_MAP_CLIENT:
+		bt_map_client_unregister();
+		break;
+	default:
+		DBG("service %u not supported", id);
+		return false;
+	}
+
+	services[id] = false;
+
+	return true;
+}
+
+static void service_unregister(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_unregister_module *m = buf;
+	uint8_t status;
+
+	if (!unregister_service(m->service_id)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+	info("Service ID=%u unregistered", m->service_id);
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+								status);
+}
+
+static char *get_prop(char *prop, uint16_t len, const uint8_t *val)
+{
+	/* TODO should fail if set more than once ? */
+	free(prop);
+
+	prop = malloc0(len);
+	if (!prop)
+		return NULL;
+
+	memcpy(prop, val, len);
+	prop[len - 1] = '\0';
+
+	return prop;
+}
+
+static void parse_pnp_id(uint16_t len, const uint8_t *val)
+{
+	int result;
+	uint16_t vendor, product, version , source;
+	char *pnp;
+
+	/* version is optional */
+	version = config_pnp_version;
+
+	pnp = get_prop(NULL, len, val);
+	if (!pnp)
+		return;
+
+	DBG("pnp_id %s", pnp);
+
+	result = sscanf(pnp, "bluetooth:%4hx:%4hx:%4hx",
+						&vendor, &product, &version);
+	if (result != EOF && result >= 2) {
+		source = 0x0001;
+		goto done;
+	}
+
+	result = sscanf(pnp, "usb:%4hx:%4hx:%4hx", &vendor, &product, &version);
+	if (result != EOF && result >= 2) {
+		source = 0x0002;
+		goto done;
+	}
+
+	free(pnp);
+	return;
+done:
+	free(pnp);
+
+	config_pnp_source = source;
+	config_pnp_vendor = vendor;
+	config_pnp_product = product;
+	config_pnp_version = version;
+}
+
+static void parse_system_id(uint16_t len, const uint8_t *val)
+{
+	uint64_t res;
+	char *id;
+
+	id = get_prop(NULL, len, val);
+	if (!id)
+		return;
+
+	res = strtoull(id, NULL, 16);
+	if (res == ULLONG_MAX && errno == ERANGE)
+		goto done;
+
+	config_system_id = res;
+done:
+	free(id);
+}
+
+static void configuration(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_configuration *cmd = buf;
+	const struct hal_config_prop *prop;
+	unsigned int i;
+
+	buf += sizeof(*cmd);
+	len -= sizeof(*cmd);
+
+	for (i = 0; i < cmd->num; i++) {
+		prop = buf;
+
+		if (len < sizeof(*prop) || len < sizeof(*prop) + prop->len) {
+			error("Invalid configuration command, terminating");
+			raise(SIGTERM);
+			return;
+		}
+
+		switch (prop->type) {
+		case HAL_CONFIG_VENDOR:
+			config_vendor = get_prop(config_vendor, prop->len,
+								prop->val);
+			DBG("vendor %s", config_vendor);
+			break;
+		case HAL_CONFIG_NAME:
+			config_name = get_prop(config_name, prop->len,
+								prop->val);
+			DBG("name %s", config_name);
+			break;
+		case HAL_CONFIG_MODEL:
+			config_model = get_prop(config_model, prop->len,
+								prop->val);
+			DBG("model %s", config_model);
+			break;
+		case HAL_CONFIG_SERIAL_NUMBER:
+			config_serial = get_prop(config_serial, prop->len,
+								prop->val);
+			DBG("serial %s", config_serial);
+			break;
+		case HAL_CONFIG_SYSTEM_ID:
+			parse_system_id(prop->len, prop->val);
+			break;
+		case HAL_CONFIG_PNP_ID:
+			parse_pnp_id(prop->len, prop->val);
+			break;
+		case HAL_CONFIG_FW_REV:
+			config_fw_rev = get_prop(config_fw_rev, prop->len,
+								prop->val);
+			DBG("fw_rev %s", config_fw_rev);
+			break;
+		case HAL_CONFIG_HW_REV:
+			config_hw_rev = get_prop(config_hw_rev, prop->len,
+								prop->val);
+			DBG("hw_rev %s", config_hw_rev);
+			break;
+		default:
+			error("Invalid configuration option (%u), terminating",
+								prop->type);
+			raise(SIGTERM);
+			return;
+		}
+
+		buf += sizeof(*prop) + prop->len;
+		len -= sizeof(*prop) + prop->len;
+	}
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_CONFIGURATION,
+							HAL_STATUS_SUCCESS);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_REGISTER_MODULE */
+	{ service_register, false, sizeof(struct hal_cmd_register_module) },
+	/* HAL_OP_UNREGISTER_MODULE */
+	{ service_unregister, false, sizeof(struct hal_cmd_unregister_module) },
+	/* HAL_OP_CONFIGURATION */
+	{ configuration, true, sizeof(struct hal_cmd_configuration) },
+};
+
+static void bluetooth_stopped(void)
+{
+	g_main_loop_quit(event_loop);
+}
+
+static gboolean quit_eventloop(gpointer user_data)
+{
+	g_main_loop_quit(event_loop);
+
+	quit_timeout = 0;
+
+	return FALSE;
+}
+
+static void stop_bluetooth(void)
+{
+	static bool __stop = false;
+
+	if (__stop)
+		return;
+
+	__stop = true;
+
+	if (!bt_bluetooth_stop(bluetooth_stopped)) {
+		g_main_loop_quit(event_loop);
+		return;
+	}
+
+	quit_timeout = g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS,
+							quit_eventloop, NULL);
+}
+
+static void ipc_disconnected(void *data)
+{
+	stop_bluetooth();
+}
+
+static void adapter_ready(int err, const bdaddr_t *addr)
+{
+	if (err < 0) {
+		error("Adapter initialization failed: %s", strerror(-err));
+		exit(EXIT_FAILURE);
+	}
+
+	bacpy(&adapter_bdaddr, addr);
+
+	if (quit_timeout > 0) {
+		g_source_remove(quit_timeout);
+		quit_timeout = 0;
+	}
+
+	info("Adapter initialized");
+
+	hal_ipc = ipc_init(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH),
+						HAL_SERVICE_ID_MAX, true,
+						ipc_disconnected, NULL);
+	if (!hal_ipc) {
+		error("Failed to initialize IPC");
+		exit(EXIT_FAILURE);
+	}
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_CORE, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+}
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	static bool __terminated = false;
+	struct signalfd_siginfo si;
+	ssize_t result;
+	int fd;
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+		return FALSE;
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	result = read(fd, &si, sizeof(si));
+	if (result != sizeof(si))
+		return FALSE;
+
+	switch (si.ssi_signo) {
+	case SIGINT:
+	case SIGTERM:
+		if (!__terminated) {
+			info("Terminating");
+			stop_bluetooth();
+		}
+
+		__terminated = true;
+		break;
+	}
+
+	return TRUE;
+}
+
+static guint setup_signalfd(void)
+{
+	GIOChannel *channel;
+	guint source;
+	sigset_t mask;
+	int fd;
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+		perror("Failed to set signal mask");
+		return 0;
+	}
+
+	fd = signalfd(-1, &mask, 0);
+	if (fd < 0) {
+		perror("Failed to create signal descriptor");
+		return 0;
+	}
+
+	channel = g_io_channel_unix_new(fd);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				signal_handler, NULL);
+
+	g_io_channel_unref(channel);
+
+	return source;
+}
+
+static gboolean option_version = FALSE;
+static gint option_index = -1;
+static gboolean option_dbg = FALSE;
+static gboolean option_mgmt_dbg = FALSE;
+
+static GOptionEntry options[] = {
+	{ "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+				"Show version information and exit", NULL },
+	{ "index", 'i', 0, G_OPTION_ARG_INT, &option_index,
+				"Use specified controller", "INDEX"},
+	{ "debug", 'd', 0, G_OPTION_ARG_NONE, &option_dbg,
+				"Enable debug logs", NULL},
+	{ "mgmt-debug", 0, 0, G_OPTION_ARG_NONE, &option_mgmt_dbg,
+				"Enable mgmt debug logs", NULL},
+
+	{ NULL }
+};
+
+static void cleanup_services(void)
+{
+	int i;
+
+	DBG("");
+
+	for (i = HAL_SERVICE_ID_MAX; i > HAL_SERVICE_ID_CORE; i--)
+		unregister_service(i);
+}
+
+static bool set_capabilities(void)
+{
+#if defined(ANDROID)
+	struct __user_cap_header_struct header;
+	struct __user_cap_data_struct cap;
+
+	header.version = _LINUX_CAPABILITY_VERSION;
+	header.pid = 0;
+
+	/*
+	 * CAP_NET_ADMIN: Allow use of MGMT interface
+	 * CAP_NET_BIND_SERVICE: Allow use of privileged PSM
+	 * CAP_NET_RAW: Allow use of bnep ioctl calls
+	 */
+	cap.effective = cap.permitted =
+		CAP_TO_MASK(CAP_NET_RAW) |
+		CAP_TO_MASK(CAP_NET_ADMIN) |
+		CAP_TO_MASK(CAP_NET_BIND_SERVICE);
+	cap.inheritable = 0;
+
+	/* don't clear capabilities when dropping root */
+	if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+		error("%s: prctl(): %s", __func__, strerror(errno));
+		return false;
+	}
+
+	/* Android bluetooth user UID=1002 */
+	if (setuid(1002) < 0) {
+		error("%s: setuid(): %s", __func__, strerror(errno));
+		return false;
+	}
+
+	/* TODO: Move to cap_set_proc once bionic support it */
+	if (capset(&header, &cap) < 0) {
+		error("%s: capset(): %s", __func__, strerror(errno));
+		return false;
+	}
+
+	/* TODO: Move to cap_get_proc once bionic support it */
+	if (capget(&header, &cap) < 0) {
+		error("%s: capget(): %s", __func__, strerror(errno));
+		return false;
+	}
+
+	DBG("Caps: eff: 0x%x, perm: 0x%x, inh: 0x%x", cap.effective,
+					cap.permitted, cap.inheritable);
+
+#endif
+	return true;
+}
+
+static void set_version(void)
+{
+	uint8_t major, minor;
+
+	if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2)
+		return;
+
+	config_pnp_version = major << 8 | minor;
+}
+
+int main(int argc, char *argv[])
+{
+	GOptionContext *context;
+	GError *err = NULL;
+	guint signal;
+
+	set_version();
+
+	context = g_option_context_new(NULL);
+	g_option_context_add_main_entries(context, options, NULL);
+
+	if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
+		if (err != NULL) {
+			g_printerr("%s\n", err->message);
+			g_error_free(err);
+		} else
+			g_printerr("An unknown error occurred\n");
+
+		exit(EXIT_FAILURE);
+	}
+
+	g_option_context_free(context);
+
+	if (option_version == TRUE) {
+		printf("%s\n", VERSION);
+		exit(EXIT_SUCCESS);
+	}
+
+	signal = setup_signalfd();
+	if (!signal)
+		return EXIT_FAILURE;
+
+	if (option_dbg || option_mgmt_dbg)
+		__btd_log_init("*", 0);
+	else
+		__btd_log_init(NULL, 0);
+
+	if (!set_capabilities()) {
+		__btd_log_cleanup();
+		g_source_remove(signal);
+		return EXIT_FAILURE;
+	}
+
+	quit_timeout = g_timeout_add_seconds(STARTUP_GRACE_SECONDS,
+							quit_eventloop, NULL);
+	if (quit_timeout == 0) {
+		error("Failed to init startup timeout");
+		__btd_log_cleanup();
+		g_source_remove(signal);
+		return EXIT_FAILURE;
+	}
+
+	if (!bt_bluetooth_start(option_index, option_mgmt_dbg, adapter_ready)) {
+		__btd_log_cleanup();
+		g_source_remove(quit_timeout);
+		g_source_remove(signal);
+		return EXIT_FAILURE;
+	}
+
+	/* Use params: mtu = 0, flags = 0 */
+	start_sdp_server(0, 0);
+
+	DBG("Entering main loop");
+
+	event_loop = g_main_loop_new(NULL, FALSE);
+
+	g_main_loop_run(event_loop);
+
+	g_source_remove(signal);
+
+	if (quit_timeout > 0)
+		g_source_remove(quit_timeout);
+
+	cleanup_services();
+
+	stop_sdp_server();
+	bt_bluetooth_cleanup();
+	g_main_loop_unref(event_loop);
+
+	/* If no adapter was initialized, hal_ipc is NULL */
+	if (hal_ipc) {
+		ipc_unregister(hal_ipc, HAL_SERVICE_ID_CORE);
+		ipc_cleanup(hal_ipc);
+	}
+
+	info("Exit");
+
+	__btd_log_cleanup();
+
+	free(config_vendor);
+	free(config_model);
+	free(config_name);
+	free(config_serial);
+	free(config_fw_rev);
+	free(config_hw_rev);
+
+	return EXIT_SUCCESS;
+}
diff --git a/repo/android/map-client.c b/repo/android/map-client.c
new file mode 100644
index 0000000..e3ad148
--- /dev/null
+++ b/repo/android/map-client.c
@@ -0,0 +1,203 @@
+/*
+ *
+ *  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <glib.h>
+
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+
+#include "ipc.h"
+#include "lib/bluetooth.h"
+#include "map-client.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "utils.h"
+#include "src/shared/util.h"
+
+static struct ipc *hal_ipc = NULL;
+static bdaddr_t adapter_addr;
+
+static int fill_mce_inst(void *buf, int32_t id, int32_t scn, int32_t msg_type,
+					const void *name, uint8_t name_len)
+{
+	struct hal_map_client_mas_instance *inst = buf;
+
+	inst->id = id;
+	inst->scn = scn;
+	inst->msg_types = msg_type;
+	inst->name_len = name_len;
+
+	if (name_len)
+		memcpy(inst->name, name, name_len);
+
+	return sizeof(*inst) + name_len;
+}
+
+static void map_client_sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_map_client_remote_mas_instances *ev = (void *) buf;
+	bdaddr_t *dst = data;
+	sdp_list_t *list, *protos;
+	uint8_t status;
+	int32_t id, scn, msg_type, name_len, num_instances = 0;
+	char *name;
+	size_t size;
+
+	size = sizeof(*ev);
+	bdaddr2android(dst, &ev->bdaddr);
+
+	if (err < 0) {
+		error("mce: Unable to get SDP record: %s", strerror(-err));
+		status = HAL_STATUS_FAILED;
+		goto fail;
+	}
+
+	for (list = recs; list != NULL; list = list->next) {
+		sdp_record_t *rec = list->data;
+		sdp_data_t *data;
+
+		data = sdp_data_get(rec, SDP_ATTR_MAS_INSTANCE_ID);
+		if (!data) {
+			error("mce: cannot get mas instance id");
+			continue;
+		}
+
+		id = data->val.uint8;
+
+		data = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY);
+		if (!data) {
+			error("mce: cannot get mas instance name");
+			continue;
+		}
+
+		name = data->val.str;
+		name_len = data->unitSize;
+
+		data = sdp_data_get(rec, SDP_ATTR_SUPPORTED_MESSAGE_TYPES);
+		if (!data) {
+			error("mce: cannot get mas instance msg type");
+			continue;
+		}
+
+		msg_type = data->val.uint8;
+
+		if (sdp_get_access_protos(rec, &protos) < 0) {
+			error("mce: cannot get mas instance sdp protocol list");
+			continue;
+		}
+
+		scn = sdp_get_proto_port(protos, RFCOMM_UUID);
+
+		sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+		sdp_list_free(protos, NULL);
+
+		if (!scn) {
+			error("mce: cannot get mas instance rfcomm channel");
+			continue;
+		}
+
+		size += fill_mce_inst(buf + size, id, scn, msg_type, name,
+								name_len);
+		num_instances++;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+fail:
+	ev->num_instances = num_instances;
+	ev->status = status;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT,
+			HAL_EV_MAP_CLIENT_REMOTE_MAS_INSTANCES, size, buf);
+}
+
+static void handle_get_instances(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_map_client_get_instances *cmd = buf;
+	uint8_t status;
+	bdaddr_t *dst;
+	uuid_t uuid;
+
+	DBG("");
+
+	dst = new0(bdaddr_t, 1);
+	if (!dst) {
+		error("mce: Fail to allocate cb data");
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	android2bdaddr(&cmd->bdaddr, dst);
+	sdp_uuid16_create(&uuid, MAP_MSE_SVCLASS_ID);
+
+	if (bt_search_service(&adapter_addr, dst, &uuid,
+				map_client_sdp_search_cb, dst, free, 0)) {
+		error("mce: Failed to search SDP details");
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT,
+				HAL_OP_MAP_CLIENT_GET_INSTANCES, status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_MAP_CLIENT_GET_INSTANCES */
+	{ handle_get_instances, false,
+			sizeof(struct hal_cmd_map_client_get_instances) },
+};
+
+bool bt_map_client_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	DBG("");
+
+	bacpy(&adapter_addr, addr);
+
+	hal_ipc = ipc;
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+}
+
+void bt_map_client_unregister(void)
+{
+	DBG("");
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT);
+	hal_ipc = NULL;
+}
diff --git a/repo/android/map-client.h b/repo/android/map-client.h
new file mode 100644
index 0000000..0e63072
--- /dev/null
+++ b/repo/android/map-client.h
@@ -0,0 +1,26 @@
+/*
+ *
+ *  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
+ *
+ */
+
+bool bt_map_client_register(struct ipc *ipc, const bdaddr_t *addr,
+								uint8_t mode);
+void bt_map_client_unregister(void);
diff --git a/repo/android/pan.c b/repo/android/pan.c
new file mode 100644
index 0000000..c40a6d3
--- /dev/null
+++ b/repo/android/pan.c
@@ -0,0 +1,903 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <glib.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <linux/if_bridge.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/bnep.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/uuid-helper.h"
+#include "profiles/network/bnep.h"
+#include "src/log.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "utils.h"
+#include "bluetooth.h"
+#include "pan.h"
+
+#define SVC_HINT_NETWORKING 0x02
+
+#define BNEP_BRIDGE "bt-pan"
+#define BNEP_PANU_INTERFACE "bt-pan"
+#define BNEP_NAP_INTERFACE "bt-pan%d"
+
+struct pan_device {
+	char		iface[16];
+	bdaddr_t	dst;
+	uint8_t		conn_state;
+	uint8_t		role;
+	GIOChannel	*io;
+	struct bnep	*session;
+	guint		watch;
+};
+
+static bdaddr_t adapter_addr;
+static GSList *devices = NULL;
+static uint8_t local_role = HAL_PAN_ROLE_NONE;
+static uint32_t nap_rec_id = 0;
+static uint32_t panu_rec_id = 0;
+static GIOChannel *nap_io = NULL;
+static bool nap_bridge_mode = false;
+static struct ipc *hal_ipc = NULL;
+
+static int set_forward_delay(int sk)
+{
+	unsigned long args[4] = { BRCTL_SET_BRIDGE_FORWARD_DELAY, 0 , 0, 0 };
+	struct ifreq ifr;
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, BNEP_BRIDGE, IFNAMSIZ - 1);
+	ifr.ifr_data = (char *) args;
+
+	if (ioctl(sk, SIOCDEVPRIVATE, &ifr) < 0) {
+		error("pan: setting forward delay failed: %d (%s)",
+							errno, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int nap_create_bridge(void)
+{
+	int sk, err;
+
+	DBG("%s", BNEP_BRIDGE);
+
+	if (nap_bridge_mode)
+		return 0;
+
+	sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+	if (sk < 0)
+		return -EOPNOTSUPP;
+
+	if (ioctl(sk, SIOCBRADDBR, BNEP_BRIDGE) < 0) {
+		err = -errno;
+		if (err != -EEXIST) {
+			close(sk);
+			return -EOPNOTSUPP;
+		}
+	}
+
+	err = set_forward_delay(sk);
+	if (err < 0)
+		ioctl(sk, SIOCBRDELBR, BNEP_BRIDGE);
+
+	close(sk);
+
+	nap_bridge_mode = err == 0;
+
+	return err;
+}
+
+static int bridge_if_down(void)
+{
+	struct ifreq ifr;
+	int sk, err;
+
+	sk = socket(AF_INET, SOCK_DGRAM, 0);
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, BNEP_BRIDGE, IF_NAMESIZE - 1);
+
+	ifr.ifr_flags &= ~IFF_UP;
+
+	/* Bring down the interface */
+	err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr);
+
+	close(sk);
+
+	if (err < 0) {
+		error("pan: Could not bring down %s", BNEP_BRIDGE);
+		return err;
+	}
+
+	return 0;
+}
+
+static int nap_remove_bridge(void)
+{
+	int sk, err;
+
+	DBG("%s", BNEP_BRIDGE);
+
+	if (!nap_bridge_mode)
+		return 0;
+
+	bridge_if_down();
+
+	sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+	if (sk < 0)
+		return -EOPNOTSUPP;
+
+	err = ioctl(sk, SIOCBRDELBR, BNEP_BRIDGE);
+	if (err < 0)
+		err = -errno;
+
+	close(sk);
+
+	if (err < 0)
+		return err;
+
+	nap_bridge_mode = false;
+
+	return 0;
+}
+
+static int device_cmp(gconstpointer s, gconstpointer user_data)
+{
+	const struct pan_device *dev = s;
+	const bdaddr_t *dst = user_data;
+
+	return bacmp(&dev->dst, dst);
+}
+
+static void pan_device_free(void *data)
+{
+	struct pan_device *dev = data;
+
+	if (dev->watch > 0) {
+		bnep_server_delete(BNEP_BRIDGE, dev->iface, &dev->dst);
+		g_source_remove(dev->watch);
+	}
+
+	if (dev->io) {
+		g_io_channel_shutdown(dev->io, FALSE, NULL);
+		g_io_channel_unref(dev->io);
+	}
+
+	if (dev->session)
+		bnep_free(dev->session);
+
+	g_free(dev);
+}
+
+static void pan_device_remove(struct pan_device *dev)
+{
+	devices = g_slist_remove(devices, dev);
+
+	if (g_slist_length(devices) == 0) {
+		local_role = HAL_PAN_ROLE_NONE;
+		nap_remove_bridge();
+	}
+
+	pan_device_free(dev);
+}
+
+static void bt_pan_notify_conn_state(struct pan_device *dev, uint8_t state)
+{
+	struct hal_ev_pan_conn_state ev;
+	char addr[18];
+
+	if (dev->conn_state == state)
+		return;
+
+	dev->conn_state = state;
+	ba2str(&dev->dst, addr);
+	DBG("device %s state %u", addr, state);
+
+	bdaddr2android(&dev->dst, ev.bdaddr);
+	ev.state = state;
+	ev.local_role = local_role;
+	ev.remote_role = dev->role;
+	ev.status = HAL_STATUS_SUCCESS;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_PAN, HAL_EV_PAN_CONN_STATE,
+							sizeof(ev), &ev);
+	if (dev->conn_state == HAL_PAN_STATE_DISCONNECTED)
+		pan_device_remove(dev);
+}
+
+static void bt_pan_notify_ctrl_state(struct pan_device *dev, uint8_t state,
+								uint8_t status)
+{
+	struct hal_ev_pan_ctrl_state ev;
+
+	DBG("");
+
+	ev.state = state;
+	ev.local_role = local_role;
+	ev.status = status;
+
+	memset(ev.name, 0, sizeof(ev.name));
+
+	if (local_role == HAL_PAN_ROLE_NAP)
+		memcpy(ev.name, BNEP_BRIDGE, sizeof(BNEP_BRIDGE));
+	else if (local_role == HAL_PAN_ROLE_PANU)
+		memcpy(ev.name, dev->iface, sizeof(dev->iface));
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_PAN, HAL_EV_PAN_CTRL_STATE,
+							sizeof(ev), &ev);
+}
+
+static void bnep_disconn_cb(void *data)
+{
+	struct pan_device *dev = data;
+
+	DBG("%s disconnected", dev->iface);
+
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+}
+
+static void bnep_conn_cb(char *iface, int err, void *data)
+{
+	struct pan_device *dev = data;
+
+	DBG("");
+
+	if (err < 0) {
+		error("bnep connect req failed: %s", strerror(-err));
+		bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+		return;
+	}
+
+	memcpy(dev->iface, iface, sizeof(dev->iface));
+
+	DBG("%s connected", dev->iface);
+
+	bt_pan_notify_ctrl_state(dev, HAL_PAN_CTRL_ENABLED, HAL_STATUS_SUCCESS);
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED);
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+	struct pan_device *dev = data;
+	uint16_t l_role, r_role;
+	int perr, sk;
+
+	DBG("");
+
+	if (err) {
+		error("%s", err->message);
+		goto fail;
+	}
+
+	l_role = (local_role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP :
+								BNEP_SVC_PANU;
+	r_role = (dev->role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : BNEP_SVC_PANU;
+
+	sk = g_io_channel_unix_get_fd(dev->io);
+
+	dev->session = bnep_new(sk, l_role, r_role, BNEP_PANU_INTERFACE);
+	if (!dev->session)
+		goto fail;
+
+	perr = bnep_connect(dev->session, bnep_conn_cb, bnep_disconn_cb, dev,
+									dev);
+	if (perr < 0) {
+		error("bnep connect req failed: %s", strerror(-perr));
+		goto fail;
+	}
+
+	if (dev->io) {
+		g_io_channel_unref(dev->io);
+		dev->io = NULL;
+	}
+
+	return;
+
+fail:
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+}
+
+static void bt_pan_connect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_pan_connect *cmd = buf;
+	struct pan_device *dev;
+	uint8_t status;
+	bdaddr_t dst;
+	char addr[18];
+	GSList *l;
+	GError *gerr = NULL;
+
+	DBG("");
+
+	switch (cmd->local_role) {
+	case HAL_PAN_ROLE_NAP:
+		if (cmd->remote_role != HAL_PAN_ROLE_PANU) {
+			status = HAL_STATUS_UNSUPPORTED;
+			goto failed;
+		}
+		break;
+	case HAL_PAN_ROLE_PANU:
+		if (cmd->remote_role != HAL_PAN_ROLE_NAP &&
+					cmd->remote_role != HAL_PAN_ROLE_PANU) {
+			status = HAL_STATUS_UNSUPPORTED;
+			goto failed;
+		}
+		break;
+	default:
+		status = HAL_STATUS_UNSUPPORTED;
+		goto failed;
+	}
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = g_new0(struct pan_device, 1);
+	bacpy(&dev->dst, &dst);
+	local_role = cmd->local_role;
+	dev->role = cmd->remote_role;
+
+	ba2str(&dev->dst, addr);
+	DBG("connecting to %s %s", addr, dev->iface);
+
+	dev->io = bt_io_connect(connect_cb, dev, NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_DEST_BDADDR, &dev->dst,
+					BT_IO_OPT_PSM, BNEP_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_OMTU, BNEP_MTU,
+					BT_IO_OPT_IMTU, BNEP_MTU,
+					BT_IO_OPT_INVALID);
+	if (!dev->io) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		g_free(dev);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	devices = g_slist_append(devices, dev);
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, status);
+}
+
+static void bt_pan_disconnect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_pan_disconnect *cmd = buf;
+	struct pan_device *dev;
+	uint8_t status;
+	GSList *l;
+	bdaddr_t dst;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+
+	if (dev->conn_state == HAL_PAN_STATE_CONNECTED && dev->session)
+		bnep_disconnect(dev->session);
+
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT,
+									status);
+}
+
+static gboolean nap_watchdog_cb(GIOChannel *chan, GIOCondition cond,
+							gpointer user_data)
+{
+	struct pan_device *dev = user_data;
+
+	DBG("disconnected");
+
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+
+	return FALSE;
+}
+
+static gboolean nap_setup_cb(GIOChannel *chan, GIOCondition cond,
+							gpointer user_data)
+{
+	struct pan_device *dev = user_data;
+	uint8_t packet[BNEP_MTU];
+	int sk, n, err;
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+		error("Hangup or error or inval on BNEP socket");
+		return FALSE;
+	}
+
+	sk = g_io_channel_unix_get_fd(chan);
+
+	/*
+	 * BNEP_SETUP_CONNECTION_REQUEST_MSG should be read and left in case
+	 * of kernel setup connection msg handling.
+	 */
+	n = recv(sk, packet, sizeof(packet), MSG_PEEK);
+	if (n  < 0) {
+		error("read(): %s(%d)", strerror(errno), errno);
+		goto failed;
+	}
+
+	if (n < 3) {
+		error("pan: to few setup connection request data received");
+		goto failed;
+	}
+
+	err = nap_create_bridge();
+	if (err < 0)
+		error("pan: Failed to create bridge: %s (%d)", strerror(-err),
+									-err);
+
+	if (bnep_server_add(sk, (err < 0) ? NULL : BNEP_BRIDGE, dev->iface,
+						&dev->dst, packet, n) < 0) {
+		error("pan: server_connadd failed");
+		goto failed;
+	}
+
+	dev->watch = g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+							nap_watchdog_cb, dev);
+	g_io_channel_unref(dev->io);
+	dev->io = NULL;
+
+	bt_pan_notify_ctrl_state(dev, HAL_PAN_CTRL_ENABLED, HAL_STATUS_SUCCESS);
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED);
+
+	return FALSE;
+
+failed:
+	pan_device_remove(dev);
+
+	return FALSE;
+}
+
+static void nap_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct pan_device *dev = user_data;
+
+	DBG("");
+
+	if (err) {
+		error("%s", err->message);
+		bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+		return;
+	}
+
+	g_io_channel_set_close_on_unref(chan, TRUE);
+	dev->watch = g_io_add_watch(chan,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				nap_setup_cb, dev);
+}
+
+static void nap_confirm_cb(GIOChannel *chan, gpointer data)
+{
+	struct pan_device *dev;
+	bdaddr_t dst;
+	char address[18];
+	GError *err = NULL;
+
+	DBG("");
+
+	bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		return;
+	}
+
+	DBG("incoming connect request from %s", address);
+	dev = g_new0(struct pan_device, 1);
+	bacpy(&dev->dst, &dst);
+	local_role = HAL_PAN_ROLE_NAP;
+	dev->role = HAL_PAN_ROLE_PANU;
+
+	strncpy(dev->iface, BNEP_NAP_INTERFACE, 16);
+	dev->iface[15] = '\0';
+
+	dev->io = g_io_channel_ref(chan);
+	g_io_channel_set_close_on_unref(dev->io, TRUE);
+
+	if (!bt_io_accept(dev->io, nap_connect_cb, dev, NULL, &err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
+		goto failed;
+	}
+
+	devices = g_slist_append(devices, dev);
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING);
+
+	return;
+
+failed:
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+}
+
+static void destroy_nap_device(void)
+{
+	DBG("");
+
+	nap_remove_bridge();
+
+	if (nap_io) {
+		g_io_channel_shutdown(nap_io, FALSE, NULL);
+		g_io_channel_unref(nap_io);
+		nap_io = NULL;
+	}
+}
+
+static int register_nap_server(void)
+{
+	GError *gerr = NULL;
+
+	DBG("");
+
+	nap_io = bt_io_listen(NULL, nap_confirm_cb, NULL, NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_PSM, BNEP_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_OMTU, BNEP_MTU,
+					BT_IO_OPT_IMTU, BNEP_MTU,
+					BT_IO_OPT_INVALID);
+
+	if (!nap_io) {
+		destroy_nap_device();
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void bt_pan_enable(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_pan_enable *cmd = buf;
+	uint8_t status, state;
+	int err;
+
+	DBG("");
+
+	if (local_role == cmd->local_role) {
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	/* destroy existing server */
+	destroy_nap_device();
+
+	switch (cmd->local_role) {
+	case HAL_PAN_ROLE_NAP:
+		break;
+	case HAL_PAN_ROLE_NONE:
+		local_role = HAL_PAN_ROLE_NONE;
+		status = HAL_STATUS_SUCCESS;
+		state = HAL_PAN_CTRL_DISABLED;
+		goto notify;
+	default:
+		status = HAL_STATUS_UNSUPPORTED;
+		goto reply;
+	}
+
+	local_role = cmd->local_role;
+	err = register_nap_server();
+	if (err < 0) {
+		status = HAL_STATUS_FAILED;
+		destroy_nap_device();
+		goto reply;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+	state = HAL_PAN_CTRL_ENABLED;
+
+notify:
+	bt_pan_notify_ctrl_state(NULL, state, status);
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, status);
+}
+
+static void bt_pan_get_role(const void *buf, uint16_t len)
+{
+	struct hal_rsp_pan_get_role rsp;
+
+	DBG("");
+
+	rsp.local_role = local_role;
+	ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE,
+							sizeof(rsp), &rsp, -1);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_PAN_ENABLE */
+	{ bt_pan_enable, false, sizeof(struct hal_cmd_pan_enable) },
+	/* HAL_OP_PAN_GET_ROLE */
+	{ bt_pan_get_role, false, 0 },
+	/* HAL_OP_PAN_CONNECT */
+	{ bt_pan_connect, false, sizeof(struct hal_cmd_pan_connect) },
+	/* HAL_OP_PAN_DISCONNECT */
+	{ bt_pan_disconnect, false, sizeof(struct hal_cmd_pan_disconnect) },
+};
+
+static sdp_record_t *nap_record(void)
+{
+	sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+	uuid_t root_uuid, nap, l2cap, bnep;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *proto[2];
+	sdp_data_t *v, *p;
+	uint16_t psm = BNEP_PSM, version = 0x0100;
+	uint16_t security = 0x0001, type = 0xfffe;
+	uint32_t rate = 0;
+	const char *desc = "Network Access Point", *name = "Network Service";
+	sdp_record_t *record;
+	uint16_t ptype[] = { 0x0800, /* IPv4 */ 0x0806,  /* ARP */ };
+	sdp_data_t *head, *pseq, *data;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	record->attrlist = NULL;
+	record->pattern = NULL;
+
+	sdp_uuid16_create(&nap, NAP_SVCLASS_ID);
+	svclass = sdp_list_append(NULL, &nap);
+	sdp_set_service_classes(record, svclass);
+
+	sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(NULL, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+	sdp_set_info_attr(record, name, NULL, desc);
+	sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &type);
+	sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
+							SDP_UINT32, &rate);
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	p = sdp_data_alloc(SDP_UINT16, &psm);
+	proto[0] = sdp_list_append(proto[0], p);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&bnep, BNEP_UUID);
+	proto[1] = sdp_list_append(NULL, &bnep);
+	v = sdp_data_alloc(SDP_UINT16, &version);
+	proto[1] = sdp_list_append(proto[1], v);
+
+	head = sdp_data_alloc(SDP_UINT16, &ptype[0]);
+	data = sdp_data_alloc(SDP_UINT16, &ptype[1]);
+	sdp_seq_append(head, data);
+
+	pseq = sdp_data_alloc(SDP_SEQ16, head);
+	proto[1] = sdp_list_append(proto[1], pseq);
+	apseq = sdp_list_append(apseq, proto[1]);
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+	sdp_add_lang_attr(record);
+	sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security);
+
+	sdp_data_free(p);
+	sdp_data_free(v);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(svclass, NULL);
+	sdp_list_free(pfseq, NULL);
+
+	return record;
+}
+
+static sdp_record_t *panu_record(void)
+{
+	sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+	uuid_t root_uuid, panu, l2cap, bnep;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *proto[2];
+	sdp_data_t *v, *p;
+	uint16_t psm = BNEP_PSM, version = 0x0100;
+	uint16_t security = 0x0001, type = 0xfffe;
+	uint32_t rate = 0;
+	const char *desc = "PAN User", *name = "Network Service";
+	sdp_record_t *record;
+	uint16_t ptype[] = { 0x0800, /* IPv4 */ 0x0806,  /* ARP */ };
+	sdp_data_t *head, *pseq, *data;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	record->attrlist = NULL;
+	record->pattern = NULL;
+
+	sdp_uuid16_create(&panu, PANU_SVCLASS_ID);
+	svclass = sdp_list_append(NULL, &panu);
+	sdp_set_service_classes(record, svclass);
+
+	sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(NULL, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+	sdp_set_info_attr(record, name, NULL, desc);
+	sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &type);
+	sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
+							SDP_UINT32, &rate);
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	p = sdp_data_alloc(SDP_UINT16, &psm);
+	proto[0] = sdp_list_append(proto[0], p);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&bnep, BNEP_UUID);
+	proto[1] = sdp_list_append(NULL, &bnep);
+	v = sdp_data_alloc(SDP_UINT16, &version);
+	proto[1] = sdp_list_append(proto[1], v);
+
+	head = sdp_data_alloc(SDP_UINT16, &ptype[0]);
+	data = sdp_data_alloc(SDP_UINT16, &ptype[1]);
+	sdp_seq_append(head, data);
+
+	pseq = sdp_data_alloc(SDP_SEQ16, head);
+	proto[1] = sdp_list_append(proto[1], pseq);
+	apseq = sdp_list_append(apseq, proto[1]);
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+	sdp_add_lang_attr(record);
+	sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security);
+
+	sdp_data_free(p);
+	sdp_data_free(v);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(svclass, NULL);
+	sdp_list_free(pfseq, NULL);
+
+	return record;
+}
+
+bool bt_pan_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	sdp_record_t *nap_rec, *panu_rec;
+	int err;
+
+	DBG("");
+
+	bacpy(&adapter_addr, addr);
+
+	nap_rec = nap_record();
+	if (bt_adapter_add_record(nap_rec, SVC_HINT_NETWORKING) < 0) {
+		sdp_record_free(nap_rec);
+		error("Failed to allocate PAN-NAP sdp record");
+		return false;
+	}
+
+	panu_rec = panu_record();
+	if (bt_adapter_add_record(panu_rec, SVC_HINT_NETWORKING) < 0) {
+		sdp_record_free(nap_rec);
+		sdp_record_free(panu_rec);
+		error("Failed to allocate PAN-PANU sdp record");
+		return false;
+	}
+
+	err = bnep_init();
+	if (err < 0) {
+		error("Failed to init BNEP");
+		bt_adapter_remove_record(nap_rec->handle);
+		bt_adapter_remove_record(panu_rec->handle);
+		return false;
+	}
+
+	err = register_nap_server();
+	if (err < 0) {
+		error("Failed to register NAP server");
+		bt_adapter_remove_record(nap_rec->handle);
+		bt_adapter_remove_record(panu_rec->handle);
+		bnep_cleanup();
+		return false;
+	}
+
+	nap_rec_id = nap_rec->handle;
+	panu_rec_id = panu_rec->handle;
+
+	hal_ipc = ipc;
+	ipc_register(hal_ipc, HAL_SERVICE_ID_PAN, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+}
+
+void bt_pan_unregister(void)
+{
+	DBG("");
+
+	g_slist_free_full(devices, pan_device_free);
+	devices = NULL;
+	local_role = HAL_PAN_ROLE_NONE;
+
+	bnep_cleanup();
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_PAN);
+	hal_ipc = NULL;
+
+	bt_adapter_remove_record(nap_rec_id);
+	nap_rec_id = 0;
+	bt_adapter_remove_record(panu_rec_id);
+	panu_rec_id = 0;
+	destroy_nap_device();
+}
diff --git a/repo/android/pan.h b/repo/android/pan.h
new file mode 100644
index 0000000..cfbea96
--- /dev/null
+++ b/repo/android/pan.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-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
+ *
+ */
+
+bool bt_pan_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_pan_unregister(void);
diff --git a/repo/android/pics-a2dp.txt b/repo/android/pics-a2dp.txt
new file mode 100644
index 0000000..2e87104
--- /dev/null
+++ b/repo/android/pics-a2dp.txt
@@ -0,0 +1,162 @@
+A2DP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+		Profile Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_0_1	False		A2DP 1.0 (C.1)
+TSPC_A2DP_0_2	False		A2DP 1.2 (C.1)
+TSPC_A2DP_0_3	True (*)	A2DP 1.3 (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to select one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_1_1	True (*)	Role: Source (C.1)
+TSPC_A2DP_1_2	False		Role: Sink (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		A2DP SRC Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_2_1	True		SRC: Initiate connection establishment (M)
+TSPC_A2DP_2_2	True		SRC: Accept connection establishment (M)
+TSPC_A2DP_2_3	True		SRC: Initiate start streaming (M)
+TSPC_A2DP_2_4	True		SRC: Accept start streaming (M)
+TSPC_A2DP_2_5	True		SRC: Send audio stream (M)
+TSPC_A2DP_2_6	True		SRC: Initiate connection release (M)
+TSPC_A2DP_2_7	True		SRC: Accept connection release (M)
+TSPC_A2DP_2_8	True (*)	SRC: Initiate suspend (O)
+TSPC_A2DP_2_9	True (*)	SRC: Accept suspend (O)
+TSPC_A2DP_2_10	True		SRC: SBC encoder (M)
+TSPC_A2DP_2_10a	False		SRC: Encode and Forward Audio Stream (O)
+TSPC_A2DP_2_11	False		SRC: SBC Configurations in 16 KHz sampling (O)
+TSPC_A2DP_2_12	False		SRC: SBC Configurations in 32 KHz sampling (O)
+TSPC_A2DP_2_13	True (*)	SRC: SBC Configurations in 44.1 KHz sampling
+					(C.1)
+TSPC_A2DP_2_14	True (*)	SRC: SBC Configurations in 48 KHz sampling (C.1)
+TSPC_A2DP_2_15	False		SRC: Delay Reporting (C.2)
+TSPC_A2DP_2_16	False		SRC: SRC video playback via Bluetooth VDP (C.3)
+TSPC_A2DP_2_17	False		SRC: SRC video playback on a local video
+					display (C.3)
+-------------------------------------------------------------------------------
+C.1: At least one of the values shall be supported.
+C.2: Mandatory if A2DP 0/3 AND (2/16 OR 2/17) is supported, otherwise excluded.
+C.3: Optional to support if A2DP 0/3 is supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+		Supported Codecs in SRC
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_3_1	True		SRC: SBC encoder (M)
+TSPC_A2DP_3_1a	False		SRC: Encode and Forward SBC Audio Stream (O)
+TSPC_A2DP_3_2	False		SRC: Optional codec (O)
+TSPC_A2DP_3_3	False		SRC: MPEG-1,2 Audio decoder (C.1)
+TSPC_A2DP_3_4	False		SRC: MPEG-1,2 Audio encoder (C.1)
+TSPC_A2DP_3_5	False		SRC: MPEG-2,4 AAC decoder (C.1)
+TSPC_A2DP_3_6	False		SRC: MPEG-2,4 AAC encoder (C.1)
+TSPC_A2DP_3_7	False		SRC: ATRAC family decoder (C.1)
+TSPC_A2DP_3_8	False		SRC: ATRAC family encoder (C.1)
+-------------------------------------------------------------------------------
+C.1: At least one of the implementations shall be supported if 3/2
+	is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+		Supported Codec Features in SRC
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_3a_1	True 		SRC: Channel Mode - Mono (M)
+TSPC_A2DP_3a_2	True (*)	SRC: Channel Mode - Dual Channel (C.1)
+TSPC_A2DP_3a_3	True (*)	SRC: Channel Mode - Stereo (C.1)
+TSPC_A2DP_3a_4	True (*)	SRC: Channel Mode - Joint Stereo (C.1)
+TSPC_A2DP_3a_5	True		SRC: Block Length 4 (M)
+TSPC_A2DP_3a_6	True		SRC: Block Length 8 (M)
+TSPC_A2DP_3a_7	True		SRC: Block Length 12 (M)
+TSPC_A2DP_3a_8	True		SRC: Block Length 16 (M)
+TSPC_A2DP_3a_9	True (*)	SRC: Subbands - 4 (O)
+TSPC_A2DP_3a_10	True		SRC: Subbands - 8 (M)
+TSPC_A2DP_3a_11	True (*)	SRC: Allocation - SNR (O)
+TSPC_A2DP_3a_12	True		SRC: Allocation - Loudness (M)
+-------------------------------------------------------------------------------
+C.1: At least one of the values shall be supported.
+-------------------------------------------------------------------------------
+
+
+		A2DP Sink Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_4_1	False		SNK: Initiate connection establishment (O)
+TSPC_A2DP_4_2	False (*)	SNK: Accept connection establishment (M)
+TSPC_A2DP_4_3	False		SNK: Initiate start streaming (O)
+TSPC_A2DP_4_4	False (*)	SNK: Accept start streaming (M)
+TSPC_A2DP_4_5	False (*)	SNK: Receive audio stream (M)
+TSPC_A2DP_4_6	False		SNK: Initiate connection release (O)
+TSPC_A2DP_4_7	False (*)	SNK: Accept connection release (M)
+TSPC_A2DP_4_8	False		SNK: Initiate suspend (O)
+TSPC_A2DP_4_9	False		SNK: Accept suspend (O)
+TSPC_A2DP_4_10	False (*)	SNK: SBC decoder (M)
+TSPC_A2DP_4_10a	False		SNK: Decode and Forward Audio Stream (O)
+TSPC_A2DP_4_11	False		SNK: SBC Configurations in 16 KHz sampling (O)
+TSPC_A2DP_4_12	False		SNK: SBC Configurations in 32 KHz sampling (O)
+TSPC_A2DP_4_13	False (*)	SNK: SBC Configurations in 44.1 KHz sampling (M)
+TSPC_A2DP_4_14	False (*)	SNK: SBC Configurations in 48 KHz sampling (M)
+TSPC_A2DP_4_15	False		SNK: Delay Reporting (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support if A2DP 0/3 is supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+		Supported codecs in SNK
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_5_1	False (*)	SNK: SBC decoder (M)
+TSPC_A2DP_5_1a	False		SNK: Decode and Forward SBC Audio Stream (O)
+TSPC_A2DP_5_2	False		SNK: Optional codec decoder (O)
+TSPC_A2DP_5_3	False		SNK: MPEG-1,2 Audio (C.1)
+TSPC_A2DP_5_4	False		SNK: MPEG-2,4 AAC (C.1)
+TSPC_A2DP_5_5	False		SNK: ATRAC family (C.1)
+-------------------------------------------------------------------------------
+C.1: At least one codec shall be supported if Table 5/2 is supported, otherwise
+	excluded.
+-------------------------------------------------------------------------------
+
+
+		Supported Codec Features in SNK
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_5a_1	False (*)	SNK: Channel Mode - Mono (M)
+TSPC_A2DP_5a_2	False (*)	SNK: Channel Mode - Dual Channel (M)
+TSPC_A2DP_5a_3	False (*)	SNK: Channel Mode - Stereo (M)
+TSPC_A2DP_5a_4	False (*)	SNK: Channel Mode - Joint Stereo (M)
+TSPC_A2DP_5a_5	False (*)	SNK: Block Length 4 (M)
+TSPC_A2DP_5a_6	False (*)	SNK: Block Length 8 (M)
+TSPC_A2DP_5a_7	False (*)	SNK: Block Length 12 (M)
+TSPC_A2DP_5a_8	False (*)	SNK: Block Length 16 (M)
+TSPC_A2DP_5a_9	False (*)	SNK: Subbands - 4 (M)
+TSPC_A2DP_5a_10	False (*)	SNK: Subbands - 8 (M)
+TSPC_A2DP_5a_11	False (*)	SNK: Allocation - SNR (M)
+TSPC_A2DP_5a_12	False (*)	SNK: Allocation - Loudness (M)
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-avctp.txt b/repo/android/pics-avctp.txt
new file mode 100644
index 0000000..3447962
--- /dev/null
+++ b/repo/android/pics-avctp.txt
@@ -0,0 +1,75 @@
+AVCTP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+		Protocol Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_0_1	False		AVCTP 1.0 (C.1)
+TSPC_AVCTP_0_2	False		AVCTP 1.2 (C.1)
+TSPC_AVCTP_0_3	False		AVCTP 1.3 (C.1)
+TSPC_AVCTP_0_4	True (*)	AVCTP 1.4 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support only one Protocol Version.
+-------------------------------------------------------------------------------
+
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_1_1	True (*)	Controller (C.1)
+TSPC_AVCTP_1_2	True (*)	Target (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		Controller Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_2_1	False		Message fragmentation (O)
+TSPC_AVCTP_2_2	True		Transaction label management (M)
+TSPC_AVCTP_2_3	True		Packet type field management (M)
+TSPC_AVCTP_2_4	True		Message type field management (M)
+TSPC_AVCTP_2_5	True		PID field management (M)
+TSPC_AVCTP_2_6	True		IPID field mangement (M)
+TSPC_AVCTP_2_7	True		Message information management (M)
+TSPC_AVCTP_2_8	False		Event registration for message reception (O)
+TSPC_AVCTP_2_9	False		Event registration for connection request (O)
+TSPC_AVCTP_2_10	False		Event registration for disconnection (O)
+TSPC_AVCTP_2_11	False		Connect request (O)
+TSPC_AVCTP_2_12	False		Disconnect request (O)
+TSPC_AVCTP_2_13	False		Send message (O)
+TSPC_AVCTP_2_14	False		Support for multiple AVCTP channel establishment
+					(O)
+-------------------------------------------------------------------------------
+
+
+		Target Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_3_1	False		Message fragmentation (O)
+TSPC_AVCTP_3_2	True		Transaction label management (M)
+TSPC_AVCTP_3_3	True		Packet type field management (M)
+TSPC_AVCTP_3_4	True		Message type field management (M)
+TSPC_AVCTP_3_5	True		PID field management (M)
+TSPC_AVCTP_3_6	True		IPID field management (M)
+TSPC_AVCTP_3_7	True		Message information management (M)
+TSPC_AVCTP_3_8	True (*)	Event registration for message reception (O)
+TSPC_AVCTP_3_9	True (*)	Event registration for connection request (O)
+TSPC_AVCTP_3_10	True (*)	Event registration for disconnection request (O)
+TSPC_AVCTP_3_11	True (*)	Connect request (O)
+TSPC_AVCTP_3_12	True (*)	Disconnect request (O)
+TSPC_AVCTP_3_13	True (*)	Send message (O)
+TSPC_AVCTP_ALL	False		Enables all test cases when set to TRUE
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-avdtp.txt b/repo/android/pics-avdtp.txt
new file mode 100644
index 0000000..c0c5529
--- /dev/null
+++ b/repo/android/pics-avdtp.txt
@@ -0,0 +1,236 @@
+AVDTP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+		Versions
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_0_1	False		AVDTP 1.0 (C.1)
+TSPC_AVDTP_0_2	False		AVDTP 1.2 (C.1)
+TSPC_AVDTP_0_3	True (*)	AVDTP 1.3 (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to select only one of the protocol versions.
+-------------------------------------------------------------------------------
+
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_1_1	True (*)	Source (C.1)
+TSPC_AVDTP_1_2	True (*)	Sink (C.1)
+TSPC_AVDTP_1_3	True (*)	Initiator (C.2)
+TSPC_AVDTP_1_4	True (*)	Acceptor (C.2)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+C.2: It is within the scope of profiles using the AVDTP specification to
+	mandate Initiator/Acceptor capabilities. It is mandatory to support at
+	least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		Signaling Message Format (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_2_1	True		Transaction label (M)
+TSPC_AVDTP_2_2	True		Packet type (M)
+TSPC_AVDTP_2_3	True		Message type (M)
+TSPC_AVDTP_2_4	True		Signal identifier (M)
+-------------------------------------------------------------------------------
+
+
+		Signaling Channel Establishment/Disconnection (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_3_1	True (*)	Establish signaling channel (O)
+TSPC_AVDTP_3_2	True (*)	Disconnect signaling channel (O)
+-------------------------------------------------------------------------------
+
+
+		Stream Discovery and Configuration (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_4_1	True (*)	Stream discover command (O)
+TSPC_AVDTP_4_2	True (*)	Stream get capabilities command (C.2)
+TSPC_AVDTP_4_3	True (*)	Set configuration command (O)
+TSPC_AVDTP_4_4	True (*)	Get configuration command (O)
+TSPC_AVDTP_4_5	False		Reconfigure command (O)
+TSPC_AVDTP_4_6	True (*)	Stream get all capabilities command (C.1)
+-------------------------------------------------------------------------------
+C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, otherwise
+	excluded.
+C.2: Mandatory to support if TSPC_AVDTP_4_6 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+		Stream Establishment, Suspension and Release (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_5_1	True (*)	Open stream command (O)
+TSPC_AVDTP_5_2	True (*)	Start stream command (O)
+TSPC_AVDTP_5_3	True (*)	Close stream command (O)
+TSPC_AVDTP_5_4	True (*)	Suspend command (O)
+TSPC_AVDTP_5_5	True (*)	Abort stream command (O)
+-------------------------------------------------------------------------------
+
+
+		Security Signaling (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_6_1	False		Content security control command (O)
+-------------------------------------------------------------------------------
+
+
+		Message Fragmentation (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_7_1	True		Signaling message fragmentation (M)
+-------------------------------------------------------------------------------
+
+
+		Signaling Message Format (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_8_1	True		Transaction label (M)
+TSPC_AVDTP_8_2	True		Packet type (M)
+TSPC_AVDTP_8_3	True		Message type (M)
+TSPC_AVDTP_8_4	True		Signal identifier (M)
+-------------------------------------------------------------------------------
+
+
+		Signaling Channel Establishment/Disconnection (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_9_1	True (*)	Establish signaling channel (O)
+TSPC_AVDTP_9_2	True (*)	Disconnect signaling channel (O)
+-------------------------------------------------------------------------------
+
+
+		Stream Discovery and Configuration (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_10_1	True (*)	Stream discover response (O)
+TSPC_AVDTP_10_2	True (*)	Stream get capabilities response (C.2)
+TSPC_AVDTP_10_3	True (*)	Set configuration response (O)
+TSPC_AVDTP_10_4	True (*)	Get configuration response (O)
+TSPC_AVDTP_10_5 False		Reconfigure response (O)
+TSPC_AVDTP_10_6	True (*)	Stream get all capabilities response (C.1)
+-------------------------------------------------------------------------------
+C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, otherwise
+	excluded.
+C.2: It is Mandatory to support if TSPC_AVDTP_10_6 is supported, otherwise
+	Optional.
+-------------------------------------------------------------------------------
+
+
+		Stream Establishment, Suspension and Release (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_11_1	True (*)	Open stream response (O)
+TSPC_AVDTP_11_2	True (*)	Start stream response (O)
+TSPC_AVDTP_11_3	True (*)	Close stream response (O)
+TSPC_AVDTP_11_4	True (*)	Suspend response (O)
+TSPC_AVDTP_11_5	True (*)	Abort stream response (O)
+TSPC_AVDTP_11_6	True (*)	General reject message (O)
+-------------------------------------------------------------------------------
+
+
+		Security Signaling (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_12_1	False		Content security control response (O)
+-------------------------------------------------------------------------------
+
+
+		Message Fragmentation (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_13_1	True		Signaling message fragmentation (M)
+-------------------------------------------------------------------------------
+
+
+		Source Capabilities
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_14_1	True		Basic transport service support (M)
+TSPC_AVDTP_14_2	False		Reporting service support (O)
+TSPC_AVDTP_14_3	False		Recovery service support (O)
+TSPC_AVDTP_14_4	False		Multiplexing service support (O)
+TSPC_AVDTP_14_5	False		Robust header compression service support (O)
+TSPC_AVDTP_14_6	True (*)	Delay Reporting (C.1)
+-------------------------------------------------------------------------------
+C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+		Sink Capabilities
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_15_1	True		Basic transport service support (M)
+TSPC_AVDTP_15_2	False		Reporting service support (O)
+TSPC_AVDTP_15_3	False		Recovery service support (O)
+TSPC_AVDTP_15_4	False		Multiplexing service support (O)
+TSPC_AVDTP_15_5	False		Robust header compression service support (O)
+TSPC_AVDTP_15_6	True (*)	Delay Reporting (C.1)
+-------------------------------------------------------------------------------
+C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+		Message Error Handling Capabilities
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_16_1	False		Reporting Capability Error (C.1)
+TSPC_AVDTP_16_2	False		Reject Corrupted Messages (C.2)
+TSPC_AVDTP_16_3	True (*)	General Reject Response Includes Signal ID (C.3)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_AVDTP_0_2 or TSPC_AVDTP_0_3 supported, excluded
+	otherwise.
+C.2: Optional, excluded if TSPC_AVDTP_16_3 (General Reject Response Includes
+	Signal ID) is supported.
+C.3: Mandatory if TSPC_AVDTP_0_3 supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+		Upper Test Interface
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_17_1	False		Upper Test Interface provided (O)
+-------------------------------------------------------------------------------
+
+
+		L2CAP Capabilities
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_18_1	False		Enhanced Retransmission Mode preferred for
+				signaling channel (O)
+TSPC_AVDTP_18_2	False		Streaming Mode preferred for Media Transport
+				channel (O)
+TSPC_AVDTP_18_3	False		FCS Option (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_AVDTP_18_1 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-avrcp.txt b/repo/android/pics-avrcp.txt
new file mode 100644
index 0000000..7bd68fa
--- /dev/null
+++ b/repo/android/pics-avrcp.txt
@@ -0,0 +1,644 @@
+AVRCP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_1_1    True (*)	Role: Controller (CT) (C.1)
+TSPC_AVRCP_1_2    True (*)	Role: Target (TG) (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		Controller Features
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_2_1    False (*)	CT: Initiating connection establishment (M)
+TSPC_AVRCP_2_2    False (*)	CT: Accepting connection establishment for
+						control initiated by TG (M)
+TSPC_AVRCP_2_3    False (*)	CT: Initiating connection release (M)
+TSPC_AVRCP_2_4    False (*)	CT: Accepting connection release for control
+							initiated by TG (M)
+TSPC_AVRCP_2_5    False		CT: Sending UNIT INFO (O)
+TSPC_AVRCP_2_6    False		CT: Sending SUBUNIT INFO (O)
+TSPC_AVRCP_2_7    False		CT: Sending PASS THROUGH command category 1
+					(C.1)
+TSPC_AVRCP_2_8    False		CT: Sending PASS THROUGH command category 2
+					(C.1)
+TSPC_AVRCP_2_9    False		CT: Sending PASS THROUGH command category 3
+					(C.1)
+TSPC_AVRCP_2_10   False		CT: Sending PASS THROUGH command category 4
+					(C.1)
+TSPC_AVRCP_2_11   False		CT: Get Capabilities (O)
+TSPC_AVRCP_2_12   False		CT: List Player Application Setting
+					Attributes (C.9)
+TSPC_AVRCP_2_13   False		CT: List Player Application Setting Values (O)
+TSPC_AVRCP_2_14   False		CT: Get Current Player Application Setting
+					(C.10)
+TSPC_AVRCP_2_15   False		CT: Set Player Application Setting Value (C.10)
+TSPC_AVRCP_2_16   False		CT: Get Player Application Setting
+						Attribute Text (O)
+TSPC_AVRCP_2_17   False		CT: Get Player Application Setting Value Text
+					(O)
+TSPC_AVRCP_2_18   False		CT: Inform Displayable Character Set (O)
+TSPC_AVRCP_2_19   False		CT: Inform Battery Status of CT (O)
+TSPC_AVRCP_2_20   False		CT: Get Element Attributes (O)
+TSPC_AVRCP_2_21   False		CT: Get Play Status (O)
+TSPC_AVRCP_2_22   False		CT: Register Notification (C.11)
+TSPC_AVRCP_2_23   False		CT: Request Continuing Response (C.2)
+TSPC_AVRCP_2_24   False		CT: Abort Continuing Response (C.2)
+TSPC_AVRCP_2_25   False		CT: Next Group (C.12)
+TSPC_AVRCP_2_26   False		CT: Previous Group (C.12)
+TSPC_AVRCP_2_27   False		CT: Media Player Selection (O)
+TSPC_AVRCP_2_28   False		CT: SetAddressedPlayer (O)
+TSPC_AVRCP_2_29   False		CT: GetFolderItems(MediaPlayerList) (C.5)
+TSPC_AVRCP_2_29b  False		CT: GetTotalNumberOfItems(MediaPlayerList) (C.5)
+TSPC_AVRCP_2_30   False		CT: EVENT_AVAILABLE_PLAYERS_CHANGED (O)
+TSPC_AVRCP_2_31   False		CT: EVENT_ADDRESSED_PLAYER_CHANGED (O)
+TSPC_AVRCP_2_32   False		CT: Browsing (O)
+TSPC_AVRCP_2_33   False		CT: SetBrowsedPlayer (C.4)
+TSPC_AVRCP_2_34   False		CT: ChangePath (C.4)
+TSPC_AVRCP_2_35   False		CT: GetFolderItems(Filesystem) (C.4)
+TSPC_AVRCP_2_35b  False		CT: GetTotalNumberOfItems(Filesystem) (C.4)
+TSPC_AVRCP_2_36   False		CT: GetItemAttributes (O)
+TSPC_AVRCP_2_37   False		CT: PlayItem(Filesystem) (C.4)
+TSPC_AVRCP_2_38   False		CT: EVENT_UIDS_CHANGED (O)
+TSPC_AVRCP_2_39   False		CT: Searching (O)
+TSPC_AVRCP_2_40   False		CT: Search (C.7)
+TSPC_AVRCP_2_41   False		CT: GetFolderItems(Search Results) (C.7)
+TSPC_AVRCP_2_41b  False		CT: GetTotalNumberOfItems(Search Results) (C.7)
+TSPC_AVRCP_2_42   False		CT: PlayItem(SearchResultList) (C.7)
+TSPC_AVRCP_2_43   False		CT: NowPlaying (C.8)
+TSPC_AVRCP_2_44   False		CT: GetFolderItems(NowPlayingList) (C.8)
+TSPC_AVRCP_2_44b  False		CT: GetTotalNumberOfItems(NowPlayingList) (C.8)
+TSPC_AVRCP_2_45   False		CT: PlayItem(NowPlayingList) (C.8)
+TSPC_AVRCP_2_46   False		CT: AddToNowPlaying (O)
+TSPC_AVRCP_2_47   False		CT: EVENT_NOW_PLAYING_CONTENT_CHANGED (O)
+TSPC_AVRCP_2_48   False		CT: Playable Folders (O)
+TSPC_AVRCP_2_49   True (*)	CT: Absolute Volume (C.3)
+TSPC_AVRCP_2_50   True (*)	CT: SetAbsoluteVolume (C.3)
+TSPC_AVRCP_2_51   True (*)	CT: NotifyVolumeChange (C.3)
+TSPC_AVRCP_2_52   False (*)	CT: Discoverable Mode (M)
+TSPC_AVRCP_2_53   False		CT: PASSTHROUGH operation supporting press
+					and hold (O)
+TSPC_AVRCP_2_54   False		CT: Cover Art (O)
+TSPC_AVRCP_2_55   False		CT: GetImageProperties (C.14)
+TSPC_AVRCP_2_56   False		CT: GetImage (C.13)
+TSPC_AVRCP_2_57   False		CT: GetLinkedThumbnail (C.13)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined categories
+	(TSPC_AVRCP_2_7 through TSPC_AVRCP_2_10).
+C.2: Mandatory to support at least one of TSPC_AVRCP_2_23 or TSPC_AVRC_2_24
+			if TSPC_AVRCP_2_20 is supported, otherwise Optional.
+C.3: Mandatory if TSPC_AVRCP_2_8 is supported, otherwise Excluded.
+C.4: Mandatory if TSPC_AVRCP_2_32 is supported, otherwise Excluded.
+C.5: Mandatory if TSPC_AVRCP_2_27 is supported, otherwise Excluded.
+C.7: Mandatory if item TSPC_AVRCP_2_39 is supported, Excluded otherwise.
+C.8: Mandatory if TSPC_AVRCP_2_32 is supported, otherwise Excluded.
+C.9: Mandatory to support if Player Application Settings feature is supported.
+	If any item TSPC_AVRCP_2_13 through TSPC_AVRCP_2_15 is supported it is
+	required to claim support for this feature in accordance with Player
+	Application Settings support requirements, otherwise Optional.
+C.10: Mandatory to support either Get or Set Player Application Settings
+	(TSPC_AVRCP_2_14 or TSPC_AVRCP_2_15) if List Player Application Setting
+	Attributes (TSPC_AVRCP_2_12) is supported. Either TSPC_AVRCP_2_14
+	or TSPC_AVRCP_2_15 must be supported if Player Application Settings
+	feature is supported, in accordance with Player Application Settings
+	support requirements.
+C.11: Mandatory if TSPC_AVRCP_2_20 or TSPC_AVRCP_2_49 is supported, otherwise
+	Optional.
+C.12: Mandatory if Basic Group Navigation Feature supported. If any item
+	TSPC_AVRCP_2_25 or TSPC_AVRCP_2_26 is supported it is mandatory to
+	support both, in accordance with Basic Group Navigation support
+	requirements, otherwise Excluded.
+C.13: Mandatory to support at least one of the functions if TSPC_AVRCP_2_54
+	(Cover Art) is support, otherwise Excluded.
+C.14: Optional if TSPC_AVRCP_2_54 (Cover Art) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Controller Profile Version
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_2b_1   False		CT: AVRCP v1.0 (C.1)
+TSPC_AVRCP_2b_2   False		CT: AVRCP v1.3 (C.1)
+TSPC_AVRCP_2b_3   False		CT: AVRCP v1.4 (C.1)
+TSPC_AVRCP_2b_4   False		CT: AVRCP v1.5 (C.1)
+TSPC_AVRCP_2b_5   False		CT: AVRCP v1.6 (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions if
+	Controller role supported (SPC_AVRCP_1_1).
+-------------------------------------------------------------------------------
+
+
+		Operation_id of Category 1 for CT
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_3_1    False		CT: category 1 - Operation id: 0 (C.1)
+TSPC_AVRCP_3_2    False		CT: category 1 - Operation id: 1 (C.1)
+TSPC_AVRCP_3_3    False		CT: category 1 - Operation id: 2 (C.1)
+TSPC_AVRCP_3_4    False		CT: category 1 - Operation id: 3 (C.1)
+TSPC_AVRCP_3_5    False		CT: category 1 - Operation id: 4 (C.1)
+TSPC_AVRCP_3_6    False		CT: category 1 - Operation id: 5 (C.1)
+TSPC_AVRCP_3_7    False		CT: category 1 - Operation id: 6 (C.1)
+TSPC_AVRCP_3_8    False		CT: category 1 - Operation id: 7 (C.1)
+TSPC_AVRCP_3_9    False		CT: category 1 - Operation id: 8 (C.1)
+TSPC_AVRCP_3_10   False		CT: category 1 - Operation id: 9 (C.1)
+TSPC_AVRCP_3_11   False		CT: category 1 - Operation id: dot (C.1)
+TSPC_AVRCP_3_12   False		CT: category 1 - Operation id: enter (C.1)
+TSPC_AVRCP_3_13   False		CT: category 1 - Operation id: clear (C.1)
+TSPC_AVRCP_3_14   False		CT: category 1 - Operation id: sound_select
+					(C.1)
+TSPC_AVRCP_3_15   False		CT: category 1 - Operation id: input_select
+					(C.1)
+TSPC_AVRCP_3_16   False		CT: category 1 - Operation id:
+					display_information (C.1)
+TSPC_AVRCP_3_17   False		CT: category 1 - Operation id: help (C.1)
+TSPC_AVRCP_3_18   False		CT: category 1 - Operation id: power (C.1)
+TSPC_AVRCP_3_19   False		CT: category 1 - Operation id: play (C.1)
+TSPC_AVRCP_3_20   False		CT: category 1 - Operation id: stop (C.1)
+TSPC_AVRCP_3_21   False		CT: category 1 - Operation id: pause (C.1)
+TSPC_AVRCP_3_22   False		CT: category 1 - Operation id: record (C.1)
+TSPC_AVRCP_3_23   False		CT: category 1 - Operation id: rewind (C.1)
+TSPC_AVRCP_3_24   False		CT: category 1 - Operation id: fast_forward
+					(C.1)
+TSPC_AVRCP_3_25   False		CT: category 1 - Operation id: eject (C.1)
+TSPC_AVRCP_3_26   False		CT: category 1 - Operation id: forward (C.1)
+TSPC_AVRCP_3_27   False		CT: category 1 - Operation id: backward (C.1)
+TSPC_AVRCP_3_28   False		CT: category 1 - Operation id: angle (C.1)
+TSPC_AVRCP_3_29   False		CT: category 1 - Operation id: subpicture (C.1)
+TSPC_AVRCP_3_30   False		CT: category 1 - Operation id: F1 (C.1)
+TSPC_AVRCP_3_31   False		CT: category 1 - Operation id: F2 (C.1)
+TSPC_AVRCP_3_32   False		CT: category 1 - Operation id: F3 (C.1)
+TSPC_AVRCP_3_33   False		CT: category 1 - Operation id: F4 (C.1)
+TSPC_AVRCP_3_34   False		CT: category 1 - Operation id: vendor_unique
+					(C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these operation_ids if the device
+	supports category 1 (TSPC_AVRCP_2_7).
+-------------------------------------------------------------------------------
+
+
+		Operation_id of category 2 for CT
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_4_1    False		CT: category 2 - Operation id: 0 (C.1)
+TSPC_AVRCP_4_2    False		CT: category 2 - Operation id: 1 (C.1)
+TSPC_AVRCP_4_3    False		CT: category 2 - Operation id: 2 (C.1)
+TSPC_AVRCP_4_4    False		CT: category 2 - Operation id: 3 (C.1)
+TSPC_AVRCP_4_5    False		CT: category 2 - Operation id: 4 (C.1)
+TSPC_AVRCP_4_6    False		CT: category 2 - Operation id: 5 (C.1)
+TSPC_AVRCP_4_7    False		CT: category 2 - Operation id: 6 (C.1)
+TSPC_AVRCP_4_8    False		CT: category 2 - Operation id: 7 (C.1)
+TSPC_AVRCP_4_9    False		CT: category 2 - Operation id: 8 (C.1)
+TSPC_AVRCP_4_10   False		CT: category 2 - Operation id: 9 (C.1)
+TSPC_AVRCP_4_11   False		CT: category 2 - Operation id: dot (C.1)
+TSPC_AVRCP_4_12   False		CT: category 2 - Operation id: enter (C.1)
+TSPC_AVRCP_4_13   False		CT: category 2 - Operation id: clear (C.1)
+TSPC_AVRCP_4_14   False		CT: category 2 - Operation id: sound_select
+					(C.1)
+TSPC_AVRCP_4_15   False		CT: category 2 - Operation id: input_select
+					(C.1)
+TSPC_AVRCP_4_16   False		CT: category 2 - Operation id:
+					display_information (C.1)
+TSPC_AVRCP_4_17   False		CT: category 2 - Operation id: help (C.1)
+TSPC_AVRCP_4_18   False		CT: category 2 - Operation id: power (C.1)
+TSPC_AVRCP_4_19   False		CT: category 2 - Operation id: volume_up (C.1)
+TSPC_AVRCP_4_20   False		CT: category 2 - Operation id: volume_down (C.1)
+TSPC_AVRCP_4_21   False		CT: category 2 - Operation id: mute (C.1)
+TSPC_AVRCP_4_22   False		CT: category 2 - Operation id: F1 (C.1)
+TSPC_AVRCP_4_23   False		CT: category 2 - Operation id: F2 (C.1)
+TSPC_AVRCP_4_24   False		CT: category 2 - Operation id: F3 (C.1)
+TSPC_AVRCP_4_25   False		CT: category 2 - Operation id: F4 (C.1)
+TSPC_AVRCP_4_26   False		CT: category 2 - Operation id: vendor_unique
+					(C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these operation_ids if the device
+	supports category 2 (TSPC_AVRCP_2_8).
+-------------------------------------------------------------------------------
+
+
+		Operation_id of category 3 for CT
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_5_1    False		CT: category 3 - Operation id: 0 (C.1)
+TSPC_AVRCP_5_2    False		CT: category 3 - Operation id: 1 (C.1)
+TSPC_AVRCP_5_3    False		CT: category 3 - Operation id: 2 (C.1)
+TSPC_AVRCP_5_4    False		CT: category 3 - Operation id: 3 (C.1)
+TSPC_AVRCP_5_5    False		CT: category 3 - Operation id: 4 (C.1)
+TSPC_AVRCP_5_6    False		CT: category 3 - Operation id: 5 (C.1)
+TSPC_AVRCP_5_7    False		CT: category 3 - Operation id: 6 (C.1)
+TSPC_AVRCP_5_8    False		CT: category 3 - Operation id: 7 (C.1)
+TSPC_AVRCP_5_9    False		CT: category 3 - Operation id: 8 (C.1)
+TSPC_AVRCP_5_10   False		CT: category 3 - Operation id: 9 (C.1)
+TSPC_AVRCP_5_11   False		CT: category 3 - Operation id: dot (C.1)
+TSPC_AVRCP_5_12   False		CT: category 3 - Operation id: enter (C.1)
+TSPC_AVRCP_5_13   False		CT: category 3 - Operation id: clear (C.1)
+TSPC_AVRCP_5_14   False		CT: category 3 - Operation id: channel up (C.1)
+TSPC_AVRCP_5_15   False		CT: category 3 - Operation id: channel down
+					(C.1)
+TSPC_AVRCP_5_16   False		CT: category 3 - Operation id: previous channel
+					(C.1)
+TSPC_AVRCP_5_17   False		CT: category 3 - Operation id: sound_select
+					(C.1)
+TSPC_AVRCP_5_18   False		CT: category 3 - Operation id: input_select
+					(C.1)
+TSPC_AVRCP_5_19   False		CT: category 3 - Operation id:
+					display_information (C.1)
+TSPC_AVRCP_5_20   False		CT: category 3 - Operation id: help (C.1)
+TSPC_AVRCP_5_21   False		CT: category 3 - Operation id: power (C.1)
+TSPC_AVRCP_5_22   False		CT: category 3 - Operation id: angle (C.1)
+TSPC_AVRCP_5_23   False		CT: category 3 - Operation id: subpicture(C.1)
+TSPC_AVRCP_5_24   False		CT: category 3 - Operation id: F1 (C.1)
+TSPC_AVRCP_5_25   False		CT: category 3 - Operation id: F2 (C.1)
+TSPC_AVRCP_5_26   False		CT: category 3 - Operation id: F3 (C.1)
+TSPC_AVRCP_5_27   False		CT: category 3 - Operation id: F4 (C.1)
+TSPC_AVRCP_5_28   False		CT: category 3 - Operation id: vendor_unique
+					(C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these operation_ids if the device
+	supports category 3 (TSPC_AVRCP_2_9).
+-------------------------------------------------------------------------------
+
+
+		Operation_id of category 4 for CT
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_6_1    False		CT: category 4 - Operation id: select (C.1)
+TSPC_AVRCP_6_2    False		CT: category 4 - Operation id: up (C.1)
+TSPC_AVRCP_6_3    False		CT: category 4 - Operation id: down (C.1)
+TSPC_AVRCP_6_4    False		CT: category 4 - Operation id: left (C.1)
+TSPC_AVRCP_6_5    False		CT: category 4 - Operation id: right (C.1)
+TSPC_AVRCP_6_6    False		CT: category 4 - Operation id: right up (C.1)
+TSPC_AVRCP_6_7    False		CT: category 4 - Operation id: right down (C.1)
+TSPC_AVRCP_6_8    False		CT: category 4 - Operation id: left up (C.1)
+TSPC_AVRCP_6_9    False		CT: category 4 - Operation id: left down (C.1)
+TSPC_AVRCP_6_10   False		CT: category 4 - Operation id: root menu (C.1)
+TSPC_AVRCP_6_11   False		CT: category 4 - Operation id: setup menu (C.1)
+TSPC_AVRCP_6_12   False		CT: category 4 - Operation id: contents menu
+					(C.1)
+TSPC_AVRCP_6_13   False		CT: category 4 - Operation id: favorite menu
+					(C.1)
+TSPC_AVRCP_6_14   False		CT: category 4 - Operation id: exit (C.1)
+TSPC_AVRCP_6_15   False		CT: category 4 - Operation id: 0 (C.1)
+TSPC_AVRCP_6_16   False		CT: category 4 - Operation id: 1 (C.1)
+TSPC_AVRCP_6_17   False		CT: category 4 - Operation id: 2 (C.1)
+TSPC_AVRCP_6_18   False		CT: category 4 - Operation id: 3 (C.1)
+TSPC_AVRCP_6_19   False		CT: category 4 - Operation id: 4 (C.1)
+TSPC_AVRCP_6_20   False		CT: category 4 - Operation id: 5 (C.1)
+TSPC_AVRCP_6_21   False		CT: category 4 - Operation id: 6 (C.1)
+TSPC_AVRCP_6_22   False		CT: category 4 - Operation id: 7 (C.1)
+TSPC_AVRCP_6_23   False		CT: category 4 - Operation id: 8 (C.1)
+TSPC_AVRCP_6_24   False		CT: category 4 - Operation id: 9 (C.1)
+TSPC_AVRCP_6_25   False		CT: category 4 - Operation id: dot (C.1)
+TSPC_AVRCP_6_26   False		CT: category 4 - Operation id: enter (C.1)
+TSPC_AVRCP_6_27   False		CT: category 4 - Operation id: clear (C.1)
+TSPC_AVRCP_6_28   False		CT: category 4 - Operation id:
+					display_information (C.1)
+TSPC_AVRCP_6_29   False		CT: category 4 - Operation id: help (C.1)
+TSPC_AVRCP_6_30   False		CT: category 4 - Operation id: page up (C.1)
+TSPC_AVRCP_6_31   False		CT: category 4 - Operation id: page down (C.1)
+TSPC_AVRCP_6_32   False		CT: category 4 - Operation id: power (C.1)
+TSPC_AVRCP_6_33   False		CT: category 4 - Operation id: F1 (C.1)
+TSPC_AVRCP_6_34   False		CT: category 4 - Operation id: F2 (C.1)
+TSPC_AVRCP_6_35   False		CT: category 4 - Operation id: F3 (C.1)
+TSPC_AVRCP_6_36   False		CT: category 4 - Operation id: F4 (C.1)
+TSPC_AVRCP_6_37   False		CT: category 4 - Operation id: vendor_unique
+					(C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these operation_ids if the device
+	supports category 4 (TSPC_AVRCP_2_10).
+-------------------------------------------------------------------------------
+
+
+		Target Features
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_7_1    True (*)	TG: Initiating connection establishment for
+					Control (O)
+TSPC_AVRCP_7_2    True		TG: Accept connection establishment for Control
+					initiated by CT (M)
+TSPC_AVRCP_7_3    True (*)	TG: Initiating connection release (M)
+TSPC_AVRCP_7_4    True		TG: Accepting connection release (M)
+TSPC_AVRCP_7_5    True		TG: Receiving UNIT INFO (M)
+TSPC_AVRCP_7_6    True		TG: Receiving SUBUNIT INFO (M)
+TSPC_AVRCP_7_7    True (*)	TG: Receiving PASS THROUGH command category 1
+					(C.1)
+TSPC_AVRCP_7_8    True (*)	TG: Receiving PASS THROUGH command category 2
+					(C.1)
+TSPC_AVRCP_7_9    False		TG: Receiving PASS THROUGH command category 3
+					(C.1)
+TSPC_AVRCP_7_10   False		TG: Receiving PASS THROUGH command category 4
+					(C.1)
+TSPC_AVRCP_7_11   True (*)	TG: Get Capabilities Response (C.3)
+TSPC_AVRCP_7_12   False		TG: List Player Application Settings Attributes
+					Response (C.14)
+TSPC_AVRCP_7_13   False		TG: List Player Application Setting Values
+					Response (C.14)
+TSPC_AVRCP_7_14   False		TG: Get Current Player Application Settings
+					Value Response (C.14)
+TSPC_AVRCP_7_15   False		TG: Set Player Application Setting Value
+					Response (C.14)
+TSPC_AVRCP_7_16   False		TG: Get Player Application Setting Attribute
+					Text Response (O)
+TSPC_AVRCP_7_17   False		TG: Get Player Application Setting Value Text
+					Response (O)
+TSPC_AVRCP_7_18   False		TG: Inform Displayable Character Set Response
+					(O)
+TSPC_AVRCP_7_19   False		TG: Inform Battery Status Of CT Response (O)
+TSPC_AVRCP_7_20   True (*)	TG: Get Element Attributes Response (C.3)
+TSPC_AVRCP_7_21   True (*)	TG: Get Play Status Response (C.2)
+TSPC_AVRCP_7_22   True (*)	TG: Register Notification Response (C.12)
+TSPC_AVRCP_7_23   True (*)	TG: Notify Event Response:
+					PLAYBACK_STATUS_CHANGED (C.4)
+TSPC_AVRCP_7_24   True (*)	TG: Notify Event Response: TRACK_CHANGED (C.4)
+TSPC_AVRCP_7_25   False		TG: Notify Event Response: TRACK_REACHED_END (O)
+TSPC_AVRCP_7_26   False		TG: Notify Event Response: TRACK_REACHED_START
+					(O)
+TSPC_AVRCP_7_27   False		TG: Notify Event Response: PLAYBACK_POS_CHANGED
+					(O)
+TSPC_AVRCP_7_28   False		TG: Notify Event Response: BATT_STATUS_CHANGED
+					(O)
+TSPC_AVRCP_7_29   False		TG: Notify Event Response: SYSTEM_STATUS_CHANGED
+					(O)
+TSPC_AVRCP_7_30   False		TG: Notify Event Response:
+					PLAYER_APPLICATION_SETTING_CHANGED (O)
+TSPC_AVRCP_7_31   True (*)	TG: Request Continuing Response (C.2)
+TSPC_AVRCP_7_32   True (*)	TG: Abort ContinuingResponse Response (C.2)
+TSPC_AVRCP_7_34   False		TG: Next Group (C.15)
+TSPC_AVRCP_7_35   False		TG: Previous Group (C.15)
+TSPC_AVRCP_7_36   False		TG: Media Player Selection (C.8)
+TSPC_AVRCP_7_37   False		TG: SetAddressedPlayer (C.8)
+TSPC_AVRCP_7_38   False		TG: GetFolderItems(MediaPlayerList) (C.8)
+TSPC_AVRCP_7_38b  False		TG: GetTotalNumberOfItems(MediaPlayerList) (C.8)
+TSPC_AVRCP_7_39   False		TG: EVENT_AVAILABLE_PLAYERS_CHANGED (C.8)
+TSPC_AVRCP_7_40   False		TG: EVENT_ADDRESSED_PLAYER_CHANGED (C.8)
+TSPC_AVRCP_7_41   False		TG: Supports Multiple Players (O)
+TSPC_AVRCP_7_42   False		TG: Browsing (O)
+TSPC_AVRCP_7_42a  False		TG: Initiating connection establishment for
+					browsing channel (O)
+TSPC_AVRCP_7_43   False		TG: SetBrowsedPlayer (C.6)
+TSPC_AVRCP_7_44   False		TG: ChangePath (C.6)
+TSPC_AVRCP_7_45   False		TG: GetFolderItems(Filesystem) (C.6)
+TSPC_AVRCP_7_45b  False		TG: GetTotalNumberOfItems(Filesystem) (C.6)
+TSPC_AVRCP_7_46   False		TG: GetItemAttributes (C.6)
+TSPC_AVRCP_7_47   False		TG: PlayItem(Filesystem) (C.6)
+TSPC_AVRCP_7_48   False		TG: EVENT_UIDS_CHANGED (C.9)
+TSPC_AVRCP_7_49   False		TG: Database Aware Players (O)
+TSPC_AVRCP_7_50   False		TG: Searching (O)
+TSPC_AVRCP_7_51   False		TG: Search (C.10)
+TSPC_AVRCP_7_52   False		TG: GetFolderItems(Search Results) (C.10)
+TSPC_AVRCP_7_52b  False		TG: GetTotalNumberOfItems(Search Results) (C.10)
+TSPC_AVRCP_7_53   False		TG: PlayItem(SearchResultList) (C.10)
+TSPC_AVRCP_7_54   False		TG: NowPlaying (C.11)
+TSPC_AVRCP_7_55   False		TG: GetFolderItems(NowPlayingList) (C.11)
+TSPC_AVRCP_7_55b  False		TG: GetTotalNumberOfItems(NowPlayingList) (C.11)
+TSPC_AVRCP_7_56   False		TG: PlayItem(NowPlayingList) (C.11)
+TSPC_AVRCP_7_57   False		TG: AddToNowPlaying (O)
+TSPC_AVRCP_7_58   False		TG: EVENT_NOW_PLAYING_CONTENT_CHANGED (C.11)
+TSPC_AVRCP_7_59   False		TG: Playable Folders (O)
+TSPC_AVRCP_7_60   False		TG: Absolute Volume (C.5)
+TSPC_AVRCP_7_61   False		TG: SetAbsoluteVolume (C.5)
+TSPC_AVRCP_7_62   False		TG: NotifyVolumeChange (C.5)
+TSPC_AVRCP_7_63   False		TG: Error Response (O)
+TSPC_AVRCP_7_64   False		TG: General Reject (C.13)
+TSPC_AVRCP_7_65   True		TG: Discoverable Mode (M)
+TSPC_AVRCP_7_66   False		TG: PASSTHROUGH operation supporting press
+					and hold (O)
+TSPC_AVRCP_7_67   False		TG: Cover Art (O)
+TSPC_AVRCP_7_68   False		TG: GetImageProperties (C.16)
+TSPC_AVRCP_7_69   False		TG: GetImage (C.16)
+TSPC_AVRCP_7_70   False		TG: GetLinkedThumbnail (C.16)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the categories. Supported
+	operation_id's are shown in Table 8 to Table 11.
+C.2: Mandatory if 7/20 is supported, otherwise Optional.
+C.3: Mandatory if 7/7 is supported, otherwise Optional.
+C.4: Mandatory if 7/22 and 7/20 is supported, otherwise Optional.
+C.5: Mandatory if 7/8 is supported, otherwise Excluded.
+C.6: Mandatory if 7/42 is supported, otherwise Excluded.
+C.7: Mandatory if 7/36 is supported, otherwise Excluded.
+C.8: Mandatory if (7/7 or 7/9) is supported, otherwise Excluded.
+C.9: Mandatory if 7/49 is supported, otherwise Optional.
+C.10: Mandatory if 7/50 is supported, otherwise Excluded.
+C.11: Mandatory if 7/42 is supported, otherwise Optional.
+C.12: Mandatory if 7/7 or (7/8 AND 7/60) or 7/9 is supported, otherwise Optional
+C.13: Mandatory if 7/7 or 7/9 or 7/42 is supported, otherwise Optional.
+C.14: Mandatory if Player Application Settings Feature supported. If any item
+	7/12 – 7/15 is supported, all items 7/12 – 7/15 shall be supported,
+	in accordance with Player Application Settings Feature support
+	requirements, otherwise Excluded.
+C.15: Mandatory if Basic Group Navigation Feature supported. If any item
+	7/34 or 7/35 is supported it is mandatory to support both,
+	in accordance with Basic Group Navigation support requirements,
+	otherwise Excluded.
+C.16: Mandatory if 7/67 (Cover Art) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+		Target Profile Version
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_7b_1   False		TG: AVRCP v1.0 (C.1)
+TSPC_AVRCP_7b_2   False		TG: AVRCP v1.3 (C.1)
+TSPC_AVRCP_7b_3   False		TG: AVRCP v1.4 (C.1)
+TSPC_AVRCP_7b_4   True (*)	TG: AVRCP v1.5 (C.1)
+TSPC_AVRCP_7b_5   False		TG: AVRCP v1.6 (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+		operation_id of category 1 for TG
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_8_1    False		TG: category 1 - Operation id: 0 (O)
+TSPC_AVRCP_8_2    False		TG: category 1 - Operation id: 1 (O)
+TSPC_AVRCP_8_3    False		TG: category 1 - Operation id: 2 (O)
+TSPC_AVRCP_8_4    False		TG: category 1 - Operation id: 3 (O)
+TSPC_AVRCP_8_5    False		TG: category 1 - Operation id: 4 (O)
+TSPC_AVRCP_8_6    False		TG: category 1 - Operation id: 5 (O)
+TSPC_AVRCP_8_7    False		TG: category 1 - Operation id: 6 (O)
+TSPC_AVRCP_8_8    False		TG: category 1 - Operation id: 7 (O)
+TSPC_AVRCP_8_9    False		TG: category 1 - Operation id: 8 (O)
+TSPC_AVRCP_8_10   False		TG: category 1 - Operation id: 9 (O)
+TSPC_AVRCP_8_11   False		TG: category 1 - Operation id: dot (O)
+TSPC_AVRCP_8_12   False		TG: category 1 - Operation id: enter (O)
+TSPC_AVRCP_8_13   False		TG: category 1 - Operation id: clear (O)
+TSPC_AVRCP_8_14   False		TG: category 1 - Operation id: sound select (O)
+TSPC_AVRCP_8_15   False		TG: category 1 - Operation id: input select (O)
+TSPC_AVRCP_8_16   False		TG: category 1 - Operation id: display
+					information (O)
+TSPC_AVRCP_8_17   False		TG: category 1 - Operation id: help (O)
+TSPC_AVRCP_8_18   False		TG: category 1 - Operation id: power (O)
+TSPC_AVRCP_8_19   True		TG: category 1 - Operation id: play (M)
+TSPC_AVRCP_8_20   True		TG: category 1 - Operation id: stop (M)
+TSPC_AVRCP_8_21   True (*)	TG: category 1 - Operation id: pause (O)
+TSPC_AVRCP_8_22   False		TG: category 1 - Operation id: record (O)
+TSPC_AVRCP_8_23   True (*)	TG: category 1 - Operation id: rewind (O)
+TSPC_AVRCP_8_24   True (*)	TG: category 1 - Operation id: fast forward (O)
+TSPC_AVRCP_8_25   False		TG: category 1 - Operation id: eject (O)
+TSPC_AVRCP_8_26   True (*)	TG: category 1 - Operation id: forward (O)
+TSPC_AVRCP_8_27   True (*)	TG: category 1 - Operation id: backward (O)
+TSPC_AVRCP_8_28   False		TG: category 1 - Operation id: angle (O)
+TSPC_AVRCP_8_29   False		TG: category 1 - Operation id: subpicture (O)
+TSPC_AVRCP_8_30   False		TG: category 1 - Operation id: F1 (O)
+TSPC_AVRCP_8_31   False		TG: category 1 - Operation id: F2 (O)
+TSPC_AVRCP_8_32   False		TG: category 1 - Operation id: F3 (O)
+TSPC_AVRCP_8_33   False		TG: category 1 - Operation id: F4 (O)
+TSPC_AVRCP_8_33a  False		TG: category 1 - Operation id: F5 (O)
+TSPC_AVRCP_8_34   False		TG: category 1 - Operation id: vendor unique (O)
+-------------------------------------------------------------------------------
+
+
+		operation_id of category 2 for TG
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_9_1    False		TG: category 2 - Operation id: 0 (O)
+TSPC_AVRCP_9_2    False		TG: category 2 - Operation id: 1 (O)
+TSPC_AVRCP_9_3    False		TG: category 2 - Operation id: 2 (O)
+TSPC_AVRCP_9_4    False		TG: category 2 - Operation id: 3 (O)
+TSPC_AVRCP_9_5    False		TG: category 2 - Operation id: 4 (O)
+TSPC_AVRCP_9_6    False		TG: category 2 - Operation id: 5 (O)
+TSPC_AVRCP_9_7    False		TG: category 2 - Operation id: 6 (O)
+TSPC_AVRCP_9_8    False		TG: category 2 - Operation id: 7 (O)
+TSPC_AVRCP_9_9    False		TG: category 2 - Operation id: 8 (O)
+TSPC_AVRCP_9_10   False		TG: category 2 - Operation id: 9 (O)
+TSPC_AVRCP_9_11   False		TG: category 2 - Operation id: dot (O)
+TSPC_AVRCP_9_12   False		TG: category 2 - Operation id: enter (O)
+TSPC_AVRCP_9_13   False		TG: category 2 - Operation id: clear (O)
+TSPC_AVRCP_9_14   False		TG: category 2 - Operation id: sound select (O)
+TSPC_AVRCP_9_15   False		TG: category 2 - Operation id: input select (O)
+TSPC_AVRCP_9_16   False		TG: category 2 - Operation id: display
+					information (O)
+TSPC_AVRCP_9_17	  False		TG: category 2 - Operation id: help (O)
+TSPC_AVRCP_9_18	  False		TG: category 2 - Operation id: power (O)
+TSPC_AVRCP_9_19   True		TG: category 2 - Operation id: volume up (C.2)
+TSPC_AVRCP_9_20   True		TG: category 2 - Operation id: volume down (C.2)
+TSPC_AVRCP_9_21   False		TG: category 2 - Operation id: mute (O)
+TSPC_AVRCP_9_24   False		TG: category 2 - Operation id: F1 (O)
+TSPC_AVRCP_9_25   False		TG: category 2 - Operation id: F2 (O)
+TSPC_AVRCP_9_26   False		TG: category 2 - Operation id: F3 (O)
+TSPC_AVRCP_9_27   False		TG: category 2 - Operation id: F4 (O)
+TSPC_AVRCP_9_27a  False		TG: category 2 - Operation id: F5 (O)
+TSPC_AVRCP_9_28   False		TG: category 2 - Operation id: vendor unique (O)
+-------------------------------------------------------------------------------
+C.2: Mandatory to support if the device supports category 2 (TSPC_AVRCP_7_8).
+-------------------------------------------------------------------------------
+
+
+		operation_id of category 3 for TG
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_10_1   False		TG: category 3 - Operation id: 0 (O)
+TSPC_AVRCP_10_2   False		TG: category 3 - Operation id: 1 (O)
+TSPC_AVRCP_10_3   False		TG: category 3 - Operation id: 2 (O)
+TSPC_AVRCP_10_4   False		TG: category 3 - Operation id: 3 (O)
+TSPC_AVRCP_10_5   False		TG: category 3 - Operation id: 4 (O)
+TSPC_AVRCP_10_6   False		TG: category 3 - Operation id: 5 (O)
+TSPC_AVRCP_10_7   False		TG: category 3 - Operation id: 6 (O)
+TSPC_AVRCP_10_8   False		TG: category 3 - Operation id: 7 (O)
+TSPC_AVRCP_10_9   False		TG: category 3 - Operation id: 8 (O)
+TSPC_AVRCP_10_10  False		TG: category 3 - Operation id: 9 (O)
+TSPC_AVRCP_10_11  False		TG: category 3 - Operation id: dot (O)
+TSPC_AVRCP_10_12  False		TG: category 3 - Operation id: enter (O)
+TSPC_AVRCP_10_13  False		TG: category 3 - Operation id: clear (O)
+TSPC_AVRCP_10_14  False (*)	TG: category 3 - Operation id: channel up (C.3)
+TSPC_AVRCP_10_15  False (*)	TG: category 3 - Operation id: channel down
+					(C.3)
+TSPC_AVRCP_10_16  False		TG: category 3 - Operation id: previous channel
+					(O)
+TSPC_AVRCP_10_17  False		TG: category 3 - Operation id: sound select (O)
+TSPC_AVRCP_10_18  False		TG: category 3 - Operation id: input select (O)
+TSPC_AVRCP_10_19  False		TG: category 3 - Operation id: display
+					information (O)
+TSPC_AVRCP_10_20  False		TG: category 3 - Operation id: help (O)
+TSPC_AVRCP_10_21  False		TG: category 3 - Operation id: power (O)
+TSPC_AVRCP_10_21a False		TG: category 3 - Operation id: angle (O)
+TSPC_AVRCP_10_21b False		TG: category 3 - Operation id: subpicture (O)
+TSPC_AVRCP_10_22  False		TG: category 3 - Operation id: F1 (O)
+TSPC_AVRCP_10_23  False		TG: category 3 - Operation id: F2 (O)
+TSPC_AVRCP_10_24  False		TG: category 3 - Operation id: F3 (O)
+TSPC_AVRCP_10_25  False		TG: category 3 - Operation id: F4 (O)
+TSPC_AVRCP_10_25a False		TG: category 3 - Operation id: F5 (O)
+TSPC_AVRCP_10_26  False		TG: category 3 - Operation id: vendor unique (O)
+-------------------------------------------------------------------------------
+C.3: Mandatory to support if the device supports category 3 (TSPC_AVRCP_7_9).
+-------------------------------------------------------------------------------
+
+
+		operation_id of category 4 for TG
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_11_1   False (*)	TG: category 4 - Operation id: select (C.4)
+TSPC_AVRCP_11_2   False (*)	TG: category 4 - Operation id: up (C.4)
+TSPC_AVRCP_11_3   False (*)	TG: category 4 - Operation id: down (C.4)
+TSPC_AVRCP_11_4   False (*)	TG: category 4 - Operation id: left (C.4)
+TSPC_AVRCP_11_5   False (*)	TG: category 4 - Operation id: right (C.4)
+TSPC_AVRCP_11_6   False		TG: category 4 - Operation id: right up (O)
+TSPC_AVRCP_11_7   False		TG: category 4 - Operation id: right down (O)
+TSPC_AVRCP_11_8   False		TG: category 4 - Operation id: left up (O)
+TSPC_AVRCP_11_9   False		TG: category 4 - Operation id: left down (O)
+TSPC_AVRCP_11_10  False (*)	TG: category 4 - Operation id: root menu (C.4)
+TSPC_AVRCP_11_11  False		TG: category 4 - Operation id: setup menu (O)
+TSPC_AVRCP_11_12  False		TG: category 4 - Operation id: contents menu (O)
+TSPC_AVRCP_11_13  False		TG: category 4 - Operation id: favorite menu (O)
+TSPC_AVRCP_11_14  False		TG: category 4 - Operation id: exit (O)
+TSPC_AVRCP_11_15  False		TG: category 4 - Operation id: 0 (O)
+TSPC_AVRCP_11_16  False		TG: category 4 - Operation id: 1 (O)
+TSPC_AVRCP_11_17  False		TG: category 4 - Operation id: 2 (O)
+TSPC_AVRCP_11_18  False		TG: category 4 - Operation id: 3 (O)
+TSPC_AVRCP_11_19  False		TG: category 4 - Operation id: 4 (O)
+TSPC_AVRCP_11_20  False		TG: category 4 - Operation id: 5 (O)
+TSPC_AVRCP_11_21  False		TG: category 4 - Operation id: 6 (O)
+TSPC_AVRCP_11_22  False		TG: category 4 - Operation id: 7 (O)
+TSPC_AVRCP_11_23  False		TG: category 4 - Operation id: 8 (O)
+TSPC_AVRCP_11_24  False		TG: category 4 - Operation id: 9 (O)
+TSPC_AVRCP_11_25  False		TG: category 4 - Operation id: dot (O)
+TSPC_AVRCP_11_26  False		TG: category 4 - Operation id: enter (O)
+TSPC_AVRCP_11_27  False		TG: category 4 - Operation id: clear (O)
+TSPC_AVRCP_11_28  False		TG: category 4 - Operation id: disply (O)
+TSPC_AVRCP_11_29  False		TG: category 4 - Operation id: help (O)
+TSPC_AVRCP_11_30  False		TG: category 4 - Operation id: page up (O)
+TSPC_AVRCP_11_31  False		TG: category 4 - Operation id: page down (O)
+TSPC_AVRCP_11_32  False		TG: category 4 - Operation id: power (O)
+TSPC_AVRCP_11_33  False		TG: category 4 - Operation id: F1 (O)
+TSPC_AVRCP_11_34  False		TG: category 4 - Operation id: F2 (O)
+TSPC_AVRCP_11_35  False		TG: category 4 - Operation id: F3 (O)
+TSPC_AVRCP_11_36  False		TG: category 4 - Operation id: F4 (O)
+TSPC_AVRCP_11_36a False		TG: category 4 - Operation id: F5 (O)
+TSPC_AVRCP_11_37  False		TG: category 4 - Operation id: vendor unique (O)
+
+TSPC_AVRCP_12_1   True		General discoverable mode (M)
+TSPC_AVRCP_13_1   True		General discoverable mode (M)
+TSPC_AVRCP_14_1   False		OBEX Connect operation (C.1)
+TSPC_AVRCP_14_2   False		OBEX Get operation (C.1)
+TSPC_AVRCP_14_3   False		OBEX Disconnect operation (C.1)
+TSPC_AVRCP_15_1   False		OBEX Connect operation (C.1)
+TSPC_AVRCP_15_2   False		OBEX Get operation (C.1)
+TSPC_AVRCP_15_3   False		OBEX Disconnect operation (C.1)
+
+TSPC_ALL          False		Enables all test cases when set to TRUE.
+-------------------------------------------------------------------------------
+C.4: Mandatory to support if the device supports category 4 (TSPC_AVRCP_7_10).
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-bnep.txt b/repo/android/pics-bnep.txt
new file mode 100644
index 0000000..289e470
--- /dev/null
+++ b/repo/android/pics-bnep.txt
@@ -0,0 +1,26 @@
+BNEP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+		Profile Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_BNEP_1_1	True		BNEP Connection Setup (M)
+TSPC_BNEP_1_2	True (*)	BNEP Data Packet Reception (M)
+TSPC_BNEP_1_3	True (*)	BNEP Data Packet Transmission (M)
+TSPC_BNEP_1_3a	True (*)	BNEP Compressed Packet Transmission (O)
+TSPC_BNEP_1_3b	True (*)	BNEP Compressed Packet Transmission Source Only
+				(O)
+TSPC_BNEP_1_4	True		BNEP Control Message Processing (M)
+TSPC_BNEP_1_5	True		BNEP Extension Header Processing (M)
+TSPC_BNEP_1_6	True		Network Protocol Filter Message Transmission (O)
+TSPC_BNEP_1_7	True		Multicast Address Filter Message Transmission
+				(O)
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-did.txt b/repo/android/pics-did.txt
new file mode 100644
index 0000000..9ecb86d
--- /dev/null
+++ b/repo/android/pics-did.txt
@@ -0,0 +1,23 @@
+DID PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+
+		SDP Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_DID_1_1	True		Specification ID (M)
+TSPC_DID_1_2	True		Vendor ID (M)
+TSPC_DID_1_3	True		Product ID (M)
+TSPC_DID_1_4	True		Version (M)
+TSPC_DID_1_5	True		Primary Record (M)
+TSPC_DID_1_6	True		Vendor ID Source (M)
+TSPC_ALL	False		Turns on all the test cases
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-dis.txt b/repo/android/pics-dis.txt
new file mode 100644
index 0000000..83f25ce
--- /dev/null
+++ b/repo/android/pics-dis.txt
@@ -0,0 +1,59 @@
+DIS PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+
+M - mandatory
+O - optional
+
+		Transport Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_DIS_1_1	True		Service supported over BR/EDR (C.1)
+TSPC_DIS_1_2	True		Service supported over LE (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of TSPC_DIS_1_1 or TSPC_DIS_1_2.
+-------------------------------------------------------------------------------
+
+
+		Service Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_DIS_2_1	True		Device Information Service (M)
+TSPC_DIS_2_2	True		Manufacturer Name String Characteristic (O)
+TSPC_DIS_2_3	True		Model Number String Characteristic (O)
+TSPC_DIS_2_4	True		Serial Number String Characteristic (O)
+TSPC_DIS_2_5	True		Hardware Revision String Characteristic (O)
+TSPC_DIS_2_6	True		Firmware Revision String Characteristic (O)
+TSPC_DIS_2_7	True		Software Revision String Characteristic (O)
+TSPC_DIS_2_8	True		System ID Characteristic (O)
+TSPC_DIS_2_9	False (*)	IEEE 11073-20601 Regulatory Certification
+				Data List Characteristic (O)
+TSPC_DIS_2_10	True		SDP Interoperability (C.1)
+TSPC_DIS_2_11	True		PnP ID (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_DIS_1_1 (BR/EDR) is supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+		GATT Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_DIS_3_1	True		Generic Attribute Profile Server (M)
+-------------------------------------------------------------------------------
+
+
+		SDP Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_DIS_4_1	True		Support for server role (M)
+TSPC_DIS_4_2	True		ProtocolDescriptorList (M)
+TSPC_DIS_4_3	True		BrowseGroupList (M)
+-------------------------------------------------------------------------------
+Note: Marked as False as TSPC_DIS_1_1 is set to False
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-gap.txt b/repo/android/pics-gap.txt
new file mode 100644
index 0000000..3775995
--- /dev/null
+++ b/repo/android/pics-gap.txt
@@ -0,0 +1,788 @@
+GAP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+
+M - mandatory
+O - optional
+
+		Device Configuration
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_0_1	False (*)	BR/EDR (C.1)
+TSPC_GAP_0_2	False (*)	LE (C.2)
+TSPC_GAP_0_3	True		BR/EDR/LE (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if ('End Product' or 'Host Subsystem') and ('BR Host' or
+	'BR/HS Host') are Supported ('End Product' or 'Host Subsystem' with 'BR'
+	or 'BR/HS Host' CC), otherwise excluded. Optional for
+	'Component (Tested)' or 'Component (Non-Tested)'.
+C.2: Mandatory if ('End Product' or 'Host Subsystem') and ('LE Host') are
+	Supported (End Product or Host Subsystem with LE Host CC),
+	otherwise excluded.  Optional for 'Component (Tested)' or
+	'Component (Non-Tested)'.
+C.3: Mandatory if ('End Product' or 'Host Subsystem') and ('BR/LE Host' or
+	'BR/HS/LE Host') are Supported (End Product or Host Subsystem with
+	BR/LE or BR/HS/LE Host CC), otherwise excluded.
+	Optional for 'Component (Tested)' or 'Component (Non-tested)'.
+Note - Only one transport shall be supported.
+-------------------------------------------------------------------------------
+
+
+		Version Configuration
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_0A_1	True		Core Specification Addendum 3 (CSA3), GAP
+					Connection Parameters Changes,
+					Authentication and Lost Bond Changes,
+					Private Addressing Changes, Dual Mode
+					Addressing Changes,
+					Adopted 24 July 2012 (C.1)
+TSPC_GAP_0A_2	True		Core Specification Addendum 4 (CSA4)
+TSPC_GAP_0A_3	True		Core Spec version 4.1 (Core v4.1) GAP Connection
+					Parameters Changes, Authentication and
+					Lost Bond Changes, Private Addressing
+					Changes, Dual Mode Addressing Changes,
+					Adopted 03 December 2013
+-------------------------------------------------------------------------------
+C.1: Mandatory if 'CSA3 Adopted 24 July 2012' is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Modes
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_1_1	True		Non-discoverable mode (C.1)
+TSPC_GAP_1_2	True		Limited-discoverable Mode (O)
+TSPC_GAP_1_3	True		General-discoverable mode (O)
+TSPC_GAP_1_4	True		Non-connectable mode (O)
+TSPC_GAP_1_5	True		Connectable mode (M)
+TSPC_GAP_1_6	True		Non-bondable mode (O)
+TSPC_GAP_1_7	True		Bondable mode (C.2)
+TSPC_GAP_1_8	False (*)	Non-Synchronizable Mode (C.3)
+TSPC_GAP_1_9	False (*)	Synchronizable Mode (C.4)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_0_2 is supported, otherwise Optional.
+C.2: Mandatory if TSPC_GAP_3_5 is supported, otherwise Optional.
+C.3: Mandatory if TSPC_GAP_0A_2 or TSPC_GAP_0A_3 and is supported, otherwise
+	Excluded.
+C.4: Optional if TSPC_GAP_0A_2 or later is supported; Mandatory if TSPC_GAP_0A_2
+	or later and BB 3a/1 (Connectionless Slave Broadcast Transmitter) are
+	supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Security Aspects
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_2_1	True		Authentication procedure (C.1)
+TSPC_GAP_2_2	True		Support of LMP-Authentication (M)
+TSPC_GAP_2_3	True		Initiate LMP-Authentication (C.5)
+TSPC_GAP_2_4	False (*)	Security mode 1 (C.2)
+TSPC_GAP_2_5	True		Security mode 2 (O)
+TSPC_GAP_2_6	False (*)	Security mode 3 (C.7)
+TSPC_GAP_2_7	True		Security mode 4 (C.4)
+TSPC_GAP_2_8	True		Support of Authenticated link key (C.6)
+TSPC_GAP_2_9	True		Support of Unauthenticated link key (C.6)
+TSPC_GAP_2_10	True		No security (C.6)
+TSPC_GAP_2_11	False (*)	Secure Connections Only Mode (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6) is supported, otherwise
+	Optional.
+Note 1: The Authentication Procedure in item GAP, TSPC_GAP_2_1 is the one
+	described in Fig. 5.1 on page 198 in the GAP Profile Specification and
+	not the LMP-Authenticaion.
+C.2: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional.
+C.5: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6 or TSPC_GAP_2_7) is supported,
+	otherwise Optional.
+C.4: Mandatory if (Core Spec 2.1 or later) is supported, otherwise Excluded.
+Note 2. If a Core 2.0 and earlier design claims to support secure communcation
+	it should support either Security mode 2 or 3.
+Note 3. A Core 2.1 or later device shall always support secure communication
+	in Security Mode 4, and shall use that mode to connect with another
+	Core 2.1 or later device. It shall use Security Mode 2 only for
+	backward compatibility purposes with Core 2.0 and earlier devices.
+	Security Mode 1 is excluded for Core 2.1 or later devices based on
+	condition C.2.
+C.6: If TSPC_GAP_2_7 is supported then at least one of (TSPC_GAP_2_8 or
+	TSPC_GAP_2_9 or TSPC_GAP_2_10) is Mandatory, otherwise Excluded.
+C.7: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+		Idle Mode Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_3_1	True		Initiation of general inquiry (C.1)
+TSPC_GAP_3_2	True		Initiation of limited inquiry (C.1)
+TSPC_GAP_3_3	True		Initiation of name discover (O)
+TSPC_GAP_3_4	True		Initiation of device discovery (O)
+TSPC_GAP_3_5	True		Initiation of general bonding (O)
+TSPC_GAP_3_6	True		Initiation of dedicated bonding (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of TSPC_GAP_3_1 or TSPC_GAP_3_2 if
+	TSPC_GAP_3_5 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+		Establishment Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_4_1	True		Support link establishment as initiator (M)
+TSPC_GAP_4_2	True		Support link establishment as acceptor (M)
+TSPC_GAP_4_3	True		Support channel establishment as initiator (O)
+TSPC_GAP_4_4	True		Support channel establishment as acceptor (M)
+TSPC_GAP_4_5	True		Support connection establishment as initiator
+					(O)
+TSPC_GAP_4_6	True		Support connection establishment as acceptor
+					(O)
+TSPC_GAP_4_7	True		Support synchronization establishment
+					as receiver (O)
+-------------------------------------------------------------------------------
+
+
+		LE Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_5_1	False (*)	Broadcaster (C.1)
+TSPC_GAP_5_2	False (*)	Observer (C.1)
+TSPC_GAP_5_3	True		Peripheral (C.1)
+TSPC_GAP_5_4	True		Central (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+Note: 'LE Roles' is applicable for LE-only configurations, but it appears that
+	PTS is checking this precondition also in some BR/EDR/LE tests.
+-------------------------------------------------------------------------------
+
+
+		Broadcaster Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_6_1	False (*)	Broadcaster: Transmitter (M)
+TSPC_GAP_6_2	False (*)	Broadcaster: Receiver (O)
+-------------------------------------------------------------------------------
+
+
+		Broadcaster Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_7_1	False (*)	Broadcaster: Standby (M)
+TSPC_GAP_7_2	False (*)	Broadcaster: Advertising (M)
+-------------------------------------------------------------------------------
+
+
+		Broadcaster Link Layer Advertising Event Types
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_8_1	False (*)	Broadcaster: Non-Connectable Undirected Event
+					(M)
+TSPC_GAP_8_2	False (*)	Broadcaster: Scannable Undirected Event (O)
+-------------------------------------------------------------------------------
+
+
+		Broadcaster Link Layer Advertising Data Types
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_8A_1	False (*)	AD Type-Service UUID (O)
+TSPC_GAP_8A_2	False (*)	AD Type-Local Name (O)
+TSPC_GAP_8A_3	False (*)	AD Type-Flags (O)
+TSPC_GAP_8A_4	False (*)	AD Type-Manufacturer Specific Data (O)
+TSPC_GAP_8A_5	False (*)	AD Type-TX Power Level (O)
+TSPC_GAP_8A_6	False (*)	AD Type-Security Manager Out of Band (OOB) (C.1)
+TSPC_GAP_8A_7	False (*)	AD Type-Security manager TK Value (O)
+TSPC_GAP_8A_8	False (*)	AD Type-Slave Connection Interval Range (O)
+TSPC_GAP_8A_9	False (*)	AD Type-Service Solicitation (O)
+TSPC_GAP_8A_10	False (*)	AD Type-Service Data (O)
+TSPC_GAP_8A_11	False (*)	AD Type-Appearance (O)
+TSPC_GAP_8A_12	False (*)	AD Type-Public Target Address (O)
+TSPC_GAP_8A_13	False (*)	AD Type-Random Target Address (O)
+TSPC_GAP_8A_14	False (*)	AD Type-Advertising Interval (O)
+TSPC_GAP_8A_15	False (*)	AD Type-LE Bluetooth Device Address (O)
+TSPC_GAP_8A_16	False (*)	AD Type –LE Role (O)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_SM_2_4 (OOB supported) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Broadcaster Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_9_1	False (*)	Broadcaster: Non-Connectable Mode
+-------------------------------------------------------------------------------
+
+
+		Broadcaster Broadcasting and Observing Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_10_1	False (*)	Broadcaster: Broadcast Mode
+TSPC_GAP_11_1	False (*)	Broadcaster: Privacy Feature v.1.0
+TSPC_GAP_11_1A	False (*)	Broadcaster: Privacy Feature v1.1 (O)
+TSPC_GAP_11_2	False (*)	Broadcaster: Resolvable Private Address
+					Generation Procedure
+TSPC_GAP_11_3	False (*)	Broadcaster: Non-Resolvable Private Address
+					Generation Procedure (O)
+-------------------------------------------------------------------------------
+
+
+		Observer Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_12_1	False (*)	Observer: Receiver
+TSPC_GAP_12_2	False (*)	Observer: Transmitter
+-------------------------------------------------------------------------------
+
+
+		Observer Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_13_1	False (*)	Observer: Standby
+TSPC_GAP_13_2	False (*)	Observer: Scanning
+-------------------------------------------------------------------------------
+
+
+		Observer Link Layer Scanning Types
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_14_1	False (*)	Observer: Passive Scanning
+TSPC_GAP_14_2	False (*)	Observer: Active Scanning
+-------------------------------------------------------------------------------
+
+
+		Observer Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_15_1	False (*)	Observer: Non-Connectable Mode
+-------------------------------------------------------------------------------
+
+
+		Observer Broadcasting and Observing Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_16_1	False (*)	Observer: Observation Procedure
+-------------------------------------------------------------------------------
+
+
+		Observer Privacy Feature
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_17_1	False (*)	Observer: Privacy Feature v1.0 (O)
+TSPC_GAP_17_1A	False (*)	Observer: Privacy Feature v1.1 (O)
+TSPC_GAP_17_2	False (*)	Observer: Non-Resolvable Private Address
+					Generation Procedure (C.1)
+TSPC_GAP_17_3	False (*)	Observer: Resolvable Private Address Resolution
+					Procedure (C.2)
+TSPC_GAP_17_4	False (*)	Observer: Resolvable Private Address Generation
+					Procedure (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_17_1 and TSPC_GAP_14_2 (Active Scanning) are
+	supported and TSPC_GAP_17_4 (Resolvable Private Address Generation
+	Procedure) is Not Supported; Optional if CSA3 or later and
+	TSPC_GAP_17_4 are supported, otherwise Excluded.
+C.2: Optional if TSPC_GAP_17_1 is supported, otherwise Excluded.
+C.3: Mandatory if CSA3 or later and TSPC_GAP_17_1 and TSPC_GAP_14_2
+	(Active Scanning) are supported and TSPC_GAP_17_2 (Non-Resolvable
+	Private	Address Generation Procedure) is not supported; Optional if
+	CSA3 or later and TSPC_GAP_17_2 (Non-Resolvable Private Address
+	Generation Procedure) are supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Peripheral Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_18_1	True		Peripheral: Transmitter
+TSPC_GAP_18_2	True		Peripheral: Receiver
+-------------------------------------------------------------------------------
+
+
+		Peripheral Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_19_1	True		Peripheral: Standby
+TSPC_GAP_19_2	True		Peripheral: Advertising
+TSPC_GAP_19_3	True		Peripheral: Connection, Slave Role
+-------------------------------------------------------------------------------
+
+
+		Peripheral Link Layer Advertising Event Types
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_20_1	True		Peripheral: Connectable Undirected Event (C.1)
+TSPC_GAP_20_2	True		Peripheral: Connectable Directed Event (C.2)
+TSPC_GAP_20_2A	True		Peripheral: Low Duty Directed Advertising (C.3)
+TSPC_GAP_20_3	True		Peripheral: Non-Connectable Undirected Event
+TSPC_GAP_20_4	True		Peripheral: Scannable Undirected Event
+-------------------------------------------------------------------------------
+
+
+		Peripheral Link Layer Advertising Data Types
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_20A_1	False (*)	AD Type-Service UUID (C.1)
+TSPC_GAP_20A_2	True		AD Type-Local Name (C.1)
+TSPC_GAP_20A_3	True		AD Type-Flags (C.2)
+TSPC_GAP_20A_4	False (*)	AD Type-Manufacturer Specific Data (C.1)
+TSPC_GAP_20A_5	True		AD Type-TX Power Level (C.1)
+TSPC_GAP_20A_6	False (*)	AD Type-Security Manager Out of Band (OOB) (C.3)
+TSPC_GAP_20A_7	False (*)	AD Type-Security manager TK Value (C.1)
+TSPC_GAP_20A_8	False (*)	AD Type-Slave Connection Interval Range (C.1)
+TSPC_GAP_20A_9	False (*)	AD Type-Service Solicitation (C.1)
+TSPC_GAP_20A_10	False (*)	AD Type-Service Data (C.1)
+TSPC_GAP_20A_11	False (*)	AD Type –Appearance (C.1)
+TSPC_GAP_20A_12	False (*)	AD Type-Public Target Address (C.1)
+TSPC_GAP_20A_13	False (*)	AD Type-Random Target Address (C.1)
+TSPC_GAP_20A_14	False (*)	AD Type-Advertising Interval (C.1)
+TSPC_GAP_20A_15	False (*)	AD Type-LE Bluetooth Device Address (C.1)
+TSPC_GAP_20A_16	False (*)	AD Type – LE Role (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if (TSPC_GAP_20_1 or TSPC_GAP_20_3 or TSPC_GAP_20_4) is
+	supported, otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_22_2 (Limited Discoverable Mode) or TSPC_GAP_22_3
+	(General Discoverable Mode) is supported, otherwise Optional.
+C.3: Optional if (TSPC_GAP_20_1 (Connectable Undirected Event) or TSPC_GAP_20_3
+	(Non-Connectable Undirected Event) or TSPC_GAP_20_4
+	(Scannable Undirected Event)) and TSPC_SM_2_4 (OOB supported) are
+	supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Peripheral Link Layer Control Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_21_1	True		Peripheral: Connection Update Procedure (M)
+TSPC_GAP_21_2	True		Peripheral: Channel Map Update Procedure (M)
+TSPC_GAP_21_3	True		Peripheral: Encryption Procedure (O)
+TSPC_GAP_21_4	True		Peripheral: Feature Exchange Procedure (M)
+TSPC_GAP_21_5	True		Peripheral: Version Exchange Procedure (M)
+TSPC_GAP_21_6	True		Peripheral: Termination Procedure (M)
+TSPC_GAP_21_7	True		Peripheral: LE Ping Procedure (C.3)
+TSPC_GAP_21_8	True		Peripheral: Slave Initiated Feature Exchange
+					Procedure (C.4)
+TSPC_GAP_21_9	True		Peripheral: Connection Parameter Request
+					Procedure (C.5)
+-------------------------------------------------------------------------------
+
+
+		Peripheral Discovery Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_22_1	True		Peripheral: Non-Discoverable Mode (C.2)
+TSPC_GAP_22_2	True		Peripheral: Limited Discoverable Mode (C.1)
+TSPC_GAP_22_3	True		Peripheral: General Discoverable Mode (C.1)
+TSPC_GAP_22_4	True		Peripheral: Name Discovery Procedure (C.3)
+-------------------------------------------------------------------------------
+C.1: Optional if (TSPC_GAP_5_3 OR TSPC_GAP_42_2), otherwise Excluded.
+C.2: Mandatory if (TSPC_GAP_5_3 or TSPC_GAP_42_1) is supported,
+	otherwise Excluded.
+C.3: Optional if TSPC_GAP_5_3 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Peripheral Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_23_1	True		Peripheral: Non-Connectable Mode (C.1)
+TSPC_GAP_23_2	True		Peripheral: Directed Connectable Mode (O)
+TSPC_GAP_23_3	True		Peripheral: Undirected Connectable Mode (M)
+TSPC_GAP_23_4	True		Peripheral: Connection Parameter Update
+					Procedure (O)
+TSPC_GAP_23_5	True		Peripheral: Terminate Connection Procedure (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_5_3 (LE Only – Peripheral role) OR TSPC_GAP_42_3
+	(BR/EDR/LE – Non-Connectable Mode) OR TSPC_GAP_42_4
+	(BR/EDR/LE – Connectable Mode) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Peripheral Bonding Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_24_1	True		Peripheral: Non-Bondable Mode (M)
+TSPC_GAP_24_2	True		Peripheral: Bondable Mode (C.1)
+TSPC_GAP_24_3	True		Peripheral: Bonding Procedure  (C.2)
+TSPC_GAP_24_4	True		Peripheral: Multiple Bonds (C.3)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_5_3 (LE Only – Peripheral role) OR (TSPC_GAP_38_3
+	(BR/EDR/LE – Peripheral role) AND NOT TSPC_GAP_42_6 (BR.EDR/LE -
+	Bondable Mode)) is supported, Mandatory if TSPC_GAP_42_6
+	(BR/EDR/LE – Bondable Mode) is supported, otherwise Excluded.
+C.2: Optional if TSPC_GAP_24_2 (Bondable Mode) is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+		Peripheral Security Aspects Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_25_1	True		Peripheral: Security Mode (O)
+TSPC_GAP_25_2	True		Peripheral: Security Mode 2 (O)
+TSPC_GAP_25_3	True		Peripheral: Authentication Procedure (C.2)
+TSPC_GAP_25_4	True		Peripheral: Authorization Procedure (O)
+TSPC_GAP_25_5	True		Peripheral: Connection Data Signing Procedure
+				(O)
+TSPC_GAP_25_6	True		Peripheral: Authenticate Signed Data Procedure
+				(O)
+TSPC_GAP_25_7	True		Peripheral: Authenticated Pairing
+				(LE security mode 1 level 3) (C.1)
+TSPC_GAP_25_8	True		Peripheral: Unauthenticated Pairing
+				(LE security mode 1 level 2) (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_25_1 is supported, otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_0A_1 and TSPC_GAP_27_4 are supported,
+	otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+		Peripheral Privacy Feature
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_26_1	False (*)	Peripheral: Privacy Feature v1.0 (O)
+TSPC_GAP_26_1A	True		Peripheral: Privacy Feature v1.1 (O)
+TSPC_GAP_26_2	True		Peripheral: Non-Resolvable Private Address
+					Generation Procedure (C.1)
+TSPC_GAP_26_3	True		Peripheral: Resolvable Private Address
+					Generation Procedure (C.2)
+TSPC_GAP_26_4	True		Peripheral: Resolvable Private Address
+					Generation Procedure (C.4)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_26_1 is supported, otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Peripheral GAP Characteristics
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_27_1	True		Peripheral: Device Name (M)
+TSPC_GAP_27_2	True		Peripheral: Appearance (M)
+TSPC_GAP_27_3	False (*)	Peripheral: Peripheral Privacy Flag (C.1)
+TSPC_GAP_27_4	False (*)	Peripheral: Reconnection Address (C.2)
+TSPC_GAP_27_5	False (*)	Peripheral: Peripheral Preferred Connection
+					Parameters (O)
+TSPC_GAP_27_6	False (*)	Peripheral: Writeable Device Name (O)
+TSPC_GAP_27_7	False (*)	Peripheral: Writeable Appearance (O)
+TSPC_GAP_27_8	False (*)	Peripheral: Writeable Peripheral Privacy Flag
+				(O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded.
+C.2: Optional if TSPC_GAP_26_1 and TSPC_GAP_27_3 are supported,
+	otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_28_1	True		Central: Transmitter (M)
+TSPC_GAP_28_2	True		Central: Receiver (M)
+-------------------------------------------------------------------------------
+
+
+		Central Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_29_1	True		Central: Standby (M)
+TSPC_GAP_29_2	True		Central: Scanning (M)
+TSPC_GAP_29_3	True		Central: Initiating (M)
+TSPC_GAP_29_4	True		Central: Connection, Master Role (M)
+-------------------------------------------------------------------------------
+
+
+		Central Link Layer Scanning Types
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_30_1	True		Central: Passive Scanning (O)
+TSPC_GAP_30_2	True		Central: Active Scanning (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_38_4) is supported.
+	Optional if TSPC_GAP_30_1 and (TSPC_GAP_5_4 OR TSPC_GAP_38_4)
+	is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central Link Layer Control Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_31_1	True		Central: Connection Update Procedure (M)
+TSPC_GAP_31_2	True		Central: Channel Map Update Procedure (M)
+TSPC_GAP_31_3	True		Central: Encryption Procedure (O)
+TSPC_GAP_31_4	True		Central: Feature Exchange Procedure (M)
+TSPC_GAP_31_5	True		Central: Version Exchange Procedure (M)
+TSPC_GAP_31_6	True		Central: Termination Procedure (M)
+TSPC_GAP_31_7	True		Central: LE Ping Procedure (C.1)
+TSPC_GAP_31_8	True		Central: Slave Initiated Feature Exchange
+					Procedure (C.2)
+TSPC_GAP_31_9	True		Central: Connection Parameter Request Procedure
+					(C.3)
+-------------------------------------------------------------------------------
+
+
+		Central Discovery Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_32_1	True		Central: Limited Discovery Procedure (C.2)
+TSPC_GAP_32_2	True		Central: General Discovery Procedure (C.1)
+TSPC_GAP_32_3	True		Central: Name Discovery Procedure (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_1) is supported, else Excluded.
+C.2: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_2) is supported,
+	otherwise Excluded.
+C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_4) is supported,
+	otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_33_1	True		Central: Auto Connection Establishment
+					Procedure (C.3)
+TSPC_GAP_33_2	True		Central: General Connection Establishment
+					Procedure (C.1)
+TSPC_GAP_33_3	True		Central: Selective Connection Establishment
+					Procedure (C.3)
+TSPC_GAP_33_4	True		Central: Direct Connection Establishment
+					Procedure (C.2)
+TSPC_GAP_33_5	True		Central: Connection Parameter Update Procedure
+					(C.2)
+TSPC_GAP_33_6	True		Central: Terminate Connection Procedure
+					(C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_5) and TSPC_GAP_36_1 is
+	supported, otherwise Optional.
+C.2: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported,
+	otherwise Excluded.
+C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported,
+	otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central Bonding Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_34_1	True		Central: Non-Bondable Mode (C.1)
+TSPC_GAP_34_2	True		Central: Bondable Mode (C.2)
+TSPC_GAP_34_3	True		Central: Bonding Procedure (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if (TSPC_GAP_5_4 or 39/5) is supported, otherwise Excluded.
+C.2: Optional if (TSPC_GAP_5_4 or 39/6) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central Security Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_35_1	True		Central: Security Mode 1 (O)
+TSPC_GAP_35_2	True		Central: Security Mode 2 (O)
+TSPC_GAP_35_3	True		Central: Authentication Procedure (O)
+TSPC_GAP_35_4	True		Central: Authorization Procedure (O)
+TSPC_GAP_35_5	True		Central: Connection Data Signing Procedure (O)
+TSPC_GAP_35_6	True		Central: Authenticate Signed Data Procedure (O)
+TSPC_GAP_35_7	True		Central: Authenticated Pairing
+					(LE security mode 1 level 3) (C.1)
+TSPC_GAP_35_8	True		Central: Unauthenticated Pairing
+					(LE security mode 1 level 2) (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_35_1 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central Privacy Feature
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_36_1	False (*)	Central: Privacy Feature v1.0 (C.2)
+TSPC_GAP_36_1A	True		Central: Privacy Feature v1.1 (C.4)
+TSPC_GAP_36_2	True		Central: Non-Resolvable Private Address
+					Generation Procedure (C.1)
+TSPC_GAP_36_3	True		Central: Resolvable Private Address Resolution
+					Procedure (C.2)
+TSPC_GAP_36_4	False (*)	Central: Write to Privacy Characteristic
+					(Enable/Disable Privacy) (O)
+TSPC_GAP_36_5	True		Central: Resolvable Private Address Generation
+					Procedure (C.6)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_36_1 and TSPC_GAP_30_2 are supported,
+	otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_36_1 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central GAP Characteristics
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_37_1	True		Central: Device Name (M)
+TSPC_GAP_37_2	True		Central: Appearance (M)
+-------------------------------------------------------------------------------
+
+
+		BR/EDR/LE Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_38_1	False (*)	BR/EDR/LE: Broadcaster (C.1)
+TSPC_GAP_38_2	False (*)	BR/EDR/LE: Observer (C.1)
+TSPC_GAP_38_3	True		BR/EDR/LE: Peripheral (C.1)
+TSPC_GAP_38_4	True		BR/EDR/LE: Central (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+This table is applicable for BR/EDR/LE configurations. For LE-only
+configurations, see 'LE Roles' table for role declarations.
+-------------------------------------------------------------------------------
+
+
+		Central BR/EDR/LE Modes
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_39_1	True		Central BR/EDR/LE: Non-Discoverable Mode (C.1)
+TSPC_GAP_39_2	True		Central BR/EDR/LE: Discoverable Mode (C.2)
+TSPC_GAP_39_3	True		Central BR/EDR/LE: Non-Connectable Mode (C.3)
+TSPC_GAP_39_4	True		Central BR/EDR/LE: Connectable Mode (M)
+TSPC_GAP_39_5	True		Central BR/EDR/LE: Non-Bondable Mode (C.4)
+TSPC_GAP_39_6	True		Central BR/EDR/LE: Bondable Mode (C.5)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded.
+C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR,
+	otherwise Excluded.
+C.3: Mandatory if TSPC_GAP_1_4 is supported over BR/EDR, otherwise Excluded.
+C.4: Mandatory if TSPC_GAP_1_6 is supported over BR/EDR, otherwise Excluded.
+C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central BR/EDR/LE Idle Mode Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_40_1	True		Central BR/EDR/LE: General Discovery (C.1)
+TSPC_GAP_40_2	True		Central BR/EDR/LE: Limited Discovery (C.2)
+TSPC_GAP_40_3	True		Central BR/EDR/LE: Device Type Discovery (C.3)
+TSPC_GAP_40_4	True		Central BR/EDR/LE: Name Discovery (C.4)
+TSPC_GAP_40_5	True		Central BR/EDR/LE: Link Establishment (C.5)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_3_1 is supported over BR/EDR, otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_3_2 is supported over BR/EDR, otherwise Excluded.
+C.3: Mandatory if (TSPC_GAP_3_1 or TSPC_GAP_3_2) is supported over BR/EDR,
+	otherwise Excluded.
+C.4: Mandatory if TSPC_GAP_3_3 is supported over BR/EDR, otherwise Excluded.
+C.5: Mandatory if (TSPC_GAP_4_1 or TSPC_GAP_4_2) is supported over BR/EDR,
+	otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central BR/EDR/LE Security Aspects
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_41_1	True		Central BR/EDR/LE: Security Aspects (M)
+-------------------------------------------------------------------------------
+
+
+		Peripheral BR/EDR/LE Modes
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_42_1	True		Peripheral BR/EDR/LE: Non-Discoverable Mode
+				(See Spec)
+TSPC_GAP_42_2	True		Peripheral BR/EDR/LE: Discoverable Mode
+				(See Spec)
+TSPC_GAP_42_3	True		Peripheral BR/EDR/LE: Non-Connectable Mode
+				(See Spec)
+TSPC_GAP_42_4	True		Peripheral BR/EDR/LE: Connectable Mode (M)
+TSPC_GAP_42_5	True		Peripheral BR/EDR/LE: Non-Bondable Mode
+				(See Spec)
+TSPC_GAP_42_6	True		Peripheral BR/EDR/LE: Bondable Mode (See Spec)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded.
+C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR,
+	otherwise Excluded.
+C.3: Mandatory if TSPC_GAP_1_4 is supported over BR/EDR, otherwise Excluded.
+C.4: Mandatory if TSPC_GAP_1_6 is supported over BR/EDR, otherwise Excluded.
+C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Peripheral BR/EDR/LE Security Aspects
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_43_1	True		Peripheral BR/EDR/LE: Non-Discoverable Mode
+-------------------------------------------------------------------------------
+
+
+		Central Simultaneous BR/EDR and LE Transports
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_44_1	True		Central BR/EDR/LE: Simultaneous BR/EDR and LE
+					Transports – BR/EDR Slave to the same
+					device (O)
+TSPC_GAP_44_2	True		Central BR/EDR/LE: Simultaneous BR/EDR and LE
+					Transports – BR/EDR Master to the same
+					device (O)
+-------------------------------------------------------------------------------
+
+
+		Peripheral Simultaneous BR/EDR and LE Transports
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_45_1	True		Simultaneous BR/EDR and LE Transports – BR/EDR
+					Slave to the same device (C.1)
+TSPC_GAP_45_2	True		Simultaneous BR/EDR and LE Transports – BR/EDR
+					Master to the same device (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if ((SUM ICS 31/14 (Core Spec Version 4.1) or SUM ICS 31/15
+(Core Spec Version 4.1+HS)) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_1_1	True		GATT Client Role (O)
+TSPC_GATT_1_2	True		GATT Server Role (O)
+TSPC_SM_1_1	True		Master Role (Initiator)
+TSPC_SM_1_2	True		Slave Role (Responder)
+TSPC_SM_2_4	True		OOB supported (O)
+-------------------------------------------------------------------------------
diff --git a/repo/android/pics-gatt.txt b/repo/android/pics-gatt.txt
new file mode 100644
index 0000000..90585f5
--- /dev/null
+++ b/repo/android/pics-gatt.txt
@@ -0,0 +1,326 @@
+GATT PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+
+M - mandatory
+O - optional
+
+		Generic Attribute Profile Role
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_1_1	True		Generic Attribute Profile Client (C.1)
+TSPC_GATT_1_2	True		Generic Attribute Profile Server (C.2)
+TSPC_GATT_1A_1	True		Complete GATT client (C.3)
+TSPC_GATT_1A_2	True		Complete GATT server (C.4)
+-------------------------------------------------------------------------------
+C.1: Optional to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is mandatory
+	to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2
+C.2: Mandatory to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is
+	mandatory to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2
+C.3: Optional IF TSPC_GATT_1_1 is supported, otherwise Excluded
+C.4: Optional IF TSPC_GATT_1_2 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+		ATT Bearer Transport
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_2_1	True		Attribute Protocol Supported over BR/EDR
+					(L2CAP fixed channel support) (C.1)
+TSPC_GATT_2_2	True		Attribute Protocol Supported over LE (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF (SUM ICS 12/2 OR SUM ICS 12/9) is supported, otherwise
+	Excluded
+C.2: Mandatory IF (SUM ICS 12/7 OR SUM ICS 12/9) is supported, otherwise
+	Excluded
+-------------------------------------------------------------------------------
+
+
+
+		Generic Attribute Profile Support
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_3_1	True		Client: Exchange MTU (C.2)
+TSPC_GATT_3_2	True		Client: Discover All Primary Services (C.1)
+TSPC_GATT_3_3	True		Client: Discover Primary Services Service
+					UUID (C.1)
+TSPC_GATT_3_4	True		Client: Find Included Services (C.1)
+TSPC_GATT_3_5	True		Client: Discover All characteristics of a
+					Service (C.1)
+TSPC_GATT_3_6	True		Client: Discover Characteristics by UUID (C.1)
+TSPC_GATT_3_7	True		Client: Discover All Characteristic Descriptors
+					(C.1)
+TSPC_GATT_3_8	True		Client: Read Characteristic Value (C.1)
+TSPC_GATT_3_9	True		Client: Read using Characteristic UUID (C.1)
+TSPC_GATT_3_10	True		Client: Read Long Characteristic Values (C.1)
+TSPC_GATT_3_11	False (*)	Client: Read Multiple Characteristic
+					Values (C.1)
+TSPC_GATT_3_12	True		Client: Write without Response (C.1)
+TSPC_GATT_3_13	True		Client: Signed Write Without Response (C.1)
+TSPC_GATT_3_14	True		Client: Write Characteristic Value (C.1)
+TSPC_GATT_3_15	True		Client: Write Long Characteristic Values (C.1)
+TSPC_GATT_3_16	True		Client: Characteristic Value Reliable
+					Writes (C.1)
+TSPC_GATT_3_17	True		Client: Notifications (C.1)
+TSPC_GATT_3_18	True		Client: Indications (M)
+TSPC_GATT_3_19	True		Client: Read Characteristic Descriptors (C.1)
+TSPC_GATT_3_20	True		Client: Read long Characteristic Descriptors
+					(C.1)
+TSPC_GATT_3_21	True		Client: Write Characteristic Descriptors (C.1)
+TSPC_GATT_3_22	True		Client: Write Long Characteristic Descriptors
+					(C.1)
+TSPC_GATT_3_23	True		Client: Service Changed Characteristic (M)
+TSPC_GATT_3B_1	True		Client: Primary Service Declaration (M)
+TSPC_GATT_3B_2	True		Client: Secondary Service Declaration (M)
+TSPC_GATT_3B_3	True		Client: Include Declaration (M)
+TSPC_GATT_3B_4	True		Client: Characteristic Declaration (M)
+TSPC_GATT_3B_5	True		Client: Characteristic Value Declaration (M)
+TSPC_GATT_3B_6	True		Client: Characteristic Extended Properties (M)
+TSPC_GATT_3B_7	True		Client: Characteristic User Description
+					Descriptor (M)
+TSPC_GATT_3B_8	True		Client: Client Characteristic Configuration
+					Descriptor (M)
+TSPC_GATT_3B_9	True		Client: Server Characteristic Configuration
+					Descriptor (M)
+TSPC_GATT_3B_10	True		Client: Characteristic Format Descriptor (M)
+TSPC_GATT_3B_11	True		Client: Characteristic Aggregate Format
+					Descriptor (M)
+TSPC_GATT_3B_12	True		Client: Characteristic Format: Boolean (M)
+TSPC_GATT_3B_13	True		Client: Characteristic Format: 2Bit (M)
+TSPC_GATT_3B_14	True		Client: Characteristic Format: nibble (M)
+TSPC_GATT_3B_15	True		Client: Characteristic Format: Uint8 (M)
+TSPC_GATT_3B_16	True		Client: Characteristic Format: Uint12 (M)
+TSPC_GATT_3B_17	True		Client: Characteristic Format: Uint16 (M)
+TSPC_GATT_3B_18	True		Client: Characteristic Format: Uint24 (M)
+TSPC_GATT_3B_19	True		Client: Characteristic Format: Uint32 (M)
+TSPC_GATT_3B_20	True		Client: Characteristic Format: Uint48 (M)
+TSPC_GATT_3B_21	True		Client: Characteristic Format: Uint64 (M)
+TSPC_GATT_3B_22	True		Client: Characteristic Format: Uint128 (M)
+TSPC_GATT_3B_23	True		Client: Characteristic Format: Sint8 (M)
+TSPC_GATT_3B_24	True		Client: Characteristic Format: Sint12 (M)
+TSPC_GATT_3B_25	True		Client: Characteristic Format: Sint16 (M)
+TSPC_GATT_3B_26	True		Client: Characteristic Format: Sint24 (M)
+TSPC_GATT_3B_27	True		Client: Characteristic Format: Sint32 (M)
+TSPC_GATT_3B_28	True		Client: Characteristic Format: Sint48 (M)
+TSPC_GATT_3B_29	True		Client: Characteristic Format: Sint64 (M)
+TSPC_GATT_3B_30	True		Client: Characteristic Format: Sint128 (M)
+TSPC_GATT_3B_31	True		Client: Characteristic Format: Float32 (M)
+TSPC_GATT_3B_32	True		Client: Characteristic Format: Float64 (M)
+TSPC_GATT_3B_33	True		Client: Characteristic Format: SFLOAT (M)
+TSPC_GATT_3B_34	True		Client: Characteristic Format: FLOAT (M)
+TSPC_GATT_3B_35	True		Client: Characteristic Format: Duint16 (M)
+TSPC_GATT_3B_36	True		Client: Characteristic Format: utf8s (M)
+TSPC_GATT_3B_37	True		Client: Characteristic Format: utf16s (M)
+TSPC_GATT_3B_38	True		Client: Characteristic Format: struct (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF TSPC_GATT_1_3 is supported, otherwise Optional
+C.2: Mandatory IF TSPC_GATT_1_3 AND TSPC_GATT_2_2 is supported, otherwise
+	Excluded
+-------------------------------------------------------------------------------
+
+
+
+		Generic Attribute Profile Support, by Server
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_4_1	True		Server: Exchange MTU (C.4)
+TSPC_GATT_4_2	True		Server: Discover All Primary Services (M)
+TSPC_GATT_4_3	True		Server: Discover Primary Services Service
+					UUID (M)
+TSPC_GATT_4_4	True		Server: Find Included Services (M)
+TSPC_GATT_4_5	True		Server: Discover All characteristics of
+					a Service (M)
+TSPC_GATT_4_6	True		Server: Discover Characteristics by UUID (M)
+TSPC_GATT_4_7	True		Server: Discover All Characteristic
+					Descriptors (M)
+TSPC_GATT_4_8	True		Server: Read Characteristic Value (M)
+TSPC_GATT_4_9	True		Server: Read using Characteristic UUID (M)
+TSPC_GATT_4_10	True		Server: Read Long Characteristic Values (C.4)
+TSPC_GATT_4_11	False (*)	Server: Read Multiple Characteristic
+					Values (C.4)
+TSPC_GATT_4_12	True		Server: Write without Response (C.2)
+TSPC_GATT_4_13	True		Server: Signed Write Without Response (C.4)
+TSPC_GATT_4_14	True		Server: Write Characteristic Value (C.3)
+TSPC_GATT_4_15	True		Server: Write Long Characteristic Values (C.4)
+TSPC_GATT_4_16	True		Server: Characteristic Value Reliable
+					Writes (C.4)
+TSPC_GATT_4_17	True		Server: Notifications (C.4)
+TSPC_GATT_4_18	True		Server: Indications (C.1)
+TSPC_GATT_4_19	True		Server: Read Characteristic Descriptors (C.4)
+TSPC_GATT_4_20	True		Server: Read long Characteristic
+					Descriptors (C.4)
+TSPC_GATT_4_21	True		Server: Write Characteristic Descriptors (C.4)
+TSPC_GATT_4_22	True		Server: Write Long Characteristic
+					Descriptors (C.4)
+TSPC_GATT_4_23	True		Server: Service Changed Characteristic (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF service definitions on the server can be added, changed, or
+	removed, otherwise Optional
+C.2: Mandatory IF GATT TSPC_GATT_4_13 is supported, otherwise Optional
+C.3: Mandatory IF GATT TSPC_GATT_4_15 is supported, otherwise Optional
+C.4: Mandatory IF GATT TSPC_GATT_1_4 is supported, otherwise Optional
+-------------------------------------------------------------------------------
+
+
+		Profile Attribute Types and Characteristic Formats
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_4B_1	True		Server: Primary Service Declaration (M)
+TSPC_GATT_4B_2	True		Server: Secondary Service Declaration (M)
+TSPC_GATT_4B_3	True		Server: Include Declaration (M)
+TSPC_GATT_4B_4	True		Server: Characteristic Declaration (M)
+TSPC_GATT_4B_5	True		Server: Characteristic Value Declaration (M)
+TSPC_GATT_4B_6	True		Server: Characteristic Extended Properties (M)
+TSPC_GATT_4B_7	True		Server: Characteristic User Description
+					Descriptor (M)
+TSPC_GATT_4B_8	True		Server: Client Characteristic Configuration
+					Descriptor (M)
+TSPC_GATT_4B_9	True		Server: Server Characteristic Configuration
+					Descriptor (M)
+TSPC_GATT_4B_10	True		Server: Characteristic Format Descriptor (M)
+TSPC_GATT_4B_11	True		Server: Characteristic Aggregate Format
+					Descriptor (M)
+TSPC_GATT_4B_12	True		Server: Characteristic Format: Boolean (M)
+TSPC_GATT_4B_13	True		Server: Characteristic Format: 2Bit (M)
+TSPC_GATT_4B_14	True		Server: Characteristic Format: nibble (M)
+TSPC_GATT_4B_15	True		Server: Characteristic Format: Uint8 (M)
+TSPC_GATT_4B_16	True		Server: Characteristic Format: Uint12 (M)
+TSPC_GATT_4B_17	True		Server: Characteristic Format: Uint16 (M)
+TSPC_GATT_4B_18	True		Server: Characteristic Format: Uint24 (M)
+TSPC_GATT_4B_19	True		Server: Characteristic Format: Uint32 (M)
+TSPC_GATT_4B_20	True		Server: Characteristic Format: Uint48 (M)
+TSPC_GATT_4B_21	True		Server: Characteristic Format: Uint64 (M)
+TSPC_GATT_4B_22	True		Server: Characteristic Format: Uint128 (M)
+TSPC_GATT_4B_23	True		Server: Characteristic Format: Sint8 (M)
+TSPC_GATT_4B_24	True		Server: Characteristic Format: Sint12 (M)
+TSPC_GATT_4B_25	True		Server: Characteristic Format: Sint16 (M)
+TSPC_GATT_4B_26	True		Server: Characteristic Format: Sint24 (M)
+TSPC_GATT_4B_27	True		Server: Characteristic Format: Sint32 (M)
+TSPC_GATT_4B_28	True		Server: Characteristic Format: Sint48 (M)
+TSPC_GATT_4B_29	True		Server: Characteristic Format: Sint64 (M)
+TSPC_GATT_4B_30	True		Server: Characteristic Format: Sint128 (M)
+TSPC_GATT_4B_31	True		Server: Characteristic Format: Float32 (M)
+TSPC_GATT_4B_32	True		Server: Characteristic Format: Float64 (M)
+TSPC_GATT_4B_33	True		Server: Characteristic Format: SFLOAT (M)
+TSPC_GATT_4B_34	True		Server: Characteristic Format: FLOAT (M)
+TSPC_GATT_4B_35	True		Server: Characteristic Format: Duint16 (M)
+TSPC_GATT_4B_36	True		Server: Characteristic Format: utf8s (M)
+TSPC_GATT_4B_37	True		Server: Characteristic Format: utf16s (M)
+TSPC_GATT_4B_38	True		Server: Characteristic Format: struct (M)
+-------------------------------------------------------------------------------
+
+
+		Generic Attribute Profile Service
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_6_2	True		Discover GATT Services using Service Discovery
+					Profile (C.1)
+TSPC_GATT_6_3	True		Publish SDP record for GATT services support
+					via BR/EDR (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF TSPC_GATT_1_1 is supported, otherwise Excluded
+C.2: Mandatory IF TSPC_GATT_1_2 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+		Attribute Protocol Transport Security
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_7_1	True		Security Mode 4 (C.1)
+TSPC_GATT_7_2	True		LE Security Mode 1 (C.2)
+TSPC_GATT_7_3	True		LE Security Mode 2 (C.2)
+TSPC_GATT_7_4	True		LE Authentication Procedure (C.2)
+TSPC_GATT_7_5	True		LE connection data signing procedure (C.2)
+TSPC_GATT_7_6	True		LE Authenticate signed data procedure (C.2)
+TSPC_GATT_7_7	True		LE Authorization Procedure (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF TSPC_GATT_2_1 is supported, otherwise Excluded
+C.2: Optional IF TSPC_GATT_2_2 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+		Attribute Protocol Client Messages
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ATT_3_1	True		Attribute Error Response (M)
+TSPC_ATT_3_2	True		Exchange MTU Request (O)
+TSPC_ATT_3_4	True		Find Information Request (O)
+TSPC_ATT_3_6	True		Find by Type Value Request (O)
+TSPC_ATT_3_8	True		Read by Type Request (O)
+TSPC_ATT_3_10	True		Read Request (O)
+TSPC_ATT_3_12	True		Read Blob Request (O)
+TSPC_ATT_3_14	False (*)	Read Multiple Request (O)
+TSPC_ATT_3_16	True		Read by Group Type Request (O)
+TSPC_ATT_3_17	True		Read by Group Type Response (C.6)
+TSPC_ATT_3_18	True		Write Request (O)
+TSPC_ATT_3_20	True		Write Command (O)
+TSPC_ATT_3_21	True		Signed Write Command (O)
+TSPC_ATT_3_22	True		Prepare Write Request (O)
+TSPC_ATT_3_24	True		Execute Write Request (C.8)
+TSPC_ATT_3_26	True		Handle Value Notification (M)
+TSPC_ATT_3_28	True		Handle Value Confirmation (M)
+-------------------------------------------------------------------------------
+C.6: Mandatory IF TSPC_ATT_3_16 is supported, otherwise Excluded
+C.8: Mandatory IF TSPC_ATT_3_22 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+		Attribute Protocol Server Messages
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ATT_4_1	True		Attribute Error Response (M)
+TSPC_ATT_4_3	True		Exchange MTU Response (M)
+TSPC_ATT_4_5	True		Find Information Response (M)
+TSPC_ATT_4_7	True		Find by Type Value Response (M)
+TSPC_ATT_4_8	True		Read by Type Request (M)
+TSPC_ATT_4_9	True		Read by Type Response (M)
+TSPC_ATT_4_11	True		Read Response (M)
+TSPC_ATT_4_15	False (*)	Read Multiple Response (C.2)
+TSPC_ATT_4_17	True		Read by Group Type Response (M)
+TSPC_ATT_4_19	True		Write Response (C.3)
+TSPC_ATT_4_20	True		Write Command (O)
+TSPC_ATT_4_21	True		Signed Write Command (O)
+TSPC_ATT_4_23	True		Prepare Write Response (C.4)
+TSPC_ATT_4_25	True		Execute Write Response (C.4)
+TSPC_ATT_4_26	True		Handle Value Notification (O)
+TSPC_ATT_4_27	True		Handle Value Indication	(O)
+-------------------------------------------------------------------------------
+C.2: Mandatory IF TSPC_ATT_4_14 is supported, otherwise Excluded
+C.3: Mandatory IF TSPC_ATT_4_18 is supported, otherwise Excluded
+C.4: Mandatory IF TSPC_ATT_4_22 is supported, otherwise Excluded
+C.5: Mandatory IF TSPC_ATT_4_27 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+		Attribute Protocol Transport
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ATT_5_2	True		LE Security Mode 1 (C.2)
+TSPC_ATT_5_4	True		LE Authentication Procedure (C.2)
+TSPC_ATT_5_7	True		LE Authorization Procedure (C.2)